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.
Index
- Installation
- Features
- Core Modules:
- RPC (Remote Procedure Call) Modules:
- React Hooks:
- Author
- License
Installation
npm install use-request-utilsor with yarn:
yarn add use-request-utilsFeatures
- 🍪 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 constructingRequestobjects 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 withephemeral-cachefor GET/HEAD requests, standardized error handling usingHttpError, and abortable requests. - ⚡ Ephemeral Cache: A lightweight, time-aware in-memory cache (
ephemeral-cache) specifically designed forResponseobjects, featuring automatic expiration, request deduplication viawrap, 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 (likeRpcContext). - ⚙️ 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 newHeadersinstance.
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 newHeadersinstance containing the merged headers.
Throws:
TypeError: If any argument (that isn'tnullorundefined) is not an object-like structure compatible withHeadersInit.
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): TheHeadersinstance 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 rawCookieheader 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 rawCookieheader string.secret(string | BufferSource): The secret key used for signing. Can be a string or a BufferSource (likeUint8Array).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,falseotherwise.
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 importedCryptoKeyobject representing the secret. (Usecrypto.subtle.importKeyor the internalgetCryptoKeylogic if needed).
Returns:
- (
Promise<boolean>): A promise resolving totrueif the signature is valid,falseotherwise.
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 forSameSite=NoneandPartitioned.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. Requiressecure: true.prefix: (Internal helper incookies.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 formattedSet-Cookieheader string.
Throws:
Error: If prefix constraints (__Secure-,__Host-) are violated.Error: Ifpartitionedis true butsecureis not.Error: IfmaxAgeorexpiresexceed 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; PartitionedserializeSigned
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., usingcookieParser.makeSignature).options(CookieSerializer.Options, optional): An object containing cookie attributes.
Returns:
- (
Promise<string>): A promise resolving to the formattedSet-Cookieheader 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): TheHeadersobject 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 thenamebefore 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): TheHeadersobject 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): TheHeadersobject 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 thenamebefore looking it up.
Returns:
- (
Promise<string | false>): A promise resolving to the original cookie value if the signature is valid, orfalseif 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: falsegetAllSigned
Retrieves and verifies all signed cookies from the Cookie or Set-Cookie header.
Parameters:
headers(Headers): TheHeadersobject 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,falseotherwise. 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 originalHeadersobject.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). Defaultspathto/. Handlesprefixoption to set__Secure-or__Host-cookies with appropriate constraints.
Returns:
- (
Headers): A newHeadersobject with theSet-Cookieheader 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; SecuresetSigned
Sets a signed cookie by appending a Set-Cookie header. Generates the signature automatically. Returns a new Headers object.
Parameters:
headers(Headers): The originalHeadersobject.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. Defaultspathto/. Handlesprefixoption.
Returns:
- (
Promise<Headers>): A promise resolving to a newHeadersobject with the signedSet-Cookieheader 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 originalHeadersobject.name(string): The name of the cookie to delete.options(CookieSerializer.Options, optional): Additional options likepathanddomainwhich must match the original cookie's settings to ensure deletion. Defaultspathto/.
Returns:
- (
Headers): A newHeadersobject with the deletingSet-Cookieheader appended. If the cookie wasn't found in the inputheaders, the originalheadersobject 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 GMTCryptography 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, ornullif 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, ornullif 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): Iftrue, 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
nullif the key is not found, the key is empty/whitespace, or the item has expired. - Expiration is checked against the
ttlSecondsprovided duringset, or the optionalttlSecondsargument passed toget. - The returned
Responseis a new instance containing the cached body and status, with added/updated headers:ephemeral-cache-status:HITephemeral-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 thisgetoperation.
set(key: string, response: Response, ttlSeconds?: number): Promise<void>
Stores a Response object in the cache.
- The
keyis trimmed. Throws an error if the key is invalid (empty/whitespace). - The
response.bodyis read into anArrayBufferfor storage. Ifresponse.bodyis null, the item is not stored. ttlSecondsdefaults to0(meaning it expires immediately unless a TTL is provided inget). The maximum effective TTL is capped at15seconds (MAX_TTL_SECONDS).- Adds
ephemeral-cache-tsandephemeral-cache-ttlheaders 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 orttlSeconds <= 0, caching is bypassed, andfnis called directly.fn: An async function that returns aPromise<Response>. Called only on cache miss or bypass.options.ttlSeconds: The TTL in seconds for caching the response fromfn. Max 15 seconds.options.refreshTtl(boolean, default:false): Iftrueand a cache hit occurs, the item's timestamp is updated, effectively resetting its TTL.- Caching Logic:
- Only responses with cacheable
content-typeheaders (e.g.,application/json,text/*, but not*stream*) and a non-null body are cached viaset. - If a request for
keyis already in progress (pending promise), subsequentwrapcalls for the samekeywill wait for the pending promise and return a clone of its result. - Errors thrown by
fnare propagated, and the pending promise is cleared.
- Only responses with cacheable
- Return Value:
- On cache HIT: Returns the cached
Responsewithephemeral-cache-status: HITand age headers. - On cache MISS: Returns the
Responsegenerated byfn. If cached, the original response fromfnis returned (not a clone from the cachesetoperation).
- On cache HIT: Returns the cached
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-typeheader includesapplication/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
GETandHEADrequests, it automatically usesephemeralCache.wrapfor caching (default TTL 1 second, refresh on hit).
Parameters:
info(RequestInfo): URL string or aRequestobject.options(HttpOptions, optional): Options for the fetch call.
Returns:
- (
Abortable<Promise<T>>): An object that is both aPromiseresolving to the parsed body (T) and has anabort()method. Rejects withHttpErroron 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
ephemeralCachesimilarly.
Parameters:
info(RequestInfo): URL string or aRequestobject.options(HttpOptions, optional): Options for the fetch call.
Returns:
- (
Abortable<Promise<{ body: T; headers: Headers; status: number }>>): An object that is both aPromiseresolving to the structured response object and has anabort()method. Rejects withHttpErroronly 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
HttpErrorfor non-OK statuses (you need to checkresponse.okyourself). - Integrates with
ephemeralCachesimilarly.
Parameters:
info(RequestInfo): URL string or aRequestobject.options(HttpOptions, optional): Options for the fetch call.
Returns:
- (
Abortable<Promise<Response>>): An object that is both aPromiseresolving to the rawResponseobject and has anabort()method. Rejects withHttpErroronly 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 viaephemeralCache. Defaults to1. Set to0or negative to disable caching for a specific call.init: Standardfetchoptions (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 verifiedheaderandpayload.
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 expectedalgin options doesn't match the token's headeralg('ALG_MISMATCH').Error: If the payload is missing or empty after decoding ('INVALID_PAYLOAD').Error: If thenbf(Not Before) claim is in the future (consideringclockTolerance) ('NBF').Error: If theexp(Expiration Time) claim is in the past (consideringclockTolerance) ('EXP').Error: If the signature is invalid ('INVALID_SIGNATURE').Error: If decryption fails (whenoptions.decryptis 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 decodedheaderandpayload. Returnsundefinedfor 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 viainitorsetwithout 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, thedefaultValueif provided and the key is not found, ornullif the key is not found and nodefaultValueis 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 thedefaultScopeset in the constructor (which defaults to'private').
Throws:
Error: Ifvalueis 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 (ornull) 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 thedefaultScopeis 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: 2Request 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): RequestParameters:
input(string): The URL for the request.options(RequestBuilder.Options, optional): Configuration options for the request.
Returns:
- (
Request): A standardRequestobject 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 standardBodyInittypes (string, Blob, BufferSource, FormData, URLSearchParams, ReadableStream) or a plain object (which will be JSON-stringified andContent-Type: application/jsonheader set, unlessformorjsonoption is also used). Ifformorjsonis set,bodytakes precedence if provided.cookies: A record of cookie names and values. These are serialized into aCookieheader string (using basiccookieSerializer.serializewithpath=/) and added to the request headers.form: Sets the request body toFormData. Can be an existingFormDatainstance or a plain object. If an object is provided, it's converted toFormData, handling array values by appending multiple entries for the same key. Automatically sets the appropriateContent-Typeheader (usually omitted, letting the browser set it with the boundary). Overridesjson. Ifbodyis also set,bodytakes precedence.headers: StandardHeadersInit(Headers object, plain object, or array of pairs) to set request headers. Merged with headers set automatically byjson,form, orcookies.json: Sets the request body by JSON-stringifying the provided object. Automatically sets theContent-Type: application/jsonheader. Overridden byformandbody.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 theinputURL.signal: AnAbortSignalto 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 requestCustom 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 standardRequestconstructor (URL string or Request object).init: Same as the standardRequestconstructor, but can optionally include acfproperty.
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 ininit.
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
