type SessionRecord$1 = { sid: string; uid: string | null; claims: object; issuedAt: number; /** * Absolute TTL — after this the session is dead. */ expiresAt: number; /** * Refreshed on `touch`; drives the idle-TTL check. */ lastSeenAt: number; /** * Last fresh-auth timestamp (sudo mode). */ freshAt?: number | undefined; deviceLabel?: string | undefined; ip?: string | undefined; ua?: string | undefined; /** * Admin user ID if this is an impersonation. */ impersonatedBy?: string | undefined; isAnonymous: boolean; /** * `true` once `revoke` has been called. */ revoked: boolean; revokedReason?: string | undefined; }; type SessionStore$1 = { get: (sid: string) => Promise; /** * Insert-or-replace. Also updates the reverse `user → set(sid)` index. */ put: (record: SessionRecord$1) => Promise; /** * Merge-patch a session. Returns the updated record, or `null` if the * sid isn't present. */ update: (sid: string, patch: Partial) => Promise; /** * Mark a single session as revoked. Returns `true` if it was already * in the store (whether previously revoked or not), `false` if not * present. */ revoke: (sid: string, reason?: string) => Promise; /** * Revoke every session belonging to `uid`. Returns the count revoked. */ revokeAllForUser: (uid: string, reason?: string) => Promise; /** * Revoke every session for `uid` other than `keepSid`. Returns the * count revoked. */ revokeAllExcept: (uid: string, keepSid: string, reason?: string) => Promise; /** * Return every non-revoked, non-expired session for `uid`, newest first. */ listByUser: (uid: string) => Promise; /** * Number of non-revoked, non-expired sessions for `uid`. Cheaper than * materialising the array when you only need the count. */ countActive: (uid: string) => Promise; /** * Optional cleanup — memory store starts a sweep timer that * `_stop()` cancels. Called from tests; production doesn't need it. */ _stop?: (() => void) | undefined; }; /** * @typedef {import('./memory.js').SessionStore} SessionStore * @typedef {import('./memory.js').SessionRecord} SessionRecord */ /** * @typedef {object} RedisStoreOptions * @property {string} [keyPrefix='sess:'] * Prefix for session keys — final key is ``. Reverse * user index lives under `u:`; revocation tombstones * under `rev:`. * @property {boolean} [publishRevocations=false] * When true, revocations are published to the `events` * channel via `PUBLISH`. Other workers can subscribe to invalidate * their per-request caches. Requires a Redis client that supports * `.publish(channel, message)` (ioredis + node-redis both do). * @property {string} [channel] * Override for the pub/sub channel. Defaults to `events`. */ /** * Redis session store. Works with any client that exposes the SET / GET / * DEL / SADD / SREM / SMEMBERS / PUBLISH commands — verified against * `ioredis` and `node-redis@4+`. The store never uses Lua scripts, so * `@upstash/redis` (HTTP client) also works. MGET / PEXPIRE are used * when the client supports them and gracefully skipped otherwise. * * Layout: * `` — JSON blob of the session record * `rev:` — revocation tombstone (see below) * `u:` — SADD-set of sids belonging to the user * `events` — PUB/SUB channel for revocation events (optional) * * Every write sets an EXPIRE to the record's absolute TTL so revoked / * expired records naturally fall out of the DB — no sweep loop needed. * * **Why tombstones:** `update()` is a read-modify-write (GET → SET), so * a concurrent `revoke()` on another worker could be overwritten by an * in-flight update carrying the pre-revoke copy — silently un-revoking * the session. Revocations are therefore written to a separate * `rev:` key that no update path ever touches; `get()` and * `listByUser()` overlay it onto the record. A revocation can never be * lost to a lost-update race. (Session IDs are base64url, so they can * never collide with the `rev:` / `u:` sub-prefixes.) * * @param {any} client * @param {RedisStoreOptions} [options] * @returns {SessionStore & { channel: string }} */ declare function redisStore(client: any, options?: RedisStoreOptions): SessionStore & { channel: string; }; type SessionStore = SessionStore$1; type SessionRecord = SessionRecord$1; type RedisStoreOptions = { /** * Prefix for session keys — final key is ``. Reverse * user index lives under `u:`; revocation tombstones * under `rev:`. */ keyPrefix?: string | undefined; /** * When true, revocations are published to the `events` * channel via `PUBLISH`. Other workers can subscribe to invalidate * their per-request caches. Requires a Redis client that supports * `.publish(channel, message)` (ioredis + node-redis both do). */ publishRevocations?: boolean | undefined; /** * Override for the pub/sub channel. Defaults to `events`. */ channel?: string | undefined; }; export { redisStore }; export type { RedisStoreOptions, SessionRecord, SessionStore };