Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Clean up & inspect zombie sessions in Express: prunes cookie-only sessions to keep Prisma/Redis stores lean.

License

Notifications You must be signed in to change notification settings

CodeCornTech/empty-session-reaper

Repository files navigation

@codecorn/empty-session-reaper

Tiny middleware to prune or debug empty sessions (cookie-only + your rules) and keep Prisma/Redis stores lean.
Agnostic core β€” no app-specific keys inside. Bring your own policy via options.

npm downloads stars issues CI coverage umbrella license


Features

  • 🧹 Prune sessions that are "empty" under your rules.
  • πŸ”Ž Debug with dry-run + logger; optional session mutation logger.
  • 🧩 Agnostic: pass allowlists, predicates, denylists β€” no hardcoded keys.
  • πŸ§ͺ Store-agnostic: Prisma/SQL, Redis, etc.
  • βš™οΈ Composable predicates (emptyObject, equals, oneOf, and, or, flashEmptyOrOneOf).
  • 🧰 Optional preset: cookieFlash() β€” beginner-friendly.
  • 🧯 Safe by design: no env access, tiny footprint.

Install

npm i @codecorn/empty-session-reaper

Import styles

// βœ… CommonJS (require)
const {
 wireEmptySessionReaper,
 predicates,
 buildAllowedKeys,
 wireSessionMutationLogger, // optional: logs added/removed keys
} = require("@codecorn/empty-session-reaper");
const { cookieFlash } = require("@codecorn/empty-session-reaper/presets");
// βœ… ESM / TypeScript
import {
 wireEmptySessionReaper,
 predicates as P,
 buildAllowedKeys,
 wireSessionMutationLogger, // optional
} from "@codecorn/empty-session-reaper";
import { cookieFlash } from "@codecorn/empty-session-reaper/presets";

Usage A β€” minimal "cookie + empty flash"

// Meaning of buildAllowedKeys(input, expandBase, base):
// - base: starting list (default: ['cookie'])
// - input: extra keys to allow (e.g., ['flash'])
// - expandBase:
// true => merge base + input (e.g., ['cookie'] + ['flash'] -> ['cookie','flash'])
// false => use input only (e.g., ['flash'])
wireEmptySessionReaper(app, {
 logger: (m, meta) => console.debug(m, meta),
 allowedKeys: buildAllowedKeys(["flash"], true, ["cookie"]),
 // ↑ allowlist = merge base ['cookie'] + ['flash'] β†’ ['cookie','flash']
 maxKeys: 2,
 keyPredicates: {
 // flash is harmless if it's an empty object {}
 flash: P.emptyObject,
 },
});

Usage B β€” advanced custom policy (full control)

const isLoginFlash = (flash: any) => {
 if (!flash || typeof flash !== "object") return false;
 const ks = Object.keys(flash);
 if (ks.length !== 1) return false;
 const arr = Array.isArray((flash as any)[ks[0]]) ? (flash as any)[ks[0]] : [];
 return arr.length === 1 && /^pleasesignin\.?$/i.test(String(arr[0] || ""));
};
wireEmptySessionReaper(app, {
 logger: (m, meta) => console.debug(m, meta),
 allowedKeys: ["cookie", "flash", "url", "flag"],
 maxKeys: 4,
 disallowedKeyPatterns: [/^csrf/i, /^token/i, /^user/i],
 keyPredicates: {
 flash: (v) => P.emptyObject(v) || isLoginFlash(v),
 url: (v) => ["/", "/login", "/signin"].includes(String(v || "")),
 flag: P.oneOf([false, "auto"]),
 },
 isSessionPrunable: (s) => {
 const url = String((s as any).url || "");
 return !(/\.(env|git)\b/i.test(url) || /\/upload\/\./i.test(url));
 },
});

Usage C β€” optional preset cookieFlash

const preset = cookieFlash({
 // flashKey: 'flash',
 // flashField: 'error',
 // loginMessages: [/^please sign in\.?$/i, /^access denied$/i],
 // extraAllowedKeys: ['url'],
 // maxKeys: 3,
 // disallowedKeyPatterns: [/^csrf/i, /^token/i],
 // extraPredicates: { url: (v) => ['/', '/login'].includes(String(v || '')) },
 // finalCheck: (s) => !/\.env\b/i.test(String((s as any).url || '')),
});
wireEmptySessionReaper(app, { logger: console.debug, ...preset });

Bonus: Session mutation logger (discover origins)

// Place AFTER session(...) and BEFORE the reaper:
wireSessionMutationLogger(app, {
 logger: (label, meta) => console.debug(label, meta),
 includeValues: false, // true to also log shallow values (use redact to mask)
 redact: (k, v) => (/(token|secret|pass)/i.test(k) ? "[redacted]" : v),
 label: "session mutation",
});

Logs { path, added: [...], removed: [...] } on each response where the session keys changed.


API (core)

createEmptySessionReaper(opts) -> (req, res, next) => void
 Create the middleware with your pruning policy.
wireEmptySessionReaper(app, opts) -> middleware
 Mounts the middleware on the app and returns it.
buildAllowedKeys(input?: string[], expandBase?: boolean, base?: string[]) -> string[]
 Helper to compose allowlists.
 - base: starting list (default: ["cookie"])
 - input: extra allowed keys (e.g., ["flash"])
 - expandBase:
 true -> merge base + input
 false -> use input only
predicates:
 - emptyObject(v)
 - equals(x)
 - oneOf([a,b,c])
 - and(p1,p2,...)
 - or(p1,p2,...)
 - flashEmptyOrOneOf(field='error', messages=[/^please sign in\.?$/i])
createSessionMutationLogger(opts) -> middleware
wireSessionMutationLogger(app, opts) -> middleware
lookUpSessMutation(app, opts) -> alias of wireSessionMutationLogger

πŸ“ License

MIT Β© CodeCornTM

Distributed under the MIT license.

Credits

  • CousΓ¬n (co-author & review) β€” PYTORCHIA FOR LIFE
  • Federico Girolami (CodeCorn) β€” Maintainer

πŸ‘€ Maintainer

πŸ‘¨β€πŸ’» Federico Girolami

Full Stack Developer | System Integrator | Digital Solution Architect πŸš€

πŸ“« Get in Touch

🌐 Website: codecorn.it *(Under Construction)*

πŸ“§ Email: f.girolami@codecorn.it

πŸ™ GitHub: github.com/fgirolami29


🀝 Contribute

Pull requests are welcome. For major changes, please open an issue first to discuss what you’d like to change.

Powered by CodeCornTM πŸš€


About

Clean up & inspect zombie sessions in Express: prunes cookie-only sessions to keep Prisma/Redis stores lean.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

AltStyle γ«γ‚ˆγ£γ¦ε€‰ζ›γ•γ‚ŒγŸγƒšγƒΌγ‚Έ (->γ‚ͺγƒͺγ‚ΈγƒŠγƒ«) /