Skip to content

Commit 3ff5dfc

Browse files
committed
1.0.0
1 parent aadb423 commit 3ff5dfc

File tree

12 files changed

+1690
-2
lines changed

12 files changed

+1690
-2
lines changed

.github/workflows/test.yml

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
name: test
2+
3+
on:
4+
push:
5+
branches: [ main, master, develop ]
6+
pull_request:
7+
branches: [ '**' ]
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
build:
14+
name: build / node ${{ matrix.node }}
15+
runs-on: ubuntu-latest
16+
strategy:
17+
fail-fast: false
18+
matrix:
19+
node: [ 16.x, 18.x, 20.x, 22.x ]
20+
21+
steps:
22+
- name: Checkout
23+
uses: actions/checkout@v4
24+
25+
- name: Use Node ${{ matrix.node }}
26+
uses: actions/setup-node@v4
27+
with:
28+
node-version: ${{ matrix.node }}
29+
cache: npm
30+
31+
- name: Install deps (npm ci)
32+
run: npm ci
33+
34+
- name: Type-check
35+
run: npm run check
36+
37+
- name: Build
38+
run: npm run build
39+
40+
- name: Verify dist files exist
41+
run: |
42+
test -f dist/index.js
43+
test -f dist/index.d.ts
44+
test -f dist/presets.js
45+
test -f dist/presets.d.ts
46+
47+
- name: Smoke test (require compiled output)
48+
run: node -e "const m=require('./dist'); console.log('exports:', Object.keys(m))"
49+
50+
- name: Pack (dry-run)
51+
run: npm pack --dry-run
52+
53+
- name: Upload dist artifact
54+
uses: actions/upload-artifact@v4
55+
with:
56+
name: dist-node-${{ matrix.node }}
57+
path: |
58+
dist/**
59+
*.tgz
60+
if-no-files-found: error
61+
62+
audit:
63+
name: audit (high+)
64+
runs-on: ubuntu-latest
65+
continue-on-error: true # non blocca le PR se ci sono advisory transitive
66+
steps:
67+
- uses: actions/checkout@v4
68+
- uses: actions/setup-node@v4
69+
with:
70+
node-version: 20.x
71+
cache: npm
72+
- run: npm ci
73+
- run: npm audit --audit-level=high

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,9 @@ dist
134134
.yarn/build-state.yml
135135
.yarn/install-state.gz
136136
.pnp.*
137+
138+
node_modules/
139+
*.log
140+
.DS_Store
141+
_STASHED/
142+
_SQL/

.npmignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
src/
2+
tsconfig.json
3+
node_modules/
4+
*.log
5+
.DS_Store
6+
_STASHED/
7+
_SQL/
8+
.github/workflows/test.yml
9+
bs.sh

README.it.md

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
# 🇮🇹 README (IT) — **`README.it.md`**
2+
3+
---
4+
5+
# @codecorn/empty-session-reaper
6+
7+
Middleware leggero per **potare _o debuggare_ le sessioni vuote** (solo cookie + le tue regole) e mantenere snelli gli store Prisma/Redis.
8+
**Core agnostico** — nessuna chiave applicativa all’interno. Porta tu la policy via opzioni.
9+
10+
<p align="right">
11+
<a href="https://www.npmjs.com/package/@codecorn/empty-session-reaper">
12+
<img alt="npm" src="https://img.shields.io/npm/v/@codecorn/empty-session-reaper?logo=npm&label=npm">
13+
</a>
14+
<a href="https://www.npmjs.com/package/@codecorn/empty-session-reaper">
15+
<img alt="downloads" src="https://img.shields.io/npm/dt/@codecorn/empty-session-reaper?color=blue&label=downloads">
16+
</a>
17+
<a href="https://github.com/CodeCornTech/empty-session-reaper/stargazers">
18+
<img alt="stars" src="https://img.shields.io/github/stars/CodeCornTech/empty-session-reaper?style=social">
19+
</a>
20+
<a href="https://github.com/CodeCornTech/empty-session-reaper/issues">
21+
<img alt="issues" src="https://img.shields.io/github/issues/CodeCornTech/empty-session-reaper">
22+
</a>
23+
<a href="https://github.com/CodeCornTech/empty-session-reaper/actions">
24+
<img alt="CI" src="https://github.com/CodeCornTech/empty-session-reaper/actions/workflows/test.yml/badge.svg">
25+
</a>
26+
<a href="https://codecov.io/gh/CodeCornTech/empty-session-reaper">
27+
<img alt="coverage umbrella" src="https://img.shields.io/badge/copertura-ombrello-9cf?logo=umbrella&logoColor=white">
28+
</a>
29+
<a href="LICENSE">
30+
<img alt="license" src="https://img.shields.io/github/license/CodeCornTech/empty-session-reaper">
31+
</a>
32+
</p>
33+
34+
---
35+
36+
## Caratteristiche
37+
38+
- 🧹 **Pota** le sessioni “vuote” secondo **le tue** regole.
39+
- 🔎 **Debug** con dry-run + logger; **logger di mutazioni** opzionale.
40+
- 🧩 **Agnostico**: allowlist, predicate e denylist — nessuna chiave hardcoded.
41+
- 🧪 **Indipendente dallo store**: Prisma/SQL, Redis, ecc.
42+
- ⚙️ **Predicate componibili** (`emptyObject`, `equals`, `oneOf`, `and`, `or`, `flashEmptyOrOneOf`).
43+
- 🧰 **Preset opzionale**: `cookieFlash()` — avvio rapido.
44+
- 🧯 **Safe**: niente env, footprint minimo.
45+
46+
---
47+
48+
## Installazione
49+
50+
```bash
51+
npm i @codecorn/empty-session-reaper
52+
```
53+
54+
### Import
55+
56+
```js
57+
// ✅ CommonJS (require)
58+
const {
59+
wireEmptySessionReaper,
60+
predicates,
61+
buildAllowedKeys,
62+
wireSessionMutationLogger, // opzionale
63+
} = require("@codecorn/empty-session-reaper");
64+
const { cookieFlash } = require("@codecorn/empty-session-reaper/presets");
65+
```
66+
67+
```ts
68+
// ✅ ESM / TypeScript
69+
import {
70+
wireEmptySessionReaper,
71+
predicates as P,
72+
buildAllowedKeys,
73+
wireSessionMutationLogger, // opzionale
74+
} from "@codecorn/empty-session-reaper";
75+
import { cookieFlash } from "@codecorn/empty-session-reaper/presets";
76+
```
77+
78+
---
79+
80+
## Uso A — “cookie + flash vuoto” (base)
81+
82+
```ts
83+
// Significato di buildAllowedKeys(input, expandBase, base):
84+
// - base: lista iniziale (default: ['cookie'])
85+
// - input: chiavi extra consentite (es. ['flash'])
86+
// - expandBase:
87+
// true => unisce base + input (['cookie'] + ['flash'] -> ['cookie','flash'])
88+
// false => usa solo input (['flash'])
89+
wireEmptySessionReaper(app, {
90+
logger: (m, meta) => console.debug(m, meta),
91+
92+
allowedKeys: buildAllowedKeys(["flash"], true, ["cookie"]),
93+
maxKeys: 2,
94+
95+
keyPredicates: { flash: P.emptyObject }, // flash innocuo se {}
96+
});
97+
```
98+
99+
## Uso B — policy avanzata (massimo controllo)
100+
101+
```ts
102+
const isLoginFlash = (flash: any) => {
103+
if (!flash || typeof flash !== "object") return false;
104+
const ks = Object.keys(flash);
105+
if (ks.length !== 1) return false;
106+
const arr = Array.isArray((flash as any)[ks[0]]) ? (flash as any)[ks[0]] : [];
107+
return arr.length === 1 && /^please sign in\.?$/i.test(String(arr[0] || ""));
108+
};
109+
110+
wireEmptySessionReaper(app, {
111+
logger: (m, meta) => console.debug(m, meta),
112+
113+
allowedKeys: ["cookie", "flash", "url", "flag"],
114+
maxKeys: 4,
115+
116+
disallowedKeyPatterns: [/^csrf/i, /^token/i, /^user/i],
117+
118+
keyPredicates: {
119+
flash: (v) => P.emptyObject(v) || isLoginFlash(v),
120+
url: (v) => ["/", "/login", "/signin"].includes(String(v || "")),
121+
flag: P.oneOf([false, "auto"]),
122+
},
123+
124+
isSessionPrunable: (s) => {
125+
const url = String((s as any).url || "");
126+
return !(/\.(env|git)\b/i.test(url) || /\/upload\/\./i.test(url));
127+
},
128+
});
129+
```
130+
131+
## Uso C — preset opzionale `cookieFlash`
132+
133+
```ts
134+
// cookie + flash: flash è {} OPPURE un messaggio ammesso (default: "Please sign in.")
135+
const preset = cookieFlash({
136+
// flashKey: 'flash',
137+
// flashField: 'error',
138+
// loginMessages: [/^please sign in\.?$/i, /^access denied$/i],
139+
// extraAllowedKeys: ['url'],
140+
// maxKeys: 3,
141+
// disallowedKeyPatterns: [/^csrf/i, /^token/i],
142+
// extraPredicates: { url: (v) => ['/', '/login'].includes(String(v || '')) },
143+
// finalCheck: (s) => !/\.env\b/i.test(String((s as any).url || '')),
144+
});
145+
wireEmptySessionReaper(app, { logger: console.debug, ...preset });
146+
```
147+
148+
---
149+
150+
## Bonus: Logger di mutazioni (capire l’origine)
151+
152+
```ts
153+
// Subito dopo session(...) e prima del reaper:
154+
wireSessionMutationLogger(app, {
155+
logger: (label, meta) => console.debug(label, meta),
156+
includeValues: false, // true per loggare anche i valori (usa redact per mascherarli)
157+
redact: (k, v) => (/(token|secret|pass)/i.test(k) ? "[redacted]" : v),
158+
label: "mutazione sessione",
159+
});
160+
```
161+
162+
> Logga `{ path, added: [...], removed: [...] }` quando le chiavi di sessione cambiano.
163+
164+
---
165+
166+
## API (core)
167+
168+
```txt
169+
createEmptySessionReaper(opts) -> (req, res, next) => void
170+
wireEmptySessionReaper(app, opts) -> middleware
171+
buildAllowedKeys(input?: string[], expandBase?: boolean, base?: string[]) -> string[]
172+
- base: lista iniziale (default: ["cookie"])
173+
- input: chiavi extra ammesse (es. ["flash"])
174+
- expandBase: true = unisci; false = usa solo input
175+
176+
predicates:
177+
- emptyObject(v)
178+
- equals(x)
179+
- oneOf([a,b,c])
180+
- and(p1,p2,...)
181+
- or(p1,p2,...)
182+
- flashEmptyOrOneOf(field='error', messages=[/^please sign in\.?$/i])
183+
184+
createSessionMutationLogger(opts) -> middleware
185+
wireSessionMutationLogger(app, opts) -> middleware
186+
lookUpSessMutation(app, opts) -> alias di wireSessionMutationLogger
187+
```
188+
189+
---
190+
191+
## Crediti
192+
193+
- **Cugggì (co-author & review)**_PYTORCHIA FOR LIFE_
194+
- **Federico Girolami (CodeCorn)** — Maintainer
195+
196+
---
197+
198+
## 👤 Maintainer
199+
200+
<div style="display: flex; justify-content: space-between; align-items: center;">
201+
<div>
202+
<p><strong>👨‍💻 Federico Girolami</strong></p>
203+
<p><strong>Full Stack Developer</strong> | <strong>System Integrator</strong> | <strong>Digital Solution Architect</strong> 🚀</p>
204+
<p>📫 <strong>Get in Touch</strong></p>
205+
<p>🌐 <strong>Website</strong>: <a href="https://codecorn.it">codecorn.it</a> *(Under Construction)*</p>
206+
<p>📧 <strong>Email</strong>: <a href="mailto:f.girolami@codecorn.it">f.girolami@codecorn.it</a></p>
207+
<p>🐙 <strong>GitHub</strong>: <a href="https://github.com/fgirolami29">github.com/fgirolami29</a></p>
208+
</div>
209+
<div style="text-align: center;">
210+
<a href="https://www.codecorn.it">
211+
<img src="https://codecorn.it/wp-content/uploads/2025/05/CODECORN-trasp-qhite.png" alt="Code Corn Logo" width="250px" height="90px" style="margin-top:30px;margin-bottom:20px;"/>
212+
</a>
213+
<a href="https://github.com/fgirolami29">
214+
<img src="https://avatars.githubusercontent.com/u/68548715?s=200&v=4" alt="Federico Girolami Avatar" style="border-radius: 50%; width: 125px; height: 125px;border: 5px solid gold" />
215+
</a>
216+
</div>
217+
</div>
218+
219+
---
220+
221+
## 📝 License
222+
223+
MIT © [CodeCorn™](https://codecorn.it)
224+
225+
Distribuito sotto licenza [MIT](LICENSE).
226+
227+
---
228+
229+
### 🤝 Contribuisci
230+
231+
Pull request benvenute. Per grosse modifiche apri una issue prima di iniziare.
232+
233+
> Powered by CodeCorn™ 🚀

0 commit comments

Comments
 (0)