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 🙏

© 2025 – Pkg Stats / Ryan Hefner

use-request-utils

v1.0.85

Published

A lightweight, [browser, cloudflare workers, node, deno, etc.] compatible collection of utilities for handling web requests, authentication, cookies, JWT tokens, caching, and cryptographic operations.

Readme

use-request-utils

A lightweight, [browser, cloudflare workers, node, deno, etc.] compatible collection of utilities for handling web requests, authentication, cookies, JWT tokens, caching, and cryptographic operations.

TypeScript Vitest MIT License

Index

Installation

npm install use-request-utils

or with yarn:

yarn add use-request-utils

Features

  • 🍪 Cookie Management: Comprehensive cookie handling (cookie-parser, cookie-serializer, cookies) with support for signed cookies and standard attributes (HttpOnly, Secure, SameSite, Max-Age, Expires, Path, Domain, Partitioned, Prefixes).
  • 🔑 JWT Tokens: Robust JWT generation, verification, and decoding (jwt) using Web Crypto API, supporting various algorithms (HS*, RS*, ES*) and optional encryption (AES-GCM).
  • 🔒 Cryptography: Standard cryptographic hashing utilities (crypto) including SHA-1 and SHA-256 for strings, objects, and buffers.
  • 📋 Headers: Convenient utilities (headers) for managing HTTP headers, including creation from JSON, merging multiple sources, and conversion back to JSON.
  • 🛠️ Request Builder: Fluent API (request-builder) for constructing Request objects with easy handling of query parameters, JSON bodies, form data, cookies, and headers.
  • 📡 Enhanced Fetch: A wrapper around the standard fetch (fetch) providing automatic integration with ephemeral-cache for GET/HEAD requests, standardized error handling using HttpError, and abortable requests.
  • Ephemeral Cache: A lightweight, time-aware in-memory cache (ephemeral-cache) specifically designed for Response objects, featuring automatic expiration, request deduplication via wrap, and cache status headers.
  • 🧭 Router: Fast and flexible request routing (router) with support for path parameters (including optional and regex-constrained parameters), wildcards, and automatic type inference for parameters.
  • 📞 RPC Framework: A simple yet powerful RPC system (rpc, rpc-proxy, rpc-context, rpc-response) enabling type-safe client-server communication, batching requests, and flexible response types (.asObject(), .asResponse()).
  • ⚛️ React Hooks: Ready-to-use React hooks (use-fetch-http, use-fetch-rpc) for declarative data fetching with features like dependency tracking, debouncing, mapping, conditional fetching, and interval polling.
  • 📦 Map Store: A general-purpose, Map-based key-value store (map-store) with support for public/private scoping, useful for managing state within request lifecycles (like RpcContext).
  • ⚙️ Utilities: A collection of general utility functions (util) for common tasks like path joining, stream reading, safe JSON parsing, date parsing, and string hashing.
  • ☁️ Environment Agnostic: Designed to work seamlessly in various JavaScript environments including browsers, Cloudflare Workers, Node.js, and Deno.
  • 🔷 TypeScript: Fully written in TypeScript with comprehensive type definitions for enhanced developer experience and safety.

Core Modules

Headers Utilities (/headers.ts)

This module provides utility functions for working with the standard Headers object.

fromJson

Creates a Headers object from a HeadersInit object (which can be a plain object, an array of key-value pairs, or another Headers object).

Parameters:

  • json (HeadersInit): The input object or array to convert into Headers.

Returns:

  • (Headers): A new Headers instance.

Usage Example:

import headersUtil from 'use-request-utils/headers';

const headers = headersUtil.fromJson({
	'edge-api-key-1': 'abc123',
	'edge-api-key-2': 'def456'
});

// headers is now a Headers object
console.log(headers.get('edge-api-key-1')); // Output: abc123

// Convert back to plain object for verification (optional)
const plainObject = Object.fromEntries(headers.entries());
console.log(plainObject);
// Output: { 'edge-api-key-1': 'abc123', 'edge-api-key-2': 'def456' }

merge

Merges multiple header sources into a single Headers object. Sources can be Headers objects, plain objects (Record<string, string>), or arrays of key-value pairs ([string, string][]).

Later sources override keys from earlier sources. If a header value in a source is null or undefined, the corresponding header key will be removed from the final merged result. null or undefined sources are skipped.

Parameters:

  • ...sources ((HeadersInit | null | undefined)[]): A variable number of header sources to merge.

Returns:

  • (Headers): A new Headers instance containing the merged headers.

