-first commit

This commit is contained in:
2025-11-11 12:36:06 +07:00
commit b99c214434
5683 changed files with 713336 additions and 0 deletions

30
node_modules/connect-redis/.github/release-drafter.yml generated vendored Normal file
View File

@@ -0,0 +1,30 @@
name-template: "v$RESOLVED_VERSION"
tag-template: "v$RESOLVED_VERSION"
template: |
$CHANGES
category-template: "#### $TITLE"
change-template: "* #$NUMBER - $TITLE (@$AUTHOR)"
categories:
- title: "Breaking changes"
label: "breaking"
- title: "Enhancements"
label: "enhancement"
- title: "Bug fixes"
label: "bug"
- title: "Maintenance"
label: "chore"
version-resolver:
major:
labels:
- "breaking"
minor:
labels:
- "enhancement"
patch:
labels:
- "bug"
- "chore"
exclude-labels:
- "skip-changelog"

22
node_modules/connect-redis/.github/workflows/build.yml generated vendored Normal file
View File

@@ -0,0 +1,22 @@
name: build
on:
push:
branches:
- master
pull_request:
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node: [18, 20, 22, 24]
name: Node v${{ matrix.node }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
- run: sudo apt-get install -y redis-server
- run: npm install
- run: npm run lint
- run: npm test

View File

@@ -0,0 +1,13 @@
name: release-draft
on:
workflow_dispatch:
push:
branches:
- master
jobs:
update_release_draft:
runs-on: ubuntu-latest
steps:
- uses: release-drafter/release-drafter@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

17
node_modules/connect-redis/.github/workflows/stale.yml generated vendored Normal file
View File

@@ -0,0 +1,17 @@
name: stale-issues-prs
on:
workflow_dispatch:
schedule:
- cron: "0 0 * * *"
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v3
with:
stale-issue-message: "This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days."
stale-pr-message: "This PR is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days."
close-issue-message: "This issue was closed because it has been stalled for 5 days with no activity."
days-before-stale: 30
days-before-close: 5
stale-issue-label: stale

40
node_modules/connect-redis/biome.json generated vendored Normal file
View File

@@ -0,0 +1,40 @@
{
"files": {
"ignore": ["dist", "pnpm-*.yaml", "coverage"]
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"bracketSpacing": false
},
"linter": {
"enabled": true,
"rules": {
"a11y": {
"all": false
},
"style": {
"useConst": "off",
"useTemplate": "off",
"noParameterAssign": "off",
"useSingleVarDeclarator": "off"
},
"correctness": {
"noUnusedImports": "error"
},
"suspicious": {
"noExplicitAny": "off",
"noArrayIndexKey": "off",
"noImplicitAnyLet": "off"
}
}
},
"javascript": {
"formatter": {
"semicolons": "asNeeded"
}
},
"organizeImports": {
"enabled": true
}
}

164
node_modules/connect-redis/dist/connect-redis.cjs generated vendored Normal file
View File

@@ -0,0 +1,164 @@
"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const expressSession = require("express-session");
function optionalCb(err, data, cb) {
if (cb) return cb(err, data);
if (err) throw err;
return data;
}
class RedisStore extends expressSession.Store {
client;
prefix;
scanCount;
serializer;
ttl;
disableTTL;
disableTouch;
constructor(opts) {
super();
this.prefix = opts.prefix == null ? "sess:" : opts.prefix;
this.scanCount = opts.scanCount || 100;
this.serializer = opts.serializer || JSON;
this.ttl = opts.ttl || 86400;
this.disableTTL = opts.disableTTL || false;
this.disableTouch = opts.disableTouch || false;
this.client = opts.client;
}
async get(sid, cb) {
let key = this.prefix + sid;
try {
let data = await this.client.get(key);
if (!data) return optionalCb(null, null, cb);
return optionalCb(null, await this.serializer.parse(data), cb);
} catch (err) {
return optionalCb(err, null, cb);
}
}
async set(sid, sess, cb) {
let key = this.prefix + sid;
let ttl = this.getTTL(sess);
try {
if (ttl > 0) {
let val = this.serializer.stringify(sess);
if (this.disableTTL) await this.client.set(key, val);
else
await this.client.set(key, val, {
expiration: { type: "EX", value: ttl }
});
return optionalCb(null, null, cb);
}
return this.destroy(sid, cb);
} catch (err) {
return optionalCb(err, null, cb);
}
}
async touch(sid, sess, cb) {
let key = this.prefix + sid;
if (this.disableTouch || this.disableTTL) return optionalCb(null, null, cb);
try {
await this.client.expire(key, this.getTTL(sess));
return optionalCb(null, null, cb);
} catch (err) {
return optionalCb(err, null, cb);
}
}
async destroy(sid, cb) {
let key = this.prefix + sid;
try {
await this.client.del([key]);
return optionalCb(null, null, cb);
} catch (err) {
return optionalCb(err, null, cb);
}
}
async clear(cb) {
try {
let keys = await this.getAllKeys();
if (!keys.length) return optionalCb(null, null, cb);
await this.client.del(keys);
return optionalCb(null, null, cb);
} catch (err) {
return optionalCb(err, null, cb);
}
}
async length(cb) {
try {
let keys = await this.getAllKeys();
return optionalCb(null, keys.length, cb);
} catch (err) {
return optionalCb(err, null, cb);
}
}
async ids(cb) {
let len = this.prefix.length;
try {
let keys = await this.getAllKeys();
return optionalCb(
null,
keys.map((k) => k.substring(len)),
cb
);
} catch (err) {
return optionalCb(err, null, cb);
}
}
async all(cb) {
let len = this.prefix.length;
try {
let keys = await this.getAllKeys();
if (keys.length === 0) return optionalCb(null, [], cb);
let data = await this.client.mGet(keys);
let results = data.reduce((acc, raw, idx) => {
if (!raw) return acc;
let sess = this.serializer.parse(raw);
sess.id = keys[idx].substring(len);
acc.push(sess);
return acc;
}, []);
return optionalCb(null, results, cb);
} catch (err) {
return optionalCb(err, null, cb);
}
}
getTTL(sess) {
if (typeof this.ttl === "function") {
return this.ttl(sess);
}
let ttl;
if (sess?.cookie?.expires) {
let ms = Number(new Date(sess.cookie.expires)) - Date.now();
ttl = Math.ceil(ms / 1e3);
} else {
ttl = this.ttl;
}
return ttl;
}
async getAllKeys() {
let pattern = this.prefix + "*";
let set = /* @__PURE__ */ new Set();
for await (let keys of this.scanIterator(pattern, this.scanCount)) {
for (let key of keys) {
set.add(key);
}
}
return set.size > 0 ? Array.from(set) : [];
}
scanIterator(match, count) {
let client = this.client;
if (!("masters" in client)) {
return client.scanIterator({ MATCH: match, COUNT: count });
}
return async function* () {
for (let master of client.masters) {
let c = await client.nodeClient(master);
for await (let keys of c.scanIterator({
COUNT: count,
MATCH: match
})) {
yield keys;
}
}
}();
}
}
exports.RedisStore = RedisStore;

45
node_modules/connect-redis/dist/connect-redis.d.cts generated vendored Normal file
View File

@@ -0,0 +1,45 @@
import { RedisClientType } from 'redis';
import { RedisClusterType } from 'redis';
import { SessionData } from 'express-session';
import { Store } from 'express-session';
declare type Callback = (_err?: unknown, _data?: any) => any;
export declare class RedisStore extends Store {
client: RedisClientType | RedisClusterType;
prefix: string;
scanCount: number;
serializer: Serializer;
ttl: number | ((sess: SessionData) => number);
disableTTL: boolean;
disableTouch: boolean;
constructor(opts: RedisStoreOptions);
get(sid: string, cb?: Callback): Promise<any>;
set(sid: string, sess: SessionData, cb?: Callback): Promise<any>;
touch(sid: string, sess: SessionData, cb?: Callback): Promise<any>;
destroy(sid: string, cb?: Callback): Promise<any>;
clear(cb?: Callback): Promise<any>;
length(cb?: Callback): Promise<any>;
ids(cb?: Callback): Promise<any>;
all(cb?: Callback): Promise<any>;
private getTTL;
private getAllKeys;
private scanIterator;
}
declare interface RedisStoreOptions {
client: any;
prefix?: string;
scanCount?: number;
serializer?: Serializer;
ttl?: number | ((sess: SessionData) => number);
disableTTL?: boolean;
disableTouch?: boolean;
}
declare interface Serializer {
parse(s: string): SessionData | Promise<SessionData>;
stringify(s: SessionData): string;
}
export { }

45
node_modules/connect-redis/dist/connect-redis.d.ts generated vendored Normal file
View File

@@ -0,0 +1,45 @@
import { RedisClientType } from 'redis';
import { RedisClusterType } from 'redis';
import { SessionData } from 'express-session';
import { Store } from 'express-session';
declare type Callback = (_err?: unknown, _data?: any) => any;
export declare class RedisStore extends Store {
client: RedisClientType | RedisClusterType;
prefix: string;
scanCount: number;
serializer: Serializer;
ttl: number | ((sess: SessionData) => number);
disableTTL: boolean;
disableTouch: boolean;
constructor(opts: RedisStoreOptions);
get(sid: string, cb?: Callback): Promise<any>;
set(sid: string, sess: SessionData, cb?: Callback): Promise<any>;
touch(sid: string, sess: SessionData, cb?: Callback): Promise<any>;
destroy(sid: string, cb?: Callback): Promise<any>;
clear(cb?: Callback): Promise<any>;
length(cb?: Callback): Promise<any>;
ids(cb?: Callback): Promise<any>;
all(cb?: Callback): Promise<any>;
private getTTL;
private getAllKeys;
private scanIterator;
}
declare interface RedisStoreOptions {
client: any;
prefix?: string;
scanCount?: number;
serializer?: Serializer;
ttl?: number | ((sess: SessionData) => number);
disableTTL?: boolean;
disableTouch?: boolean;
}
declare interface Serializer {
parse(s: string): SessionData | Promise<SessionData>;
stringify(s: SessionData): string;
}
export { }

164
node_modules/connect-redis/dist/connect-redis.js generated vendored Normal file
View File

@@ -0,0 +1,164 @@
import { Store } from "express-session";
function optionalCb(err, data, cb) {
if (cb) return cb(err, data);
if (err) throw err;
return data;
}
class RedisStore extends Store {
client;
prefix;
scanCount;
serializer;
ttl;
disableTTL;
disableTouch;
constructor(opts) {
super();
this.prefix = opts.prefix == null ? "sess:" : opts.prefix;
this.scanCount = opts.scanCount || 100;
this.serializer = opts.serializer || JSON;
this.ttl = opts.ttl || 86400;
this.disableTTL = opts.disableTTL || false;
this.disableTouch = opts.disableTouch || false;
this.client = opts.client;
}
async get(sid, cb) {
let key = this.prefix + sid;
try {
let data = await this.client.get(key);
if (!data) return optionalCb(null, null, cb);
return optionalCb(null, await this.serializer.parse(data), cb);
} catch (err) {
return optionalCb(err, null, cb);
}
}
async set(sid, sess, cb) {
let key = this.prefix + sid;
let ttl = this.getTTL(sess);
try {
if (ttl > 0) {
let val = this.serializer.stringify(sess);
if (this.disableTTL) await this.client.set(key, val);
else
await this.client.set(key, val, {
expiration: { type: "EX", value: ttl }
});
return optionalCb(null, null, cb);
}
return this.destroy(sid, cb);
} catch (err) {
return optionalCb(err, null, cb);
}
}
async touch(sid, sess, cb) {
let key = this.prefix + sid;
if (this.disableTouch || this.disableTTL) return optionalCb(null, null, cb);
try {
await this.client.expire(key, this.getTTL(sess));
return optionalCb(null, null, cb);
} catch (err) {
return optionalCb(err, null, cb);
}
}
async destroy(sid, cb) {
let key = this.prefix + sid;
try {
await this.client.del([key]);
return optionalCb(null, null, cb);
} catch (err) {
return optionalCb(err, null, cb);
}
}
async clear(cb) {
try {
let keys = await this.getAllKeys();
if (!keys.length) return optionalCb(null, null, cb);
await this.client.del(keys);
return optionalCb(null, null, cb);
} catch (err) {
return optionalCb(err, null, cb);
}
}
async length(cb) {
try {
let keys = await this.getAllKeys();
return optionalCb(null, keys.length, cb);
} catch (err) {
return optionalCb(err, null, cb);
}
}
async ids(cb) {
let len = this.prefix.length;
try {
let keys = await this.getAllKeys();
return optionalCb(
null,
keys.map((k) => k.substring(len)),
cb
);
} catch (err) {
return optionalCb(err, null, cb);
}
}
async all(cb) {
let len = this.prefix.length;
try {
let keys = await this.getAllKeys();
if (keys.length === 0) return optionalCb(null, [], cb);
let data = await this.client.mGet(keys);
let results = data.reduce((acc, raw, idx) => {
if (!raw) return acc;
let sess = this.serializer.parse(raw);
sess.id = keys[idx].substring(len);
acc.push(sess);
return acc;
}, []);
return optionalCb(null, results, cb);
} catch (err) {
return optionalCb(err, null, cb);
}
}
getTTL(sess) {
if (typeof this.ttl === "function") {
return this.ttl(sess);
}
let ttl;
if (sess?.cookie?.expires) {
let ms = Number(new Date(sess.cookie.expires)) - Date.now();
ttl = Math.ceil(ms / 1e3);
} else {
ttl = this.ttl;
}
return ttl;
}
async getAllKeys() {
let pattern = this.prefix + "*";
let set = /* @__PURE__ */ new Set();
for await (let keys of this.scanIterator(pattern, this.scanCount)) {
for (let key of keys) {
set.add(key);
}
}
return set.size > 0 ? Array.from(set) : [];
}
scanIterator(match, count) {
let client = this.client;
if (!("masters" in client)) {
return client.scanIterator({ MATCH: match, COUNT: count });
}
return async function* () {
for (let master of client.masters) {
let c = await client.nodeClient(master);
for await (let keys of c.scanIterator({
COUNT: count,
MATCH: match
})) {
yield keys;
}
}
}();
}
}
export {
RedisStore
};

197
node_modules/connect-redis/index.ts generated vendored Normal file
View File

@@ -0,0 +1,197 @@
import {type SessionData, Store} from "express-session"
import type {RedisClientType, RedisClusterType} from "redis"
type Callback = (_err?: unknown, _data?: any) => any
function optionalCb(err: unknown, data: unknown, cb?: Callback) {
if (cb) return cb(err, data)
if (err) throw err
return data
}
interface Serializer {
parse(s: string): SessionData | Promise<SessionData>
stringify(s: SessionData): string
}
interface RedisStoreOptions {
client: any
prefix?: string
scanCount?: number
serializer?: Serializer
ttl?: number | ((sess: SessionData) => number)
disableTTL?: boolean
disableTouch?: boolean
}
export class RedisStore extends Store {
client: RedisClientType | RedisClusterType
prefix: string
scanCount: number
serializer: Serializer
ttl: number | ((sess: SessionData) => number)
disableTTL: boolean
disableTouch: boolean
constructor(opts: RedisStoreOptions) {
super()
this.prefix = opts.prefix == null ? "sess:" : opts.prefix
this.scanCount = opts.scanCount || 100
this.serializer = opts.serializer || JSON
this.ttl = opts.ttl || 86400 // One day in seconds.
this.disableTTL = opts.disableTTL || false
this.disableTouch = opts.disableTouch || false
this.client = opts.client
}
async get(sid: string, cb?: Callback) {
let key = this.prefix + sid
try {
let data = await this.client.get(key)
if (!data) return optionalCb(null, null, cb)
return optionalCb(null, await this.serializer.parse(data), cb)
} catch (err) {
return optionalCb(err, null, cb)
}
}
async set(sid: string, sess: SessionData, cb?: Callback) {
let key = this.prefix + sid
let ttl = this.getTTL(sess)
try {
if (ttl > 0) {
let val = this.serializer.stringify(sess)
if (this.disableTTL) await this.client.set(key, val)
else
await this.client.set(key, val, {
expiration: {type: "EX", value: ttl},
})
return optionalCb(null, null, cb)
}
return this.destroy(sid, cb)
} catch (err) {
return optionalCb(err, null, cb)
}
}
async touch(sid: string, sess: SessionData, cb?: Callback) {
let key = this.prefix + sid
if (this.disableTouch || this.disableTTL) return optionalCb(null, null, cb)
try {
await this.client.expire(key, this.getTTL(sess))
return optionalCb(null, null, cb)
} catch (err) {
return optionalCb(err, null, cb)
}
}
async destroy(sid: string, cb?: Callback) {
let key = this.prefix + sid
try {
await this.client.del([key])
return optionalCb(null, null, cb)
} catch (err) {
return optionalCb(err, null, cb)
}
}
async clear(cb?: Callback) {
try {
let keys = await this.getAllKeys()
if (!keys.length) return optionalCb(null, null, cb)
await this.client.del(keys)
return optionalCb(null, null, cb)
} catch (err) {
return optionalCb(err, null, cb)
}
}
async length(cb?: Callback) {
try {
let keys = await this.getAllKeys()
return optionalCb(null, keys.length, cb)
} catch (err) {
return optionalCb(err, null, cb)
}
}
async ids(cb?: Callback) {
let len = this.prefix.length
try {
let keys = await this.getAllKeys()
return optionalCb(
null,
keys.map((k) => k.substring(len)),
cb,
)
} catch (err) {
return optionalCb(err, null, cb)
}
}
async all(cb?: Callback) {
let len = this.prefix.length
try {
let keys = await this.getAllKeys()
if (keys.length === 0) return optionalCb(null, [], cb)
let data = await this.client.mGet(keys)
let results = data.reduce((acc, raw, idx) => {
if (!raw) return acc
let sess = this.serializer.parse(raw) as any
sess.id = keys[idx].substring(len)
acc.push(sess)
return acc
}, [] as SessionData[])
return optionalCb(null, results, cb)
} catch (err) {
return optionalCb(err, null, cb)
}
}
private getTTL(sess: SessionData) {
if (typeof this.ttl === "function") {
return this.ttl(sess)
}
let ttl
if (sess?.cookie?.expires) {
let ms = Number(new Date(sess.cookie.expires)) - Date.now()
ttl = Math.ceil(ms / 1000)
} else {
ttl = this.ttl
}
return ttl
}
private async getAllKeys() {
let pattern = this.prefix + "*"
let set = new Set<string>()
for await (let keys of this.scanIterator(pattern, this.scanCount)) {
for (let key of keys) {
set.add(key)
}
}
return set.size > 0 ? Array.from(set) : []
}
private scanIterator(match: string, count: number) {
let client = this.client
if (!("masters" in client)) {
return client.scanIterator({MATCH: match, COUNT: count})
}
return (async function* () {
for (let master of client.masters) {
let c = await client.nodeClient(master)
for await (let keys of c.scanIterator({
COUNT: count,
MATCH: match,
})) {
yield keys
}
}
})()
}
}

113
node_modules/connect-redis/index_test.ts generated vendored Normal file
View File

@@ -0,0 +1,113 @@
import {Cookie} from "express-session"
import {createClient} from "redis"
import {expect, test} from "vitest"
import {RedisStore} from "./"
import * as redisSrv from "./testdata/server"
test("setup", async () => {
await redisSrv.connect()
})
test("defaults", async () => {
let client = createClient({url: `redis://localhost:${redisSrv.port}`})
await client.connect()
let store = new RedisStore({client})
expect(store.client).toBeDefined()
expect(store.prefix).toBe("sess:")
expect(store.ttl).toBe(86400) // defaults to one day
expect(store.scanCount).toBe(100)
expect(store.serializer).toBe(JSON)
expect(store.disableTouch).toBe(false)
expect(store.disableTTL).toBe(false)
client.destroy()
})
test("redis", async () => {
let client = createClient({url: `redis://localhost:${redisSrv.port}`})
await client.connect()
let store = new RedisStore({client})
await lifecycleTest(store, client)
client.destroy()
})
test("teardown", redisSrv.disconnect)
async function lifecycleTest(store: RedisStore, client: any): Promise<void> {
let res = await store.clear()
let sess = {foo: "bar", cookie: {originalMaxAge: null}}
await store.set("123", sess)
res = await store.get("123")
expect(res).toEqual(sess)
let ttl = await client.ttl("sess:123")
expect(ttl).toBeGreaterThanOrEqual(86399)
ttl = 60
let expires = new Date(Date.now() + ttl * 1000)
await store.set("456", {cookie: {originalMaxAge: null, expires}})
ttl = await client.ttl("sess:456")
expect(ttl).toBeLessThanOrEqual(60)
ttl = 90
let expires2 = new Date(Date.now() + ttl * 1000)
await store.touch("456", {cookie: {originalMaxAge: null, expires: expires2}})
ttl = await client.ttl("sess:456")
expect(ttl).toBeGreaterThan(60)
res = await store.length()
expect(res).toBe(2) // stored two keys length
res = await store.ids()
res.sort()
expect(res).toEqual(["123", "456"])
res = await store.all()
res.sort((a: any, b: any) => (a.id > b.id ? 1 : -1))
expect(res).toEqual([
{id: "123", foo: "bar", cookie: {originalMaxAge: null}},
{id: "456", cookie: {originalMaxAge: null, expires: expires.toISOString()}},
])
await store.destroy("456")
res = await store.length()
expect(res).toBe(1) // one key remains
res = await store.clear()
res = await store.length()
expect(res).toBe(0) // no keys remain
let count = 1000
await load(store, count)
res = await store.length()
expect(res).toBe(count)
await store.clear()
res = await store.length()
expect(res).toBe(0)
expires = new Date(Date.now() + ttl * 1000) // expires in the future
res = await store.set("789", {cookie: {originalMaxAge: null, expires}})
res = await store.length()
expect(res).toBe(1)
expires = new Date(Date.now() - ttl * 1000) // expires in the past
await store.set("789", {cookie: {originalMaxAge: null, expires}})
res = await store.length()
expect(res).toBe(0) // no key remains and that includes session 789
}
async function load(store: RedisStore, count: number) {
let cookie = new Cookie()
for (let sid = 0; sid < count; sid++) {
cookie.expires = new Date(Date.now() + 1000)
await store.set("s" + sid, {cookie})
}
}

21
node_modules/connect-redis/license generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2010-2025 TJ Holowaychuk
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

64
node_modules/connect-redis/package.json generated vendored Normal file
View File

@@ -0,0 +1,64 @@
{
"name": "connect-redis",
"description": "Redis session store for Connect",
"version": "9.0.0",
"author": "TJ Holowaychuk <tj@vision-media.ca>",
"contributors": [
"Marc Harter <wavded@gmail.com>"
],
"license": "MIT",
"type": "module",
"main": "./dist/connect-redis.cjs",
"module": "./dist/connect-redis.js",
"types": "./dist/connect-redis.d.ts",
"exports": {
".": {
"import": {
"types": "./dist/connect-redis.d.ts",
"default": "./dist/connect-redis.js"
},
"require": {
"types": "./dist/connect-redis.d.cts",
"default": "./dist/connect-redis.cjs"
}
}
},
"repository": {
"type": "git",
"url": "git+ssh://git@github.com/tj/connect-redis.git"
},
"devDependencies": {
"@biomejs/biome": "^1.9.4",
"@types/express-session": "^1.18.2",
"@types/node": "^22.15.31",
"@vitest/coverage-v8": "^3.2.3",
"express-session": "^1.18.1",
"ts-node": "^10.9.2",
"typescript": "^5.8.3",
"vite": "^6.3.5",
"vite-plugin-dts": "^4.5.4",
"vitest": "^3.2.3"
},
"peerDependencies": {
"redis": ">=5",
"express-session": ">=1"
},
"engines": {
"node": ">=18"
},
"bugs": {
"url": "https://github.com/tj/connect-redis/issues"
},
"keywords": [
"connect",
"redis",
"session",
"express"
],
"scripts": {
"build": "vite build",
"test": "vitest run --coverage",
"lint": "tsc --noemit && biome check .",
"fix": "biome check --write ."
}
}

3
node_modules/connect-redis/pnpm-workspace.yaml generated vendored Normal file
View File

@@ -0,0 +1,3 @@
onlyBuiltDependencies:
- '@biomejs/biome'
- esbuild

111
node_modules/connect-redis/readme.md generated vendored Normal file
View File

@@ -0,0 +1,111 @@
[![build](https://github.com/tj/connect-redis/actions/workflows/build.yml/badge.svg)](https://github.com/tj/connect-redis/actions/workflows/build.yml) [![npm](https://img.shields.io/npm/v/connect-redis.svg)](https://npmjs.com/package/connect-redis) ![Downloads](https://img.shields.io/npm/dm/connect-redis.svg)
**connect-redis** provides Redis session storage for Express.
## Installation
**connect-redis** requires `express-session` and [`redis`][1]:
```sh
npm install redis connect-redis express-session
```
## API
Full setup:
```js
import {RedisStore} from "connect-redis"
import session from "express-session"
import {createClient} from "redis"
// Initialize client.
let redisClient = createClient()
redisClient.connect().catch(console.error)
// Initialize store.
let redisStore = new RedisStore({
client: redisClient,
prefix: "myapp:",
})
// Initialize session storage.
app.use(
session({
store: redisStore,
resave: false, // required: force lightweight session keep alive (touch)
saveUninitialized: false, // recommended: only save session when data exists
secret: "keyboard cat",
}),
)
```
### RedisStore(options)
#### Options
##### client
An instance of [`redis`][1]
##### prefix
Key prefix in Redis (default: `sess:`).
**Note**: This prefix appends to whatever prefix you may have set on the `client` itself.
**Note**: You may need unique prefixes for different applications sharing the same Redis instance. This limits bulk commands exposed in `express-session` (like `length`, `all`, `keys`, and `clear`) to a single application's data.
##### ttl
If the session cookie has a `expires` date, `connect-redis` will use it as the TTL.
Otherwise, it will expire the session using the `ttl` option (default: `86400` seconds or one day).
```ts
interface RedisStoreOptions {
...
ttl?: number | {(sess: SessionData): number}
}
```
`ttl` also has external callback support. You can use it for dynamic TTL generation. It has access to `session` data.
**Note**: The TTL is reset every time a user interacts with the server. You can disable this behavior in _some_ instances by using `disableTouch`.
**Note**: `express-session` does not update `expires` until the end of the request life cycle. _Calling `session.save()` manually beforehand will have the previous value_.
##### disableTouch
Disables resetting the TTL when using `touch` (default: `false`)
The `express-session` package uses `touch` to signal to the store that the user has interacted with the session but hasn't changed anything in its data. Typically, this helps keep the users session alive if session changes are infrequent but you may want to disable it to cut down the extra calls or to prevent users from keeping sessions open too long. Also consider enabling if you store a lot of data on the session.
Ref: <https://github.com/expressjs/session#storetouchsid-session-callback>
##### disableTTL
Disables key expiration completely (default: `false`)
This option disables key expiration requiring the user to manually manage key cleanup outside of `connect-redis`. Only use if you know what you are doing and have an exceptional case where you need to manage your own expiration in Redis.
**Note**: This has no effect on `express-session` setting cookie expiration.
##### serializer
Provide a custom encoder/decoder to use when storing and retrieving session data from Redis (default: `JSON.parse` and `JSON.stringify`).
Optionally `parse` method can be async if need be.
```ts
interface Serializer {
parse(string): object | Promise<object>
stringify(object): string
}
```
##### scanCount
Value used for _count_ parameter in [Redis `SCAN` command](https://redis.io/commands/scan#the-count-option). Used for `ids()` and `all()` methods (default: `100`).
[1]: https://github.com/NodeRedis/node-redis

16
node_modules/connect-redis/tsconfig.json generated vendored Normal file
View File

@@ -0,0 +1,16 @@
{
"compilerOptions": {
"target": "es2022",
"module": "esnext",
"strict": true,
"isolatedModules": true,
"skipLibCheck": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"esModuleInterop": true,
"resolveJsonModule": true
},
"exclude": ["node_modules", "dist"]
}

37
node_modules/connect-redis/vite.config.ts generated vendored Normal file
View File

@@ -0,0 +1,37 @@
import {copyFileSync} from "node:fs"
import dts from "vite-plugin-dts"
import {defineConfig} from "vitest/config"
// https://vitest.dev/config/
export default defineConfig({
build: {
lib: {
entry: "index.ts",
name: "connect-redis",
formats: ["es", "cjs"],
},
emptyOutDir: true,
minify: false,
rollupOptions: {
external: ["express-session"],
treeshake: false,
},
target: "node18",
},
plugins: [
dts({
include: ["index.ts"],
rollupTypes: true,
insertTypesEntry: true,
afterBuild: () => {
copyFileSync("dist/connect-redis.d.ts", "dist/connect-redis.d.cts")
},
}),
],
test: {
include: ["**/*_test.[jt]s"],
coverage: {
reporter: ["text"],
},
},
})