Define cookies once as named values in application code, choose signing or encryption, provide multiple keys for rotation, and set transport attributes such as path, domain, SameSite, HttpOnly, Secure, Partitioned, and Max-Age. The library reads them from the incoming Cookie header, returns a typed cookie interface for getting, setting, refreshing, and deleting values, and emits only the changed Set-Cookie headers for the response. Existing cookie definitions support runtime updates for keys, Max-Age, HttpOnly, SameSite, Secure, and Partitioned without restarting the process. Cookie definitions can be grouped into reusable jars and combined across modules.
pnpm add seedpodsA typical request and response flow has four parts: derive or load keys, assign stable identifiers to them, define cookies, and open the resulting jars for each request.
import { createCookie, createJar, deriveKey, patchCookie, useCookies } from 'seedpods'
// Derive or load keys once at startup.
const currentSessionKey = await deriveKey(process.env.SESSION_SECRET!, { salt: 'session' })
const previousSessionKey = await deriveKey(process.env.SESSION_SECRET_PREVIOUS!, {
salt: 'session',
})
const recentViewsKey = await deriveKey(process.env.RECENT_VIEWS_SECRET!, {
salt: 'recent-views',
})
// Wrap each key with a stable identifier. The first entry writes new cookies.
// Later entries remain readable during rotation.
const currentSessionKeyEntry = { id: 'current-session', value: currentSessionKey }
const previousSessionKeyEntry = {
id: 'previous-session',
value: previousSessionKey,
}
const recentViewsKeyEntry = { id: 'recent-views', value: recentViewsKey }
// Define cookies.
const sessionCookie = createCookie<'session', 'aes-gcm', { userId: string }>({
key: 'session',
type: 'aes-gcm',
keys: [currentSessionKeyEntry, previousSessionKeyEntry],
prefix: '__Host-',
path: '/',
secure: true,
httpOnly: true,
sameSite: 'Lax',
maxAge: 60 * 60 * 24 * 7,
})
const recentViewsCookie = createCookie<'recentViews', 'hmac', string[]>({
key: 'recentViews',
name: 'recent-views',
type: 'hmac',
keys: [recentViewsKeyEntry],
path: '/',
secure: true,
sameSite: 'Lax',
maxAge: 60 * 60 * 24 * 30,
})
// Group cookies into reusable jars.
const authCookies = createJar().put(sessionCookie)
const uiCookies = createJar().put(recentViewsCookie)
const appCookies = createJar().combine(authCookies).combine(uiCookies)
export async function handleRequest(request: Request) {
const cookies = await useCookies(request.headers.get('cookie') ?? undefined, appCookies, {
recentViews(previous = [], next = []) {
return [...previous, ...next].slice(-10)
},
})
const signOut = new URL(request.url).pathname === '/logout'
if (signOut) {
cookies.del('session')
} else if (cookies.get('session') === undefined) {
cookies.set('session', { userId: '123' })
}
cookies.set('recentViews', ['/docs/getting-started'])
const responseHeaders = new Headers()
for (const value of await cookies.values()) {
responseHeaders.append('Set-Cookie', value)
}
return new Response('ok', { headers: responseHeaders })
}useCookies() returns only the changed Set-Cookie values through values(). Use entries() when the cookie key is also needed.
Runtime cookie settings can be updated on an existing cookie definition without recreating jars or restarting the process.
patchCookie(sessionCookie, (draft) => {
draft.keys = [nextSessionKeyEntry, currentSessionKeyEntry, previousSessionKeyEntry]
draft.sameSite = 'Strict'
draft.maxAge = 60 * 60 * 24 * 14
})The call updates later requests immediately. Existing useCookies() instances keep the request-local configuration they already captured.
useCookies()accepts the rawCookieheader value.- Each jar key must be unique. Calling
putwith the same key twice throws. - Different cookie definitions may still share one cookie name when the decoded value carries a different logical key.
- A reducer may return
undefinedto delete a cookie. If a reducer throws, that update is aborted and the earlier state is preserved. Domainmust be an ASCII host name. Internationalized domains must use their ASCII form, for examplexn--bcher-kva.exampleinstead ofbücher.example.partitioned: truerequiressecure: true. Browsers enforce partitioned storage semantics; seedpods only emits thePartitionedattribute.- Reading a cookie with a non-primary configured key causes the next output to rewrite it with the first configured key. Reading a cookie whose transport policy differs from the current definition rewrites it with the current
maxAge,sameSite,httpOnly,secure, andpartitionedattributes. Some rewrites take effect only when the user agent accepts theSet-Cookieheader for that response. Underrfc6265bis,SameSite=LaxandSameSite=Strictcookies are not set in responses to cross-site subresource requests or cross-site nested navigations. Changes toname,prefix,domain, andpathare not migrated automatically. - If a configured cookie cannot be verified or decoded,
get()returnsundefinedand the next output expires it. values()andentries()emit only changes. Deleting an already unset cookie records no new change, and unchanged values do not produce aSet-Cookieheader unlessrefresh()is called explicitly. Userefresh(key)for sliding sessions and other flows that need to rewrite the current value to extend browser-managed lifetime such asMax-Age.
function assertCookie ↗
Asserts that a value was created by createCookie.
export declare function assertCookie(
cookie: unknown,
): asserts cookie is SeedpodsCookie<string, SeedpodsCookieType, unknown>| Parameter | Type | Description |
|---|---|---|
cookie |
unknown |
Value to validate. |
SeedpodsError When the value is not a cookie definition created by this package.
function assertJar ↗
Asserts that a value was created by createJar.
export declare function assertJar(value: unknown): asserts value is SeedpodsJarInterface| Parameter | Type | Description |
|---|---|---|
value |
unknown |
Value to validate. |
SeedpodsError When the value is not a cookie jar created by this package.
function createCookie ↗
Creates a cookie definition for one application value.
createCookie: <T extends string, U extends SeedpodsCookieType, V>(
options: SeedpodsCookieOptionsForType<T, U>,
) => SeedpodsCookie<T, U, V>| Parameter | Description |
|---|---|
T |
Key used to read, write, and delete this cookie through the cookie interface. |
U |
Cookie protection mode. |
V |
Application value stored in the cookie. |
| Parameter | Type | Description |
|---|---|---|
options |
SeedpodsCookieOptionsForType<T, U> |
Cookie configuration, including the logical cookie key, configured cryptographic keys, and transport attributes. |
A cookie definition that can be added to a jar.
When the cookie options are invalid.
Use this function to declare how a cookie is named, protected, and serialized. The returned definition can be added to a jar created by createJar and later used by useCookies to read incoming cookies and produce Set-Cookie header values.
function createJar ↗
Creates an empty cookie jar.
createJar: () => SeedpodsJarBuilder<SeedpodsJar<SeedpodsJarEmptyState>, 'combine' | 'put'>An empty cookie jar with put and combine operations.
Use put to add cookie definitions and combine to merge another jar. In TypeScript, each call returns a new jar type that keeps the available cookie keys aligned with the configured definitions.
function getSeedpodsErrorCausesByType ↗
Returns the causes from a SeedpodsError that match the given type.
export declare function getSeedpodsErrorCausesByType<T extends SeedpodsErrorType>(
error: SeedpodsError,
type: T,
): Array<SeedpodsErrorCause<T>>| Parameter | Description |
|---|---|
T |
Error cause type to match. |
| Parameter | Type | Description |
|---|---|---|
error |
SeedpodsError |
Error instance to inspect. |
type |
T |
Error cause type to match. |
All matching causes in their original order.
function isSeedpodsError ↗
Checks whether a value is a SeedpodsError.
export declare function isSeedpodsError(value: unknown): value is SeedpodsError| Parameter | Type | Description |
|---|---|---|
value |
unknown |
Value to test. |
true when the value is a SeedpodsError; otherwise, false.
function isSeedpodsErrorOfType ↗
Checks whether a SeedpodsError contains at least one cause of the given type.
export declare function isSeedpodsErrorOfType<T extends SeedpodsErrorType>(
error: SeedpodsError,
type: T,
): error is SeedpodsError<T>| Parameter | Description |
|---|---|
T |
Error cause type to match. |
| Parameter | Type | Description |
|---|---|---|
error |
SeedpodsError |
Error instance to inspect. |
type |
T |
Error cause type to match. |
true when the error contains at least one matching cause; otherwise, false.
function patchCookie ↗
Updates the hot-patchable runtime fields of an existing cookie definition.
patchCookie: <T extends string, U extends SeedpodsCookieType, V>(cookie: SeedpodsCookie<T, U, V>,
recipe: (draft: SeedpodsCookieRuntimeDraft) => void) => void| Parameter | Description |
|---|---|
T |
Application key used to address the cookie through the cookie interface. |
U |
Cookie protection mode. |
V |
Application value stored in the cookie. |
| Parameter | Type | Description |
|---|---|---|
cookie |
SeedpodsCookie<T, U, V> |
Cookie definition created by createCookie. |
recipe |
(draft: SeedpodsCookieRuntimeDraft) => void |
Callback that mutates the runtime draft before the next configuration is validated and committed. |
SeedpodsError When cookie was not created by this package or when the patched runtime options are invalid.
This function can rotate configured keys and update maxAge, httpOnly, sameSite, secure, and partitioned without recreating the cookie definition. It does not change cookie identity or browser scope fields such as key, type, name, prefix, domain, or path.
The update is committed before the function returns. Later requests observe the new runtime immediately. Existing useCookies instances keep the request-local configuration they captured when they were created.
Scope follows the cookie object returned by createCookie. When the same cookie object is reused across multiple jars, one call updates all jars that reference it. When validation fails, the function throws and leaves the previous runtime unchanged.
function useCookies ↗
Creates a cookie interface from a Cookie header value or parsed cookie map and a cookie jar.
useCookies: <SeedpodsJar extends SeedpodsJarInterface>(
cookieHeader: SeedpodsCookieHeader,
jar: SeedpodsJar,
reducers?: SeedpodsCookiesReducers<SeedpodsJar>,
) => Promise<SeedpodsCookies<SeedpodsJar>>| Parameter | Description |
|---|---|
SeedpodsJar |
Jar type that defines the available cookie keys and value types. |
| Parameter | Type | Description |
|---|---|---|
cookieHeader |
SeedpodsCookieHeader |
Incoming Cookie header value or map returned by parseCookieHeader. When omitted, the interface starts with no received cookies. |
jar |
SeedpodsJar |
Cookie definitions created with createJar. |
reducers |
SeedpodsCookiesReducers<SeedpodsJar> |
Optional reducers that combine the current cookie value with a later value passed to set. |
A cookie interface for reading values, recording changes, and generating changed Set-Cookie header values.
The returned interface reads configured cookie values through get, records changes through set, del, and refresh, and produces changed Set-Cookie header values through entries and values. If the header contains the same cookie name more than once, the function keeps the best decodable value for each configured cookie.
function deriveKey ↗
Derives a symmetric key from a secret string.
deriveKey: (secret: string, options?: SeedpodsDeriveKeyOptions) => Promise<Uint8Array>| Parameter | Type | Description |
|---|---|---|
secret |
string |
Secret input used as the derivation source. |
options |
SeedpodsDeriveKeyOptions |
Optional derivation parameters, including salt and iteration count. |
The derived key bytes.
The function uses Password-Based Key Derivation Function 2 with SHA-512 and returns raw key bytes. Pass a fixed salt when the same secret must produce the same key across processes or deployments. When no salt is provided, a random salt is generated and the derived key changes between calls.
The returned bytes are algorithm-agnostic. The WebCrypto API requires an algorithm label during derivation; internally the function uses AES-GCM as that label, but the exported raw bytes are suitable for both aes-gcm and hmac cookie types.
function parseCookieHeader ↗
Parses a Cookie header value into a map of cookie names and values.
export declare function parseCookieHeader(string?: string): Map<string, string[]>| Parameter | Type | Description |
|---|---|---|
string |
string |
Raw Cookie header value. |
A map from each cookie name to all received values for that name.
Repeated cookie names are preserved in encounter order. Quoted cookie values keep their surrounding double quotes because rfc6265bis treats them as part of the cookie-value. Fragments without an equals sign are ignored. When the input is undefined, the function returns an empty map.
class SeedpodsError ↗
Error thrown for invalid cookie definitions, invalid cookie values, invalid jar values, and other rejected inputs.
export declare class SeedpodsError<T extends SeedpodsErrorType = SeedpodsErrorType> extends Error| Parameter | Description |
|---|---|
T |
Error cause type carried by the instance. |
The causes property stores machine-readable error details. Validation may report more than one cause in a single error.
Constructs a new instance of the SeedpodsError class
constructor(causes: ReadonlyArray<SeedpodsErrorCause<T>>);| Parameter | Type |
|---|---|
causes |
ReadonlyArray<SeedpodsErrorCause<T>> |
interface SeedpodsConfiguredKey ↗
Configured cryptographic key used to sign or encrypt cookies.
export interface SeedpodsConfiguredKeyThe first configured key writes new cookies. Later keys remain readable during rotation. The identifier is written into the cookie as non-secret metadata so the matching configured key can be selected directly.
Stable identifier for this configured key.
id: stringRaw key bytes.
value: Uint8Arrayinterface SeedpodsCookie ↗
Cookie definition returned by createCookie.
export interface SeedpodsCookie<T extends string = any, U extends SeedpodsCookieType = any, _V = any>| Parameter | Description |
|---|---|
T |
Application key used to address the cookie. |
U |
Cookie protection mode. |
_V |
Application value stored in the cookie. |
Most callers create values of this type through createCookie and then add them to a jar with createJar.
interface SeedpodsCookieOptionsBase ↗
Common cookie options shared by all cookie definitions.
export interface SeedpodsCookieOptionsBase<SeedpodsCookieKey extends string = string>| Parameter | Description |
|---|---|
SeedpodsCookieKey |
Application key used to address the cookie through the cookie interface. |
Domain attribute written to Set-Cookie.
domain?: string;Whether the cookie is inaccessible to client-side JavaScript.
httpOnly?: boolean;Application key used to read, write, and delete the cookie.
key: SeedpodsCookieKeyMax-Age attribute, in seconds.
maxAge?: number;Cookie name written to the header. When omitted, the key is used.
name?: string;Whether the cookie should opt in to partitioned storage where supported.
partitioned?: boolean;partitioned: true requires secure: true.
Path attribute written to Set-Cookie.
path?: string;Optional cookie name prefix.
prefix?: SeedpodsCookiePrefix;SameSite attribute written to Set-Cookie.
sameSite?: SeedpodsCookieSameSite;sameSite: 'None' requires secure: true.
Whether the cookie requires a secure transport.
secure?: boolean;interface SeedpodsCookies ↗
Mutable cookie interface returned by useCookies.
export interface SeedpodsCookies<SeedpodsJarType extends SeedpodsJarInterface>| Parameter | Description |
|---|---|
SeedpodsJarType |
Jar type that defines the available cookie keys and value types. |
Call set, del, or refresh to record deliberate cookie writes. Calling refresh rewrites the current cookie value without changing its logical value.
Records deletion of one cookie key.
del: (key: SeedpodsJarKeys<SeedpodsJarType>) => void;Returns changed cookie keys and their Set-Cookie header values.
entries: () => Promise<Array<[SeedpodsJarKeys<SeedpodsJarType>, string]>>Returns the current value for one cookie key.
get: <SeedpodsKey extends SeedpodsJarKeys<SeedpodsJarType>>(key: SeedpodsKey) =>
SeedpodsJarCookieValue<SeedpodsJarType, SeedpodsKey> | undefinedRewrites the current value for one cookie key without changing that value.
refresh: (key: SeedpodsJarKeys<SeedpodsJarType>) => void;This is useful for renewing browser-managed lifetime such as Max-Age when the logical value stays the same.
Records a new value for one cookie key.
set: <SeedpodsKey extends SeedpodsJarKeys<SeedpodsJarType>>(key: SeedpodsKey,
value: SeedpodsJarCookieValue<SeedpodsJarType, SeedpodsKey> | undefined) => void;Returns changed Set-Cookie header values.
values: () => Promise<string[]>interface SeedpodsDeriveKeyOptions ↗
Options for deriveKey.
export interface SeedpodsDeriveKeyOptionsPassword-Based Key Derivation Function 2 iteration count.
iterations?: number;Requested output length, in bytes.
length?: number;The current implementation always derives a 256-bit key.
Salt used for key derivation.
salt?: string;interface SeedpodsEncryptedCookieOptions ↗
Options for an encrypted cookie definition.
export interface SeedpodsEncryptedCookieOptions<SeedpodsCookieKey extends string = string>
extends SeedpodsCookieOptionsBase<SeedpodsCookieKey>| Parameter | Description |
|---|---|
SeedpodsCookieKey |
Application key used to address the cookie through the cookie interface. |
The first configured key writes new cookies. Later keys remain readable during rotation.
Configured encryption keys in primary-first order.
keys: SeedpodsConfiguredKey[];Encryption mode.
type: 'aes-gcm'interface SeedpodsErrorMetadata ↗
Structured data carried by each SeedpodsError cause type.
export interface SeedpodsErrorMetadatainterface SeedpodsJar ↗
Cookie jar returned by createJar.
export interface SeedpodsJar<State extends SeedpodsJarState> extends SeedpodsJarInterface<State>| Parameter | Description |
|---|---|
State |
Jar state tracked by the type system. |
Most callers build this type through chained put and combine calls rather than constructing it directly.
Merges another cookie jar into this one.
combine: <SeedpodsChildJar extends SeedpodsJar<SeedpodsJarState>>(jar: SeedpodsChildJar) =>
SeedpodsJarBuilder<
SeedpodsJar<SeedpodsJarStateAfterAction<State, SeedpodsJarCombineAction<SeedpodsChildJar>>>,
'combine' | 'put' | typeof SEEDPODS_SYMBOL_JAR
>Adds one cookie definition to the jar.
put: <SeedpodsCookieTypeValue extends SeedpodsCookie>(cookie: SeedpodsCookieTypeValue) =>
SeedpodsJarBuilder<
SeedpodsJar<
SeedpodsJarStateAfterAction<State, SeedpodsJarCookieAction<SeedpodsCookieTypeValue>>
>,
'combine' | 'put' | typeof SEEDPODS_SYMBOL_JAR
>interface SeedpodsSignedCookieOptions ↗
Options for a signed cookie definition.
export interface SeedpodsSignedCookieOptions<SeedpodsCookieKey extends string = string>
extends SeedpodsCookieOptionsBase<SeedpodsCookieKey>| Parameter | Description |
|---|---|
SeedpodsCookieKey |
Application key used to address the cookie through the cookie interface. |
The cookie value remains readable by clients. The signature prevents undetected modification.
Configured signing keys in primary-first order.
keys: SeedpodsConfiguredKey[];Signing mode.
type: 'hmac'type SeedpodsCookieHeader ↗
Cookie header input accepted by useCookies.
export type SeedpodsCookieHeader = string | SeedpodsParsedCookieHeader | undefinedPass a string to let seedpods parse the header, pass a parsed map to filter or otherwise adjust cookie values before opening a jar, or pass undefined to start with no received cookies.
type SeedpodsCookieOptions ↗
Supported options accepted by createCookie.
export type SeedpodsCookieOptions<SeedpodsCookieKey extends string = string> =
| SeedpodsEncryptedCookieOptions<SeedpodsCookieKey>
| SeedpodsSignedCookieOptions<SeedpodsCookieKey>| Parameter | Description |
|---|---|
SeedpodsCookieKey |
Application key used to address the cookie through the cookie interface. |
type SeedpodsCookiePrefix ↗
Supported cookie name prefixes for createCookie.
export type SeedpodsCookiePrefix = (typeof SEEDPODS_COOKIE_PREFIXES)[number]__Secure- requires secure: true. __Host- requires secure: true, path: '/', and no domain.
type SeedpodsCookieSameSite ↗
Supported SameSite attribute values for cookie definitions.
export type SeedpodsCookieSameSite = (typeof SEEDPODS_COOKIE_SAME_SITE_VALUES)[number]type SeedpodsCookiesReducer ↗
Reducer used to combine the current and next value for one cookie key.
export type SeedpodsCookiesReducer<SeedpodsValue> = (
previous?: SeedpodsValue,
next?: SeedpodsValue,
) => SeedpodsValue | undefined| Parameter | Description |
|---|---|
SeedpodsValue |
Cookie value type. |
type SeedpodsCookiesReducers ↗
Reducer map accepted by useCookies.
export type SeedpodsCookiesReducers<SeedpodsJarType extends SeedpodsJarInterface> = {
[SeedpodsKey in SeedpodsJarKeys<SeedpodsJarType>]?:
| SeedpodsCookiesReducer<SeedpodsJarCookieValue<SeedpodsJarType, SeedpodsKey>>
| undefined
}| Parameter | Description |
|---|---|
SeedpodsJarType |
Jar type that defines the available cookie keys and value types. |
type SeedpodsCookieType ↗
Supported cookie protection modes.
export type SeedpodsCookieType = (typeof SEEDPODS_COOKIE_TYPES)[number]'aes-gcm' encrypts and authenticates the cookie value. 'hmac' signs the value without encrypting it.
type SeedpodsErrorCause ↗
Machine-readable error detail carried by SeedpodsError.
export type SeedpodsErrorCause<T extends SeedpodsErrorType = SeedpodsErrorType> = T
extends SeedpodsErrorType ? {
type: T;
} & SeedpodsErrorMetadata[T] : never;| Parameter | Description |
|---|---|
T |
Error cause type to project. |
type SeedpodsErrorType ↗
Supported SeedpodsError cause types.
export type SeedpodsErrorType = (typeof SEEDPODS_ERROR_TYPES)[number]type SeedpodsParsedCookieHeader ↗
Parsed Cookie header value accepted by useCookies.
export type SeedpodsParsedCookieHeader = ReadonlyMap<string, readonly string[]>This is the return shape of parseCookieHeader. Each map key is a cookie name, and each value preserves all received values for that name in encounter order.