Throws:

  • TypeError: If any argument (that isn't null or undefined) is not an object-like structure compatible with HeadersInit.

Usage Example:

import headersUtil from 'use-request-utils/headers';

const baseHeaders = new Headers();
const apiHeaders = { 'edge-api-key-1': 'abc123' };
const userHeaders = new Headers({ 'edge-api-key-2': 'def456' });
const overrideHeaders = { 'edge-api-key-1': 'abc124' }; // Overrides the first key
const thirdPartyHeaders = new Headers({ 'edge-api-key-3': 'ghi789' });
const removalHeaders = { 'edge-api-key-2': null }; // This will remove edge-api-key-2

const mergedHeaders = headersUtil.merge(
	baseHeaders,
	null, // ignored
	undefined, // ignored
	apiHeaders,
	userHeaders,
	overrideHeaders,
	thirdPartyHeaders
	// removalHeaders, // Example if we wanted to remove edge-api-key-2
);

// Convert back to plain object for verification
const plainObject = Object.fromEntries(mergedHeaders.entries());
console.log(plainObject);
// Output: { 'edge-api-key-1': 'abc124', 'edge-api-key-2': 'def456', 'edge-api-key-3': 'ghi789' }

toJson

Converts a Headers object into a plain JavaScript object (Record<string, string>). Header names are normalized to lowercase.

Parameters:

  • headers (Headers): The Headers instance to convert.

Returns:

  • (Record<string, string>): A plain object representation of the headers.

Usage Example:

import headersUtil from 'use-request-utils/headers';

const myHeaders = new Headers({
	'edge-api-key-1': 'abc123',
	'EDGE-API-KEY-2': 'def456' // Name will be normalized
});

const jsonHeaders = headersUtil.toJson(myHeaders);

console.log(jsonHeaders);
// Output: { 'edge-api-key-1': 'abc123', 'edge-api-key-2': 'def456' }

Cookie Parser (/cookie-parser.ts)

Utilities for parsing Cookie header strings into JavaScript objects and verifying signed cookies.

This module relies on the Web Crypto API for cryptographic operations (crypto.subtle).

Types

CookieParser.Cookies
type Cookies = Record<string, string>;

A simple object where keys are cookie names and values are the corresponding cookie values.

CookieParser.SignedCookie
type SignedCookie = Record<string, string | false>;

An object where keys are cookie names. Values are the original cookie value if the signature is valid, or false if the signature is invalid or missing.

parse

Parses a Cookie header string into an object of key-value pairs. It handles URI decoding and quoted values.

Parameters:

  • cookie (string): The raw Cookie header string (e.g., 'yummy_cookie=choco; tasty_cookie=strawberry').
  • name (string, optional): If provided, only the cookie with this specific name will be parsed and returned in the object.

Returns:

  • (CookieParser.Cookies): An object containing the parsed cookies.

Usage Examples:

import cookieParser from 'use-request-utils/cookie-parser';

// Parse all cookies
const cookieString1 = 'yummy_cookie=choco; tasty_cookie=strawberry; best_cookie="%20sugar%20";';
const cookies1 = cookieParser.parse(cookieString1);
console.log(cookies1);
// Output: { yummy_cookie: 'choco', tasty_cookie: 'strawberry', best_cookie: ' sugar ' }

// Parse a specific cookie
const cookieString2 = 'yummy_cookie=choco; tasty_cookie=strawberry';
const cookies2 = cookieParser.parse(cookieString2, 'yummy_cookie');
console.log(cookies2);
// Output: { yummy_cookie: 'choco' }

// Parse empty string
const cookies3 = cookieParser.parse('');
console.log(cookies3);
// Output: {}

// Ignores invalid names/values
const cookieString4 = 'yummy_cookie=choco; tasty cookie=strawberry; yummy_cookie=choco\\nchip;';
const cookies4 = cookieParser.parse(cookieString4);
console.log(cookies4);
// Output: { yummy_cookie: 'choco' }

parseSigned

Parses a Cookie header string and verifies the signatures of potential signed cookies using a secret key.

Signed cookies are expected in the format key=value.signature. Only cookies matching this format with a valid signature are included in the result with their original value. Invalid or unsigned cookies are omitted or set to false.

Parameters:

  • cookie (string): The raw Cookie header string.
  • secret (string | BufferSource): The secret key used for signing. Can be a string or a BufferSource (like Uint8Array).
  • name (string, optional): If provided, only the signed cookie with this specific name will be parsed and verified.

Returns:

  • (Promise<CookieParser.SignedCookie>): A promise resolving to an object containing the verified signed cookies. Values are the original string if valid, false otherwise.

Usage Examples:

import cookieParser from 'use-request-utils/cookie-parser';

const secret = 'secret ingredient';

// Parse all signed cookies
const cookieString1 =
	'yummy_cookie=choco.UdFR2rBpS1GsHfGlUiYyMIdqxqwuEgplyQIgTJgpGWY%3D; tasty_cookie=strawberry.I9qAeGQOvWjCEJgRPmrw90JjYpnnX2C9zoOiGSxh1Ig%3D; regular_cookie=vanilla';
const cookies1 = await cookieParser.parseSigned(cookieString1, secret);
console.log(cookies1);
// Output: { yummy_cookie: 'choco', tasty_cookie: 'strawberry' } (regular_cookie ignored)

// Parse specific signed cookie
const cookies2 = await cookieParser.parseSigned(cookieString1, secret, 'tasty_cookie');
console.log(cookies2);
// Output: { tasty_cookie: 'strawberry' }

// Handle invalid signature
const cookieString3 = 'yummy_cookie=choco.UdFR2rBpS1GsHfGlUiYyMIdqxqwuEgplyQIgTJgpGWY%3D; tasty_cookie=strawberry.INVALID_SIGNATURE';
const cookies3 = await cookieParser.parseSigned(cookieString3, secret);
console.log(cookies3);
// Output: { yummy_cookie: 'choco', tasty_cookie: false }

makeSignature

Generates an HMAC-SHA256 signature for a given value using a secret key. The signature is returned as a Base64 encoded string.

Parameters:

  • value (string): The value to sign.
  • secret (string | BufferSource): The secret key used for signing.

Returns:

  • (Promise<string>): A promise resolving to the Base64 encoded signature.

Usage Example:

import cookieParser from 'use-request-utils/cookie-parser';

const secret = 'my-secret';
const value = 'user-session-data';

const signature = await cookieParser.makeSignature(value, secret);
console.log(signature); // Output: Base64 signature string (e.g., "...")

// Example of creating a signed cookie value string
const signedCookieValue = `${value}.${signature}`;

verifySignature

Verifies if a given Base64 signature matches a value using a secret key.

Parameters:

  • base64Signature (string): The Base64 encoded signature to verify.
  • value (string): The original value that was signed.
  • secret (CryptoKey): The imported CryptoKey object representing the secret. (Use crypto.subtle.importKey or the internal getCryptoKey logic if needed).

Returns:

  • (Promise<boolean>): A promise resolving to true if the signature is valid, false otherwise.

Note: This is a lower-level function. parseSigned is generally preferred for parsing headers.

Cookie Serializer (/cookie-serializer.ts)

Utilities for serializing cookie names and values into Set-Cookie header strings, including support for various attributes like HttpOnly, Secure, SameSite, Max-Age, Expires, Domain, Path, Partitioned, and cookie prefixes (__Secure-, __Host-).

This module relies on the Web Crypto API for cryptographic operations (crypto.subtle) when using serializeSigned.

Types

CookieSerializer.Options
type Options = {
	domain?: string;
	expires?: Date;
	httpOnly?: boolean;
	maxAge?: number; // In seconds
	path?: string;
	secure?: boolean;
	sameSite?: 'Strict' | 'Lax' | 'None' | 'strict' | 'lax' | 'none';
	partitioned?: boolean;
	prefix?: 'host' | 'secure'; // Internal helper, not a standard cookie attribute
} & PartitionConstraint; // Ensures 'partitioned' requires 'secure'

// Constraints based on prefixes:
type Constraint<Name> = Name extends `__Secure-${string}`
	? Options & { secure: true } // __Secure- requires secure
	: Name extends `__Host-${string}`
		? Options & { secure: true; path: '/'; domain?: undefined } // __Host- requires secure, path='/', no domain
		: Options;

An object defining attributes for the Set-Cookie header.

  • domain: Specifies the domain for which the cookie is valid.
  • expires: Sets an absolute expiration date/time.
  • httpOnly: If true, the cookie cannot be accessed via client-side JavaScript.
  • maxAge: Sets the cookie's lifespan in seconds. If 0, expires immediately. If negative, treated as session cookie (omitted). Values > 400 days throw an error.
  • path: Specifies the URL path that must exist in the requested URL.
  • secure: If true, the cookie is only sent over HTTPS. Required for SameSite=None and Partitioned.
  • sameSite: Controls cross-site request behavior ('Strict', 'Lax', 'None'). Case-insensitive input is normalized.
  • partitioned: If true, associates the cookie with the top-level site where it's embedded. Requires secure: true.
  • prefix: (Internal helper in cookies.ts) Used to automatically apply prefix rules (__Secure-, __Host-). Not a standard cookie attribute itself.

serialize

Serializes a cookie name and value into a Set-Cookie header string with specified options. The value is automatically URI-encoded.

Parameters:

  • name (string): The name of the cookie. If using prefixes (__Secure-, __Host-), constraints are checked.
  • value (string): The value of the cookie. Will be URI-encoded.
  • options (CookieSerializer.Constraint<Name>, optional): An object containing cookie attributes. Types ensure constraints for prefixes are met.

Returns:

  • (string): The formatted Set-Cookie header string.

Throws:

  • Error: If prefix constraints (__Secure-, __Host-) are violated.
  • Error: If partitioned is true but secure is not.
  • Error: If maxAge or expires exceed the 400-day limit.

Usage Examples:

import cookieSerializer from 'use-request-utils/cookie-serializer';

// Simple cookie
const simple = cookieSerializer.serialize('myCookie', 'myValue');
console.log(simple); // Output: myCookie=myValue

// Cookie with options
const complex = cookieSerializer.serialize('session', 'user123', {
	path: '/',
	httpOnly: true,
	secure: true,
	maxAge: 3600, // 1 hour
	sameSite: 'Lax',
	domain: 'example.com'
});
console.log(complex);
// Output: session=user123; Max-Age=3600; Domain=example.com; Path=/; HttpOnly; Secure; SameSite=Lax

// __Secure- prefix cookie
const securePrefixed = cookieSerializer.serialize('__Secure-pref', 'dark', {
	secure: true, // Required
	path: '/settings'
});
console.log(securePrefixed);
// Output: __Secure-pref=dark; Path=/settings; Secure

// __Host- prefix cookie
const hostPrefixed = cookieSerializer.serialize('__Host-user', 'admin', {
	secure: true, // Required
	path: '/', // Required
	httpOnly: true
	// domain must NOT be set
});
console.log(hostPrefixed);
// Output: __Host-user=admin; Path=/; HttpOnly; Secure

// Partitioned cookie
const partitioned = cookieSerializer.serialize('embedId', 'xyz789', {
	secure: true, // Required
	partitioned: true,
	path: '/'
});
console.log(partitioned);
// Output: embedId=xyz789; Path=/; Secure; Partitioned

serializeSigned

Serializes a cookie name, value, and a pre-computed signature into a Set-Cookie header string. The value and signature are combined (value.signature) and then URI-encoded.

Parameters:

  • name (string): The name of the cookie.
  • value (string): The original value of the cookie.
  • signature (string): The Base64 signature generated for the value (e.g., using cookieParser.makeSignature).
  • options (CookieSerializer.Options, optional): An object containing cookie attributes.

Returns:

  • (Promise<string>): A promise resolving to the formatted Set-Cookie header string for the signed cookie.

Usage Example:

import cookieSerializer from 'use-request-utils/cookie-serializer';
import cookieParser from 'use-request-utils/cookie-parser'; // To generate signature

const secret = 'my-signing-secret';
const value = 'sensitive-data';

// 1. Generate the signature
const signature = await cookieParser.makeSignature(value, secret);

// 2. Serialize the signed cookie
const signedCookieString = await cookieSerializer.serializeSigned('authToken', value, signature, {
	httpOnly: true,
	secure: true,
	path: '/',
	maxAge: 86400 // 1 day
});

console.log(signedCookieString);
// Output: authToken=sensitive-data.SIGNATURE_STRING; Max-Age=86400; Path=/; HttpOnly; Secure
// (where SIGNATURE_STRING is the URI-encoded version of the generated signature)

Cookies (/cookies.ts)

This module provides a high-level API for managing cookies using Headers objects. It integrates cookie-parser and cookie-serializer for seamless handling of cookie reading, writing, signing, and deleting.

It assumes interaction with Headers objects, typically from incoming requests (Cookie header) or outgoing responses (Set-Cookie header).

get

Retrieves the value of a specific cookie from the Cookie or Set-Cookie header.

Parameters:

  • headers (Headers): The Headers object containing the cookies.
  • name (string): The name of the cookie to retrieve.
  • prefix ('secure' | 'host', optional): If specified, automatically adds the corresponding prefix (__Secure- or __Host-) to the name before looking it up.

Returns:

  • (string): The decoded value of the cookie, or an empty string ('') if not found.

Usage Example:

import cookies from 'use-request-utils/cookies';

// Assuming headers has 'Cookie: user=john; theme=dark; __Secure-pref=safe'
const headers = new Headers({
	Cookie: 'user=john; theme=dark; __Secure-pref=safe'
});

const user = cookies.get(headers, 'user');
console.log(user); // Output: 'john'

const preference = cookies.get(headers, 'pref', 'secure');
console.log(preference); // Output: 'safe'

const nonExistent = cookies.get(headers, 'nonExistent');
console.log(nonExistent); // Output: ''

getAll

Retrieves all cookies from the Cookie or Set-Cookie header as an object.

Parameters:

  • headers (Headers): The Headers object containing the cookies.

Returns:

  • (Record<string, string>): An object where keys are cookie names and values are their decoded values. Returns an empty object ({}) if no cookies are found.

Usage Example:

import cookies from 'use-request-utils/cookies';

const headers = new Headers({
	Cookie: 'user=john; theme=dark; sessionToken=abc123xyz'
});

const allCookies = cookies.getAll(headers);
console.log(allCookies);
// Output: { user: 'john', theme: 'dark', sessionToken: 'abc123xyz' }

const emptyHeaders = new Headers();
const noCookies = cookies.getAll(emptyHeaders);
console.log(noCookies); // Output: {}

getSigned

Retrieves and verifies a specific signed cookie from the Cookie or Set-Cookie header.

Parameters:

  • headers (Headers): The Headers object containing the cookies.
  • name (string): The name of the signed cookie to retrieve (without the signature part).
  • secret (string | BufferSource): The secret key used for verifying the signature.
  • prefix ('secure' | 'host', optional): If specified, automatically adds the corresponding prefix (__Secure- or __Host-) to the name before looking it up.

Returns:

  • (Promise<string | false>): A promise resolving to the original cookie value if the signature is valid, or false if the cookie is not found, unsigned, or has an invalid signature.

Usage Example:

import cookies from 'use-request-utils/cookies';

const secret = 'my-very-secret-key';

// Assume headers contain a valid signed cookie set previously:
// Set-Cookie: session=data.VALID_SIGNATURE; HttpOnly
const headers = new Headers({
	Cookie: 'session=data.VALID_SIGNATURE; other=plain' // Replace VALID_SIGNATURE
});

// (You'd need to generate a valid signature first to test this properly)
// Let's assume 'session=data.I9qAeGQOvWjCEJgRPmrw90JjYpnnX2C9zoOiGSxh1Ig%3D' is valid for 'secret ingredient'
const headersWithValidSigned = await cookies.setSigned(new Headers(), 'session', 'data', 'secret ingredient');

// Get valid signed cookie
const sessionData = await cookies.getSigned(headersWithValidSigned, 'session', 'secret ingredient');
console.log(sessionData); // Output: 'data'

// Get unsigned cookie (returns false)
const otherData = await cookies.getSigned(headersWithValidSigned, 'other', 'secret ingredient');
console.log(otherData); // Output: false

// Get with wrong secret (returns false)
const wrongSecretData = await cookies.getSigned(headersWithValidSigned, 'session', 'wrong-secret');
console.log(wrongSecretData); // Output: false

getAllSigned

Retrieves and verifies all signed cookies from the Cookie or Set-Cookie header.

Parameters:

  • headers (Headers): The Headers object containing the cookies.
  • secret (string | BufferSource): The secret key used for verifying signatures.

Returns:

  • (Promise<Record<string, string | false>>): A promise resolving to an object containing all found signed cookies. Keys are cookie names. Values are the original value if the signature is valid, false otherwise. Unsigned cookies are ignored. Returns an empty object ({}) if no signed cookies are found.

Usage Example:

import cookies from 'use-request-utils/cookies';

const secret = 'another-secret';

// Assume headers contain multiple cookies, some signed
let headers = new Headers();
headers = await cookies.setSigned(headers, 'user', 'admin', secret);
headers = await cookies.setSigned(headers, 'pref', 'light', secret);
headers = await cookies.set(headers, 'tracker', 'xyz'); // Unsigned

const signedCookies = await cookies.getAllSigned(headers, secret);
console.log(signedCookies);
// Output: { user: 'admin', pref: 'light' }

set

Sets a cookie by appending a Set-Cookie header. Returns a new Headers object with the appended header.

Parameters:

  • headers (Headers): The original Headers object.
  • name (string): The name of the cookie.
  • value (string): The value of the cookie.
  • options (CookieSerializer.Options, optional): Cookie attributes (e.g., maxAge, path, httpOnly, secure, sameSite, prefix). Defaults path to /. Handles prefix option to set __Secure- or __Host- cookies with appropriate constraints.

Returns:

  • (Headers): A new Headers object with the Set-Cookie header appended.

Usage Example:

import cookies from 'use-request-utils/cookies';

let headers = new Headers();

// Set a simple cookie
headers = cookies.set(headers, 'theme', 'dark');
console.log(headers.get('set-cookie')); // Output: theme=dark; Path=/

// Set a cookie with options and prefix
headers = cookies.set(headers, 'session', 'xyz123', {
	httpOnly: true,
	maxAge: 3600,
	prefix: 'secure' // Will become __Secure-session and set secure=true
});
console.log(headers.get('set-cookie'));
// Output: theme=dark; Path=/, __Secure-session=xyz123; Max-Age=3600; Path=/; HttpOnly; Secure

setSigned

Sets a signed cookie by appending a Set-Cookie header. Generates the signature automatically. Returns a new Headers object.

Parameters:

  • headers (Headers): The original Headers object.
  • name (string): The name of the cookie.
  • value (string): The value to be signed and stored.
  • secret (string | BufferSource): The secret key used for signing.
  • options (CookieSerializer.Options, optional): Cookie attributes. Defaults path to /. Handles prefix option.

Returns:

  • (Promise<Headers>): A promise resolving to a new Headers object with the signed Set-Cookie header appended.

Usage Example:

import cookies from 'use-request-utils/cookies';

const secret = 'signing-key-shhh';
let headers = new Headers();

// Set a signed cookie
headers = await cookies.setSigned(headers, 'authToken', 'user-token-data', secret, {
	httpOnly: true,
	secure: true,
	maxAge: 86400 // 1 day
});

console.log(headers.get('set-cookie'));
// Output: authToken=user-token-data.VALID_SIGNATURE; Max-Age=86400; Path=/; HttpOnly; Secure
// (Where VALID_SIGNATURE is the generated signature)

del

Deletes a cookie by setting its Max-Age to 0 and Expires to a past date. Returns a new Headers object.

Parameters:

  • headers (Headers): The original Headers object.
  • name (string): The name of the cookie to delete.
  • options (CookieSerializer.Options, optional): Additional options like path and domain which must match the original cookie's settings to ensure deletion. Defaults path to /.

Returns:

  • (Headers): A new Headers object with the deleting Set-Cookie header appended. If the cookie wasn't found in the input headers, the original headers object is returned unmodified.

Usage Example:

import cookies from 'use-request-utils/cookies';

// Assume headers has 'Cookie: user=john; session=abc'
let headers = new Headers({ Cookie: 'user=john; session=abc' });

// Delete the 'session' cookie
headers = cookies.del(headers, 'session', { path: '/' });

console.log(headers.get('set-cookie'));
// Output: session=; Max-Age=0; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT

Cryptography Utilities (/crypto.ts)

Provides basic cryptographic hashing functions using the Web Crypto API (crypto.subtle).

Types

Data
type Data = string | boolean | number | object | ArrayBufferView | ArrayBuffer | ReadableStream;

Represents the type of data that can be hashed. Objects are automatically stringified using use-json.

sha1

Computes the SHA-1 hash of the input data.

Parameters:

  • data (Data): The data to hash.

Returns:

  • (Promise<string | null>): A promise resolving to the SHA-1 hash as a lowercase hexadecimal string, or null if the Web Crypto API is unavailable.

Usage Example:

import cryptoUtil from 'use-request-utils/crypto';

const text = 'rohde';
const hash = await cryptoUtil.sha1(text);
console.log(hash); // Output: '503f01750607b19c75b7815d7d5e9c221d48e4cc'

const buffer = new TextEncoder().encode(text).buffer;
const bufferHash = await cryptoUtil.sha1(buffer);
console.log(bufferHash); // Output: '503f01750607b19c75b7815d7d5e9c221d48e4cc'

sha256

Computes the SHA-256 hash of the input data.

Parameters:

  • data (Data): The data to hash.

Returns:

  • (Promise<string | null>): A promise resolving to the SHA-256 hash as a lowercase hexadecimal string, or null if the Web Crypto API is unavailable.

Usage Example:

import cryptoUtil from 'use-request-utils/crypto';

const text = 'rohde';
const hash = await cryptoUtil.sha256(text);
console.log(hash); // Output: '1094ef31e36b77eac0a14db33f777e25a12857d4e23b3b8f395c5cb6c1a28d2a'

const obj = { key: 'value' };
const objHash = await cryptoUtil.sha256(obj);
console.log(objHash); // Output: hash of JSON.stringify(obj)

const buffer = new Uint8Array([1, 2, 3]);
const bufferHash = await cryptoUtil.sha256(buffer);
console.log(bufferHash); // Output: '039058c6f2c0cb492c533b0a4d14ef77cc0f78abccced5287d84a1a2011cfb81'

Ephemeral Cache (/ephemeral-cache.ts)

Provides a lightweight, time-aware, in-memory cache specifically designed for storing and retrieving Response objects. It includes features like automatic expiration (TTL), request deduplication for concurrent requests (wrap), and cache status headers.

A default instance is exported, but you can also create your own EphemeralCache instances.

EphemeralCache Class

Constructor
new EphemeralCache(options?: { autoCleanExpired?: boolean })

Creates a new cache instance.

Parameters:

  • options.autoCleanExpired (boolean, optional, default: true): If true, automatically sets up an interval (every 15 seconds) to clean expired cache items.
Methods
get(key: string, ttlSeconds?: number): Response | null

Retrieves a cached Response object by key.

  • Returns null if the key is not found, the key is empty/whitespace, or the item has expired.
  • Expiration is checked against the ttlSeconds provided during set, or the optional ttlSeconds argument passed to get.
  • The returned Response is a new instance containing the cached body and status, with added/updated headers:
    • ephemeral-cache-status: HIT
    • ephemeral-cache-age: Age of the cached item in seconds.
    • ephemeral-cache-remaining-age: Remaining TTL in seconds.
    • ephemeral-cache-ts: Original timestamp (milliseconds) when the item was cached.
    • ephemeral-cache-ttl: The effective TTL (in seconds) used for this get operation.
set(key: string, response: Response, ttlSeconds?: number): Promise<void>

Stores a Response object in the cache.

  • The key is trimmed. Throws an error if the key is invalid (empty/whitespace).
  • The response.body is read into an ArrayBuffer for storage. If response.body is null, the item is not stored.
  • ttlSeconds defaults to 0 (meaning it expires immediately unless a TTL is provided in get). The maximum effective TTL is capped at 15 seconds (MAX_TTL_SECONDS).
  • Adds ephemeral-cache-ts and ephemeral-cache-ttl headers to the stored headers.
  • Removes any pending promise for the same key.
wrap(key: string, fn: () => Promise<Response>, options: { refreshTtl?: boolean; ttlSeconds: number }): Promise<Response>

A powerful method that attempts to retrieve a response from the cache. If missed or expired, it calls the provided function fn to generate a fresh response, caches it (if cacheable), and returns it. Handles concurrent requests for the same key, ensuring fn is called only once while pending.

  • key: The cache key (trimmed). If empty/whitespace or ttlSeconds <= 0, caching is bypassed, and fn is called directly.
  • fn: An async function that returns a Promise<Response>. Called only on cache miss or bypass.
  • options.ttlSeconds: The TTL in seconds for caching the response from fn. Max 15 seconds.
  • options.refreshTtl (boolean, default: false): If true and a cache hit occurs, the item's timestamp is updated, effectively resetting its TTL.
  • Caching Logic:
    • Only responses with cacheable content-type headers (e.g., application/json, text/*, but not *stream*) and a non-null body are cached via set.
    • If a request for key is already in progress (pending promise), subsequent wrap calls for the same key will wait for the pending promise and return a clone of its result.
    • Errors thrown by fn are propagated, and the pending promise is cleared.
  • Return Value:
    • On cache HIT: Returns the cached Response with ephemeral-cache-status: HIT and age headers.
    • On cache MISS: Returns the Response generated by fn. If cached, the original response from fn is returned (not a clone from the cache set operation).
has(key: string): boolean

Checks if a non-expired item exists for the given key (ignores TTL override).

delete(key: string): boolean

Removes an item from the cache and any associated pending promise. Returns true if an item was deleted.

clear(): void

Removes all items from the cache and clears all pending promises.

clearExpired(): void

Removes only the items that have exceeded their original ttlSeconds.

refreshTtl(key: string): void

Updates the timestamp (ts) of an existing cached item to the current time, effectively resetting its TTL. Does nothing if the key doesn't exist.

size(): number

Returns the current number of items stored in the cache.

Default Export

import ephemeralCache from 'use-request-utils/ephemeral-cache';

A pre-instantiated EphemeralCache instance with autoCleanExpired: true.

Usage Examples

import ephemeralCache, { EphemeralCache } from 'use-request-utils/ephemeral-cache';
import headersUtil from 'use-request-utils/headers';

// --- Using the default instance ---

// Example: Caching a fetch response
async function fetchData(url: string): Promise<Response> {
	console.log('Fetching fresh data for:', url);
	return fetch(url);
}

const url = 'https://api.example.com/data';
const cacheKey = 'api-data';

// First call: fetches, caches, returns fresh response
const response1 = await ephemeralCache.wrap(cacheKey, () => fetchData(url), { ttlSeconds: 5 });
const data1 = await response1.json();
console.log('Data 1:', data1);
console.log('Response 1 Headers:', headersUtil.toJson(response1.headers));
// Output: Fetching fresh data for: https://api.example.com/data
// Output: Data 1: { ... }
// Output: Response 1 Headers: { content-type: 'application/json', ... }

await new Promise(resolve => setTimeout(resolve, 1000)); // Wait 1 second

// Second call (within TTL): returns cached response
const response2 = await ephemeralCache.wrap(cacheKey, () => fetchData(url), { ttlSeconds: 5 });
const data2 = await response2.json();
console.log('Data 2:', data2);
console.log('Response 2 Headers:', headersUtil.toJson(response2.headers));
// Output: Data 2: { ... }
// Output: Response 2 Headers: { content-type: 'application/json', 'ephemeral-cache-status': 'HIT', 'ephemeral-cache-age': '1.xxx', ... }

await new Promise(resolve => setTimeout(resolve, 5000)); // Wait 5 more seconds (total > 5s)

// Third call (after TTL): fetches again
const response3 = await ephemeralCache.wrap(cacheKey, () => fetchData(url), { ttlSeconds: 5 });
const data3 = await response3.json();
console.log('Data 3:', data3);
// Output: Fetching fresh data for: https://api.example.com/data
// Output: Data 3: { ... }

// --- Manual Caching ---
const manualResponse = new Response(JSON.stringify({ manual: true }), {
	headers: { 'content-type': 'application/json' }
});
await ephemeralCache.set('manual-key', manualResponse, 10); // Cache for 10 seconds

const cachedManual = ephemeralCache.get('manual-key');
if (cachedManual) {
	console.log('Manual Cache Hit:', await cachedManual.json());
	console.log('Manual Cache Headers:', headersUtil.toJson(cachedManual.headers));
	// Output: Manual Cache Hit: { manual: true }
	// Output: Manual Cache Headers: { ..., 'ephemeral-cache-status': 'HIT', ... }
}

// Check size
console.log('Cache size:', ephemeralCache.size());

// Delete item
ephemeralCache.delete('manual-key');
console.log('Has manual-key:', ephemeralCache.has('manual-key')); // Output: false

// Clear all
ephemeralCache.clear();
console.log('Cache size after clear:', ephemeralCache.size()); // Output: 0

// --- Using a separate instance ---
const myCache = new EphemeralCache({ autoCleanExpired: false });
// Use myCache.set, myCache.get, etc.

Fetch Utilities (/fetch.ts)

Provides an enhanced fetch wrapper (fetch.http) that integrates with ephemeral-cache for automatic caching of GET/HEAD requests and standardizes error handling using HttpError. It also makes requests abortable.

fetch.http

The primary export is an http function object with multiple ways to be called, providing flexibility in how you receive the response. All variations return an abortable promise-like object.

Standard Call: http<T>(info: RequestInfo, options?: HttpOptions): Abortable<Promise<T>>

Fetches the resource and attempts to parse the response body.

  • If the response content-type header includes application/json, the body is parsed as JSON.
  • Otherwise, the raw body (likely a string) is returned.
  • If the response status is not OK (e.g., 4xx, 5xx), it throws an HttpError. The error message attempts to use the response body, otherwise uses the standard status text.
  • For GET and HEAD requests, it automatically uses ephemeralCache.wrap for caching (default TTL 1 second, refresh on hit).

Parameters:

  • info (RequestInfo): URL string or a Request object.
  • options (HttpOptions, optional): Options for the fetch call.

Returns:

  • (Abortable<Promise<T>>): An object that is both a Promise resolving to the parsed body (T) and has an abort() method. Rejects with HttpError on network errors or non-OK responses.
.asObject<T>(info: RequestInfo, options?: HttpOptions): Abortable<Promise<{ body: T; headers: Headers; status: number }>>

Fetches the resource and returns a structured object containing the parsed body, headers, and status, regardless of whether the request was successful (i.e., it does not throw for non-OK statuses).

  • Body parsing follows the same logic as the standard call.
  • Integrates with ephemeralCache similarly.

Parameters:

  • info (RequestInfo): URL string or a Request object.
  • options (HttpOptions, optional): Options for the fetch call.

Returns:

  • (Abortable<Promise<{ body: T; headers: Headers; status: number }>>): An object that is both a Promise resolving to the structured response object and has an abort() method. Rejects with HttpError only on network errors or unhandled exceptions.
.asResponse(info: RequestInfo, options?: HttpOptions): Abortable<Promise<Response>>

Fetches the resource and returns the raw Response object.

  • Does not automatically parse the body or throw HttpError for non-OK statuses (you need to check response.ok yourself).
  • Integrates with ephemeralCache similarly.

Parameters:

  • info (RequestInfo): URL string or a Request object.
  • options (HttpOptions, optional): Options for the fetch call.

Returns:

  • (Abortable<Promise<Response>>): An object that is both a Promise resolving to the raw Response object and has an abort() method. Rejects with HttpError only on network errors or unhandled exceptions.

HttpOptions Class

A helper class used internally to manage options passed to fetch.http.

class HttpOptions {
	public ephemeralCacheTtlSeconds: number; // Default: 1 second
	public init: RequestInit | null; // Standard fetch RequestInit options

	constructor(options?: { init?: RequestInit | null; ephemeralCacheTtlSeconds?: number });
}

Properties:

  • ephemeralCacheTtlSeconds: The TTL (in seconds) to use for caching GET/HEAD requests via ephemeralCache. Defaults to 1. Set to 0 or negative to disable caching for a specific call.
  • init: Standard fetch options (method, headers, body, signal, etc.) to be merged with the request.

Aborting Requests

All variants of fetch.http return an object that includes an abort() method. Calling this method will abort the underlying fetch request using an AbortController. The promise will typically reject with an HttpError with status 499 and message 'Graceful Abort'.

const request = fetch.http('https://api.example.com/long-running');

// Sometime later...
request.abort();

try {
	await request;
} catch (err) {
	if (err instanceof HttpError && err.status === 499) {
		console.log('Request was aborted successfully.');
	} else {
		console.error('Fetch failed:', err);
	}
}

Usage Examples

import fetchUtil from 'use-request-utils/fetch';
import Request from 'use-request-utils/request'; // Assuming custom Request if needed
import HttpError from 'use-http-error';

// --- Standard Call (Get JSON) ---
try {
	const data = await fetchUtil.http<{ id: number; name: string }>('https://api.example.com/users/1');
	console.log('User data:', data);
} catch (err) {
	console.error('Failed to fetch user:', err instanceof HttpError ? err.toJson() : err);
}

// --- Standard Call (Get Text) ---
try {
	const text = await fetchUtil.http<string>('https://api.example.com/status.txt');
	console.log('Status:', text);
} catch (err) {
	console.error('Failed to fetch status:', err);
}

// --- Standard Call with Options (POST) ---
try {
	const response = await fetchUtil.http<{ success: boolean }>('https://api.example.com/users', {
		init: {
			method: 'POST',
			headers: { 'Content-Type': 'application/json' },
			body: JSON.stringify({ name: 'New User' })
		},
		ephemeralCacheTtlSeconds: 0 // Disable cache for POST
	});
	console.log('Create user response:', response);
} catch (err) {
	console.error('Failed to create user:', err);
}

// --- Get as Object (Handles OK and non-OK responses) ---
const { body, headers, status } = await fetchUtil.http.asObject<{ data?: any; error?: string }>('https://api.example.com/maybe-error');
if (status === 200) {
	console.log('Success Body:', body);
} else {
	console.log(`Error ${status}:`, body);
	console.log('Error Headers:', headers);
}

// --- Get as Response (Manual Handling) ---
const response = await fetchUtil.http.asResponse('https://api.example.com/data');
if (response.ok) {
	const data = await response.json();
	console.log('Raw response success:', data);
} else {
	console.error(`Raw response failed with status ${response.status}`);
	const errorText = await response.text();
	console.error('Error body:', errorText);
}

// --- Caching Example ---
console.time('fetch1');
await fetchUtil.http('https://api.example.com/cached-resource');
console.timeEnd('fetch1'); // ~ Network time

await new Promise(r => setTimeout(r, 100)); // Wait briefly

console.time('fetch2');
await fetchUtil.http('https://api.example.com/cached-resource');
console.timeEnd('fetch2'); // ~ Should be much faster due to cache HIT

// --- Abort Example ---
const longRequest = fetchUtil.http('https://api.example.com/long-operation');
setTimeout(() => {
	console.log('Aborting request...');
	longRequest.abort();
}, 500);

try {
	await longRequest;
} catch (error) {
	if (error instanceof HttpError && error.status === 499) {
		console.log('Long request successfully aborted.');
	} else {
		console.error('Error during long request:', error);
	}
}

JWT Utilities (/jwt.ts)

Provides functions for signing, verifying, and decoding JSON Web Tokens (JWTs) using the Web Crypto API. Supports standard algorithms (HS*, RS*, ES*) and optional JWE-like encryption (AES-GCM).

Types

Jwt.Algorithm
type Algorithm = 'ES256' | 'ES384' | 'ES512' | 'HS256' | 'HS384' | 'HS512' | 'RS256' | 'RS384' | 'RS512';

Supported signing algorithms.

Jwt.EncryptionEncoding
type EncryptionEncoding = 'A128GCM' | 'A192GCM' | 'A256GCM';

Supported encryption algorithms (used when options.encrypt is enabled).

Jwt.Header<T>
type Header<T = any> = {
	alg?: Algorithm;
	type?: string; // Typically 'JWT'
} & T;

Represents the JWT header, allowing custom properties via T. alg and type are automatically managed.

Jwt.Payload<T>
type Payload<T = { [key: string]: any }> = {
	iss?: string; // Issuer
	sub?: string; // Subject
	aud?: string | string[]; // Audience
	exp?: number; // Expiration Time (Unix timestamp in seconds)
	nbf?: number; // Not Before (Unix timestamp in seconds)
	iat?: number; // Issued At (Unix timestamp in seconds) - Automatically added during sign
	jti?: string; // JWT ID
} & T;

Represents the JWT payload, including standard registered claims and custom properties via T.

Jwt.SignOptions<H>
type SignOptions<H> = {
	alg?: Algorithm | string; // Algorithm (default: 'HS256')
	encrypt?: boolean | { enc: EncryptionEncoding }; // Enable encryption (default: false). If true, uses 'A256GCM'.
	header?: Header<H>; // Custom header fields
};

Options for the sign function.

Jwt.VerifyOptions
type VerifyOptions = {
	alg?: Algorithm | string; // Algorithm to expect (default: 'HS256')
	clockTolerance?: number; // Seconds to tolerate clock skew for 'exp' and 'nbf' checks (default: 0)
	decrypt?: boolean; // Decrypt the token before verification (default: false)
};

Options for the verify function.

Jwt.Data<P, H>
type Data<P = any, H = any> = {
	header?: Header<H>;
	payload?: Payload<P>;
};

The structure returned by decode and verify, containing the parsed header and payload.

sign

Creates and signs a JWT. Can optionally encrypt the resulting JWS.

Parameters:

  • payload (Jwt.Payload<P>): The payload object for the token. iat (Issued At) is added automatically.
  • secret (string | JsonWebKey | CryptoKey): The secret or private key for signing. Can be a raw string (for HS*), a JWK object, or a CryptoKey.
  • options (Jwt.SignOptions<H> | Jwt.Algorithm, optional): Signing options or just the algorithm string. Defaults to { alg: 'HS256', header: { type: 'JWT' } }.

Returns:

  • (Promise<string>): A promise resolving to the signed (and optionally encrypted) JWT string.

Throws:

  • Error: If the payload is not an object, the secret is invalid, the algorithm is unsupported, or cryptographic operations fail.

Usage Example:

import jwt from 'use-request-utils/jwt';

const secret = 'super-secret-key';
const payload = { userId: 123, role: 'user', exp: Math.floor(Date.now() / 1000) + 3600 }; // Expires in 1 hour

// Sign with default HS256
const tokenHS256 = await jwt.sign(payload, secret);
console.log('HS256 Token:', tokenHS256);

// Sign with HS512
const tokenHS512 = await jwt.sign(payload, secret, 'HS512');
console.log('HS512 Token:', tokenHS512);

// Sign with custom header and encryption (A256GCM)
const tokenEncrypted = await jwt.sign(payload, secret, {
	alg: 'HS256',
	header: { kid: 'key-id-1' },
	encrypt: true // Enables encryption with default A256GCM
});
console.log('Encrypted Token:', tokenEncrypted);

// Sign with specific encryption encoding
const tokenEncryptedA128 = await jwt.sign(payload, secret, {
	alg: 'HS256',
	encrypt: { enc: 'A128GCM' }
});
console.log('A128GCM Encrypted Token:', tokenEncryptedA128);

// Example with RS256 (requires private key)
// const privateKey = await crypto.subtle.importKey(...); // Import your PKCS8 key
// const tokenRS256 = await jwt.sign(payload, privateKey, 'RS256');

verify

Verifies a JWT's signature and optionally decrypts it. Checks standard claims (exp, nbf) if present.

Parameters:

  • token (string): The JWT string to verify.
  • secret (string | JsonWebKey | CryptoKey): The secret or public key for verification.
  • options (Jwt.VerifyOptions | Jwt.Algorithm, optional): Verification options or just the expected algorithm string. Defaults to { alg: 'HS256', clockTolerance: 0 }.

Returns:

  • (Promise<Jwt.Data<P, H>>): A promise resolving to an object containing the verified header and payload.

Throws:

  • Error: If the token format is invalid ('Token must consist of 3 parts').
  • Error: If the algorithm is not found ('Algorithm not found').
  • Error: If the expected alg in options doesn't match the token's header alg ('ALG_MISMATCH').
  • Error: If the payload is missing or empty after decoding ('INVALID_PAYLOAD').
  • Error: If the nbf (Not Before) claim is in the future (considering clockTolerance) ('NBF').
  • Error: If the exp (Expiration Time) claim is in the past (considering clockTolerance) ('EXP').
  • Error: If the signature is invalid ('INVALID_SIGNATURE').
  • Error: If decryption fails (when options.decrypt is true).

Usage Example:

import jwt from 'use-request-utils/jwt';

const secret = 'super-secret-key';
// Assume tokenHS256 and tokenEncrypted are valid tokens generated above

try {
	// Verify HS256 token
	const verifiedDataHS256 = await jwt.verify(tokenHS256, secret); // Defaults to HS256 check
	console.log('Verified HS256 Payload:', verifiedDataHS256.payload);

	// Verify HS512 token (must specify alg)
	// const verifiedDataHS512 = await jwt.verify(tokenHS512, secret, 'HS512');
	// console.log('Verified HS512 Payload:', verifiedDataHS512.payload);

	// Verify encrypted token (must specify decrypt: true and the original signing alg)
	const verifiedEncryptedData = await jwt.verify(tokenEncrypted, secret, {
		alg: 'HS256', // The signing algorithm inside the encrypted token
		decrypt: true
	});
	console.log('Verified Encrypted Payload:', verifiedEncryptedData.payload);
	console.log('Verified Encrypted Header:', verifiedEncryptedData.header); // Should contain { alg, typ }

	// Verify with clock tolerance
	const expiredToken = await jwt.sign({ exp: Math.floor(Date.now() / 1000) - 10 }, secret);
	try {
		await jwt.verify(expiredToken, secret);
	} catch (e) {
		console.log('Verification failed (expected):', e.message); // Output: EXP
	}
	const verifiedTolerant = await jwt.verify(expiredToken, secret, { clockTolerance: 15 });
	console.log('Verified with tolerance:', verifiedTolerant.payload); // Should succeed
} catch (error) {
	console.error('Verification failed:', error.message);
}

// Example with RS256 (requires public key corresponding to the private key used for signing)
// const publicKey = await crypto.subtle.importKey(...); // Import your SPKI key
// const verifiedDataRS256 = await jwt.verify(tokenRS256, publicKey, 'RS256');

decode

Decodes a JWT without verifying the signature. Useful for reading claims before verification or when verification is handled elsewhere.

Parameters:

  • token (string): The JWT string to decode.

Returns:

  • (Jwt.Data<P, H>): An object containing the decoded header and payload. Returns undefined for header or payload if decoding fails for that part.

Usage Example:

import jwt from 'use-request-utils/jwt';

// Assume tokenHS256 is a valid token generated previously
const decodedData = jwt.decode(tokenHS256);

console.log('Decoded Header:', decodedData.header);
console.log('Decoded Payload:', decodedData.payload);

// Decoding an invalid token part might result in undefined
const invalidToken = 'invalidPart1.invalidPart2.invalidPart3';
const decodedInvalid = jwt.decode(invalidToken);
console.log('Decoded Invalid:', decodedInvalid); // Output: { header: undefined, payload: undefined }

Map Store (/map-store.ts)

A general-purpose, Map-based key-value store with support for scoping data as public or private. Useful for managing state within request lifecycles or other contexts where selective serialization is needed.

Only serializable values (string, number, boolean, null, plain object, array) can be stored.

Constructor

new MapStore(init?: Record<string, any>, defaultScope?: MapStore.Scope)

Creates a new MapStore instance.

Parameters:

  • init (Record<string, any>, optional): An initial set of key-value pairs to populate the store.
  • defaultScope (MapStore.Scope, optional, default: 'private'): The scope assigned to values added via init or set without an explicit scope.

Methods

get<T>(key: string): T | null
get<T>(key: string, defaultValue: T): T

Retrieves the value associated with a key.

Parameters:

  • key (string): The key of the value to retrieve.
  • defaultValue (T, optional): A value to return if the key is not found.

Returns:

  • (T | null): The stored value, the defaultValue if provided and the key is not found, or null if the key is not found and no defaultValue is provided. Scope is ignored when getting values.
set(key: string, value: any, scope?: MapStore.Scope): void

Sets a value associated with a key, optionally specifying its scope.

Parameters:

  • key (string): The key to set.
  • value (any): The value to store. Must be serializable (string, number, boolean, null, plain object, or array).
  • scope (MapStore.Scope, optional): The scope ('public' or 'private') for the value. Defaults to the defaultScope set in the constructor (which defaults to 'private').

Throws:

  • Error: If value is not a serializable type.
update<T = any>(key: string, fn: (value: T | null) => T, scope?: MapStore.Scope): void

Updates the value associated with a key using a function. If the key doesn't exist, null is passed to the function.

Parameters:

  • key (string): The key to update.
  • fn ((value: T | null) => T): A function that receives the current value (or null) and returns the new value to be stored.
  • scope (MapStore.Scope, optional): If provided, sets the scope of the updated value. If omitted, the existing scope is maintained, or the defaultScope is used if the key is new.
delete(key: string): void

Removes a key and its associated value from the store.

Parameters:

  • key (string): The key to delete.
size(scope?: MapStore.Scope): number

Returns the number of items in the store.

Parameters:

  • scope (MapStore.Scope, optional): If provided, returns the count of items within that specific scope ('public' or 'private'). If omitted, returns the total number of items in the store.
toJson(scope?: 'public' | 'private' | 'all'): Record<string, any>

Serializes the store's data into a plain JavaScript object, filtered by scope.

Parameters:

  • scope ('public' | 'private' | 'all', optional, default: 'public'):
    • 'public': Returns only items with public scope.
    • 'private': Returns only items with private scope.
    • 'all': Returns all items regardless of scope.

Returns:

  • (Record<string, any>): A plain object containing the key-value pairs matching the specified scope.

Usage Examples

import MapStore from 'use-request-utils/map-store';

// Initialize with some private data
const store = new MapStore({ user: { id: 1, name: 'Alice' } }); // Default scope is private

// Set public and private data
store.set('sessionId', 'abc123xyz', 'public');
store.set('internalCounter', 0); // Uses default private scope

// Get values
console.log(store.get('user')); // Output: { id: 1, name: 'Alice' }
console.log(store.get('sessionId')); // Output: 'abc123xyz'
console.log(store.get('nonExistent', 'default')); // Output: 'default'

// Update values
store.update<number>('internalCounter', count => (count ?? 0) + 1);
console.log(store.get('internalCounter')); // Output: 1

// Update and change scope
store.update('user', user => ({ ...user, role: 'admin' }), 'public');

// Check sizes
console.log('Total size:', store.size()); // Output: 3
console.log('Public size:', store.size('public')); // Output: 2 (sessionId, user)
console.log('Private size:', store.size('private')); // Output: 1 (internalCounter)

// Serialize to JSON based on scope
console.log('Public JSON:', store.toJson('public'));
// Output: { sessionId: 'abc123xyz', user: { id: 1, name: 'Alice', role: 'admin' } }

console.log('Private JSON:', store.toJson('private'));
// Output: { internalCounter: 1 }

console.log('All JSON:', store.toJson('all'));
// Output: { user: { id: 1, name: 'Alice', role: 'admin' }, sessionId: 'abc123xyz', internalCounter: 1 }

// Delete a key
store.delete('internalCounter');
console.log('Total size after delete:', store.size()); // Output: 2

Request Builder (/request-builder.ts)

A utility function for easily constructing standard Request objects, simplifying the process of setting methods, URLs, query parameters, headers, cookies, and various body types (JSON, FormData, raw).

requestBuilder Function

requestBuilder(input: string, options?: RequestBuilder.Options): Request

Parameters:

  • input (string): The URL for the request.
  • options (RequestBuilder.Options, optional): Configuration options for the request.

Returns:

  • (Request): A standard Request object configured according to the input and options.
RequestBuilder.Options Type
type Options = {
	body?: BodyInit | Record<string, unknown>; // Raw body or JSON object
	cookies?: Record<string, string>; // Cookies to add as 'Cookie' header
	form?: FormData | Record<string, string | string[]>; // FormData or object to convert
	headers?: HeadersInit | null | undefined; // Request headers
	json?: Record<string, unknown>; // JSON body (sets Content-Type)
	method?: string; // HTTP method (default: 'GET')
	query?: Record<string, string | string[]>; // URL query parameters
	signal?: AbortSignal; // AbortSignal for cancellation
};
  • body: Sets the request body. Can be standard BodyInit types (string, Blob, BufferSource, FormData, URLSearchParams, ReadableStream) or a plain object (which will be JSON-stringified and Content-Type: application/json header set, unless form or json option is also used). If form or json is set, body takes precedence if provided.
  • cookies: A record of cookie names and values. These are serialized into a Cookie header string (using basic cookieSerializer.serialize with path=/) and added to the request headers.
  • form: Sets the request body to FormData. Can be an existing FormData instance or a plain object. If an object is provided, it's converted to FormData, handling array values by appending multiple entries for the same key. Automatically sets the appropriate Content-Type header (usually omitted, letting the browser set it with the boundary). Overrides json. If body is also set, body takes precedence.
  • headers: Standard HeadersInit (Headers object, plain object, or array of pairs) to set request headers. Merged with headers set automatically by json, form, or cookies.
  • json: Sets the request body by JSON-stringifying the provided object. Automatically sets the Content-Type: application/json header. Overridden by form and body.
  • method: The HTTP request method (e.g., 'GET', 'POST', 'PUT'). Defaults to 'GET'. The body is only included for methods other than 'GET', 'HEAD', 'OPTIONS'.
  • query: A record of query parameters. Keys are parameter names, values are strings or arrays of strings. Appended to the input URL.
  • signal: An AbortSignal to allow cancelling the request.

Usage Examples

import requestBuilder from 'use-request-utils/request-builder';

// 1. GET request with query parameters
const getReq = requestBuilder('https://api.example.com/items', {
	query: {
		page: '2',
		sortBy: 'price',
		tags: ['featured', 'sale'] // Array values become multiple params
	}
});
console.log(getReq.url);
// Output: https://api.example.com/items?page=2&sortBy=price&tags=featured&tags=sale
console.log(getReq.method); // Output: GET

// 2. POST request with JSON body
const postJsonReq = requestBuilder('https://api.example.com/users', {
	method: 'POST',
	json: { name: 'Alice', email: '[email protected]' }
});
console.log(postJsonReq.method); // Output: POST
console.log(postJsonReq.headers.get('content-type')); // Output: application/json
// console.log(await postJsonReq.text()); // Output: {"name":"Alice","email":"[email protected]"}

// 3. POST request with FormData (from object)
const postFormReq = requestBuilder('https://api.example.com/upload', {
	method: 'POST',
	form: {
		description: 'User profile picture',
		tags: ['profile', 'avatar']
		// file: new File(...) // Usually you'd use a File object here in browser
	}
});
console.log(postFormReq.method); // Output: POST
// Content-Type header will be set by fetch/browser with boundary
// const formData = await postFormReq.formData();
// console.log(formData.get('description')); // Output: User profile picture
// console.log(formData.getAll('tags'));   // Output: ['profile', 'avatar']

// 4. Request with custom headers and cookies
const secureReq = requestBuilder('https://api.example.com/profile', {
	method: 'GET',
	headers: {
		'X-API-Key': 'mysecretkey',
		Accept: 'application/vnd.api+json'
	},
	cookies: {
		sessionToken: 'abc123xyz',
		userId: 'user-42'
	}
});
console.log(secureReq.headers.get('X-API-Key')); // Output: mysecretkey
console.log(secureReq.headers.get('cookie'));
// Output: sessionToken=abc123xyz; Path=/; userId=user-42; Path=/

// 5. Using the 'body' option directly (e.g., for plain text or pre-formatted data)
const putReq = requestBuilder('https://api.example.com/config.txt', {
	method: 'PUT',
	body: 'Setting=Value\nAnother=Option',
	headers: { 'Content-Type': 'text/plain' } // Explicitly set Content-Type if needed
});
console.log(putReq.method); // Output: PUT
// console.log(await putReq.text()); // Output: Setting=Value\nAnother=Option

// 6. Using AbortSignal
const controller = new AbortController();
const abortableReq = requestBuilder('https://api.example.com/long-poll', {
	signal: controller.signal
});
// controller.abort(); // Call this to cancel the request

Custom Request Class (/request.ts)

Extends the standard Request class to include Cloudflare-specific properties, specifically the cf object.

CustomRequest Class

This class behaves identically to the standard Request but adds a cf property.

Constructor
new CustomRequest(
  input: RequestInfo | URL,
  init?: RequestInit & {
    cf?: CfProperties; // Cloudflare properties object
  }
)

Creates a new CustomRequest instance.

Parameters:

  • input: Same as the standard Request constructor (URL string or Request object).
  • init: Same as the standard Request constructor, but can optionally include a cf property.

Properties:

  • Inherits all properties and methods from the standard Request.
  • cf (CfProperties): Contains Cloudflare-specific request properties (like geo-location, TLS version, etc.). Defaults to an empty object ({}) if not provided in init.

Usage Example

This class is typically used internally by frameworks or utilities running in a Cloudflare Workers environment where the cf object is available on the incoming request.

// In a Cloudflare Worker environment:
// Assuming 'incomingRequest' is the Request object passed to the fetch handler

import CustomRequest from 'use-request-utils/request';
import type { CfProperties } from '@cloudflare/workers-types';

// Example of how it might be instantiated internally
const cfProps: CfProperties = {
  // Example Cloudflare properties
  country: 'US',
  colo: 'LAX',
  tlsVersion: 'TLSv1.3',
  // ... other properties
};

const customReq = new CustomRequest(incomingRequest.url, {
    method: incomingRequest.method,
    headers: incomingRequest.headers,
    body: incomingRequest.body,
    cf: cfProps // Pass the cf object during instantiation
});

// Accessing the cf property
console.log('Request country:', customReq.cf.country); // Output: US
console.log('Request colo:', customReq.cf.colo);     // Output: LAX

// Can be used like a regular Request object
fetch(customReq).then(...);

Router (/router.ts)

A fast and flexible request router implementation based on path matching. It supports static paths, path parameters (including optional and regex-constrained parameters), wildcards, and different HTTP methods.

Router<T> Class

Constructor
new Router<T>();

Creates a new router instance. The type parameter T defines the type of the handler associated with each route (e.g., a function, an object, a s