@umeshindu222/apisnap 1.1.3 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,28 +7,31 @@
7
7
 
8
8
  ---
9
9
 
10
- ## Why APISnap?
10
+ ## Why APISnap?
11
11
 
12
- **The Problem:** Every time you make a change to your Express.js backend, you have to manually open Postman, find every route, and click "Send" one by one. For a project with 20+ endpoints, this is slow, error-prone, and boring.
13
-
14
- **The Solution:** APISnap plugs directly into your Express app, **automatically discovers every route you've registered**, and then health-checks all of them in seconds with zero configuration.
12
+ Every time you change your Express backend, manually testing 20+ endpoints in Postman is slow and error-prone. APISnap **auto-discovers every route** and health-checks all of them in seconds with zero config.
15
13
 
16
14
  ---
17
15
 
18
- ## Features
16
+ ## Features
19
17
 
20
- - **Auto Route Discovery** — Scans your Express router stack, no manual config needed
21
- - **Auth Header Support** — Pass JWT tokens or API keys via `--header`
22
- - **Slow Route Detection** — Flags endpoints that exceed your response time threshold
23
- - **JSON Report Export** — Save results to a file for CI/CD pipelines or sharing
24
- - **Beautiful CLI Output** — Color-coded results with spinners and summary table
25
- - **Express v4 & v5** — Compatible with both versions
18
+ - 🔍 **Auto Route Discovery** — Scans your full Express router stack including sub-routers
19
+ - 🔐 **Full Auth Support** — JWT, API Keys, Cookies, multiple headers simultaneously
20
+ - 🔁 **Retry Logic** Auto-retry failed requests with exponential backoff
21
+ -**Slow Route Detection** — Flags endpoints exceeding your threshold
22
+ - 📊 **HTML Reports** Beautiful visual reports for sharing/archiving
23
+ - 💾 **JSON Export** — Structured output for CI/CD pipelines
24
+ - ⚙️ **Config File** — Persist options in `.apisnaprc.json`
25
+ - 🎯 **Method Filter** — Test only GET, POST, etc.
26
+ - 🧠 **Smart Params** — Auto-replaces `:id`, `:slug`, `:uuid` with safe defaults
27
+ - 🚨 **Auth Hints** — Tells you exactly how to fix 401/403 errors
28
+ - 🏗️ **CI/CD Ready** — Exit code 1 on failures for pipeline integration
26
29
 
27
30
  ---
28
31
 
29
- ## 🚀 Quick Start
32
+ ## Quick Start
30
33
 
31
- ### Step 1: Install and add the middleware to your Express app
34
+ ### Step 1 Install & add middleware
32
35
 
33
36
  ```bash
34
37
  npm install @umeshindu222/apisnap
@@ -40,27 +43,118 @@ const apisnap = require('@umeshindu222/apisnap');
40
43
 
41
44
  const app = express();
42
45
 
43
- // Your routes here
46
+ // Your routes go here
44
47
  app.get('/users', (req, res) => res.json({ users: [] }));
45
48
  app.post('/users', (req, res) => res.json({ message: 'Created' }));
46
49
 
47
- // Add APISnap place AFTER your routes
50
+ // APISnap goes AFTER your routes (so it can discover them)
51
+ // ✅ APISnap goes BEFORE global auth middleware (to allow discovery)
48
52
  apisnap.init(app);
49
53
 
54
+ // ⚠️ If you use global auth middleware, place it AFTER apisnap.init():
55
+ // app.use(authMiddleware); ← AFTER init, not before
56
+
50
57
  app.listen(3000);
51
58
  ```
52
59
 
53
- ### Step 2: Run the health check
60
+ ### Step 2 Run
54
61
 
55
62
  ```bash
56
63
  npx @umeshindu222/apisnap --port 3000
57
64
  ```
58
65
 
59
- That's it. APISnap finds and tests every route automatically.
66
+ ---
67
+
68
+ ## 🔐 Fixing 401 / 403 Errors
69
+
70
+ This is the most common issue. APISnap sends real HTTP requests, so **protected routes require credentials** just like any client would.
71
+
72
+ ### JWT / Bearer Token
73
+ ```bash
74
+ npx @umeshindu222/apisnap -H "Authorization: Bearer eyJhbGci..."
75
+ ```
76
+
77
+ ### API Key header
78
+ ```bash
79
+ npx @umeshindu222/apisnap -H "x-api-key: my-secret-key"
80
+ ```
81
+
82
+ ### Cookie / Session auth
83
+ ```bash
84
+ npx @umeshindu222/apisnap --cookie "sessionId=abc123; connect.sid=xyz"
85
+ ```
86
+
87
+ ### Multiple headers at once (`-H` can be repeated)
88
+ ```bash
89
+ npx @umeshindu222/apisnap \
90
+ -H "Authorization: Bearer TOKEN" \
91
+ -H "x-tenant-id: acme" \
92
+ -H "x-api-version: 2"
93
+ ```
94
+
95
+ ### Skip specific protected routes
96
+ ```javascript
97
+ // In your server — skip routes you don't want tested:
98
+ apisnap.init(app, {
99
+ skip: ['/admin', '/internal', '/webhooks']
100
+ });
101
+ ```
102
+
103
+ ### Use a config file (recommended for teams)
104
+
105
+ Create `.apisnaprc.json` in your project root:
106
+
107
+ ```json
108
+ {
109
+ "port": "3000",
110
+ "slow": "300",
111
+ "headers": [
112
+ "Authorization: Bearer YOUR_DEV_TOKEN",
113
+ "x-api-key: YOUR_KEY"
114
+ ],
115
+ "cookie": "sessionId=dev-session-abc",
116
+ "params": {
117
+ "id": "42",
118
+ "slug": "hello-world",
119
+ "uuid": "550e8400-e29b-41d4-a716-446655440000"
120
+ }
121
+ }
122
+ ```
123
+
124
+ Then just run:
125
+ ```bash
126
+ npx @umeshindu222/apisnap
127
+ ```
128
+
129
+ ---
130
+
131
+ ## Middleware Placement (Important!)
132
+
133
+ The order of middleware in Express matters:
134
+
135
+ ```javascript
136
+ // ✅ CORRECT — apisnap can bypass auth because it registers first
137
+ app.use(express.json());
138
+ apisnap.init(app); // ← BEFORE auth middleware
139
+ app.use(authMiddleware); // ← AFTER apisnap.init
140
+
141
+ // ❌ WRONG — auth blocks the discovery endpoint
142
+ app.use(authMiddleware); // ← BEFORE apisnap
143
+ apisnap.init(app); // discovery endpoint gets blocked!
144
+ ```
145
+
146
+ If you **must** put auth before apisnap, manually whitelist the discovery path:
147
+
148
+ ```javascript
149
+ app.use((req, res, next) => {
150
+ if (req.path === '/__apisnap_discovery') return next(); // bypass
151
+ return authMiddleware(req, res, next);
152
+ });
153
+ ```
60
154
 
61
155
  ---
62
156
 
63
- ## 📋 CLI Usage
157
+ ## CLI Reference
64
158
 
65
159
  ```bash
66
160
  npx @umeshindu222/apisnap [options]
@@ -68,114 +162,164 @@ npx @umeshindu222/apisnap [options]
68
162
 
69
163
  | Option | Description | Default |
70
164
  |--------|-------------|---------|
71
- | `-p, --port <number>` | Port your server is running on | `3000` |
72
- | `-H, --header <string>` | Custom header for auth (e.g., `"Authorization: Bearer token"`) | — |
73
- | `-s, --slow <number>` | Response time threshold in ms for slow warnings | `200` |
74
- | `-e, --export <filename>` | Export results to a JSON file | |
75
- | `-V, --version` | Show version number | |
76
- | `-h, --help` | Show help | |
165
+ | `-p, --port <n>` | Port your server runs on | `3000` |
166
+ | `-H, --header <str>` | Add auth header (repeatable) | — |
167
+ | `-c, --cookie <str>` | Cookie string for session auth | |
168
+ | `-s, --slow <n>` | Slow threshold in ms | `200` |
169
+ | `-t, --timeout <n>` | Request timeout in ms | `5000` |
170
+ | `-r, --retry <n>` | Retry failed requests N times | `0` |
171
+ | `-e, --export <file>` | Export JSON report | — |
172
+ | `--html <file>` | Export HTML report | — |
173
+ | `--only <methods>` | Filter methods (e.g. `GET,POST`) | — |
174
+ | `--base-url <url>` | Override base URL (for staging) | `localhost` |
175
+ | `--params <json>` | Path param overrides as JSON | — |
176
+ | `--fail-on-slow` | Exit code 1 if slow routes found | `false` |
77
177
 
78
178
  ---
79
179
 
80
- ## Examples
180
+ ## Examples
81
181
 
82
- ### Basic health check
182
+ ### Basic
83
183
  ```bash
84
184
  npx @umeshindu222/apisnap --port 3000
85
185
  ```
86
186
 
87
- ### With authentication (JWT / Bearer tokens)
187
+ ### With JWT auth
88
188
  ```bash
89
- npx @umeshindu222/apisnap --port 3000 --header "Authorization: Bearer eyJhbGci..."
189
+ npx @umeshindu222/apisnap -p 3000 -H "Authorization: Bearer eyJhbGci..."
90
190
  ```
91
191
 
92
- ### Custom slow threshold (flag routes > 500ms)
192
+ ### Custom path params (for routes like `/users/:id/posts/:postId`)
93
193
  ```bash
94
- npx @umeshindu222/apisnap --port 3000 --slow 500
194
+ npx @umeshindu222/apisnap --params '{"id":"42","postId":"7"}'
95
195
  ```
96
196
 
97
- ### Export report to JSON
197
+ ### Test only GET routes
98
198
  ```bash
99
- npx @umeshindu222/apisnap --port 3000 --export my-report
100
- # Creates: my-report.json
199
+ npx @umeshindu222/apisnap --only GET
101
200
  ```
102
201
 
103
- ### All options together
202
+ ### Test staging server
104
203
  ```bash
105
- npx @umeshindu222/apisnap --port 5000 --header "Authorization: Bearer TOKEN" --slow 300 --export ci-report
204
+ npx @umeshindu222/apisnap --base-url https://staging.myapp.com -H "Authorization: Bearer TOKEN"
106
205
  ```
107
206
 
108
- ---
207
+ ### Generate HTML report
208
+ ```bash
209
+ npx @umeshindu222/apisnap --html report
210
+
211
+ # Mac
212
+ # open report.html
109
213
 
110
- ## Sample Output
214
+ # Windows
215
+ # start report.html
111
216
 
217
+ # Linux
218
+ # xdg-open report.html
112
219
  ```
113
- 📸 APISnap v1.0.0
114
- Slow threshold: 200ms
115
220
 
116
- Connected! Found 6 endpoints.
221
+ ### CI/CD fail pipeline on any broken endpoint
222
+ ```bash
223
+ npx @umeshindu222/apisnap --export ci-report && echo "All healthy!"
224
+ # Exit code 1 if any endpoint fails
225
+ ```
226
+
227
+ ### Full power
228
+ ```bash
229
+ npx @umeshindu222/apisnap \
230
+ -p 5000 \
231
+ -H "Authorization: Bearer TOKEN" \
232
+ -H "x-api-key: SECRET" \
233
+ --cookie "sessionId=abc" \
234
+ --slow 300 \
235
+ --retry 2 \
236
+ --html report \
237
+ --export report \
238
+ --fail-on-slow
239
+ ```
240
+
241
+ ---
117
242
 
118
- ✔ GET /health [200 OK] 3ms
119
- ✔ GET /users [200 OK] 12ms
120
- ✔ POST /users [200 OK] 8ms
121
- ✔ GET /users/1 [200 OK] 15ms
122
- ⚠️ GET /reports [200 OK] 543ms ← slow!
123
- ✖ DELETE /users/1 [401]
243
+ ## Sample Output
244
+
245
+ ```
246
+ 📸 APISnap v2.0.0
247
+ Target: http://localhost:3000
248
+ Slow: >200ms
249
+ Timeout: 5000ms
250
+ Headers: {"Authorization":"Bearer ••••••"}
251
+
252
+ ✔ Connected! Found 6 endpoints to test.
253
+
254
+ ✔ GET /health [200] 3ms
255
+ ✔ GET /users [200] 12ms
256
+ ✔ POST /users [200] 8ms
257
+ ✔ GET /users/1 [200] 15ms
258
+ ⚠️ GET /reports [200] 543ms ← slow!
259
+ ✖ DELETE /users/1 [401]
260
+ 💡 Hint: 401 Unauthorized — try adding -H "Authorization: Bearer YOUR_TOKEN"
124
261
 
125
262
  📊 Summary:
126
263
  ✅ Passed: 5
127
264
  ❌ Failed: 1
128
265
  ⚠️ Slow: 1 (>200ms)
266
+ ⏱ Avg: 100ms
267
+ 🕐 Total: 600ms
129
268
 
130
269
  ⚠️ Some endpoints are unhealthy!
131
270
  ```
132
271
 
133
272
  ---
134
273
 
135
- ## 💾 JSON Report Format
274
+ ## HTML Report
136
275
 
137
- When using `--export`, a structured JSON file is created:
276
+ `--html report` generates a beautiful standalone HTML file:
277
+
278
+ - Pass rate progress bar
279
+ - Color-coded result table
280
+ - Per-endpoint timing, status, retry count
281
+ - No external dependencies — works offline
282
+
283
+ ---
284
+
285
+ ## JSON Report Format
138
286
 
139
287
  ```json
140
288
  {
141
289
  "tool": "APISnap",
142
- "generatedAt": "2026-03-06T15:56:20.375Z",
290
+ "version": "2.0.0",
291
+ "generatedAt": "2026-03-08T10:00:00.000Z",
143
292
  "config": { "port": "3000", "slowThreshold": 200 },
144
- "summary": { "total": 6, "passed": 5, "failed": 1, "slow": 1 },
293
+ "summary": {
294
+ "total": 6, "passed": 5, "failed": 1,
295
+ "slow": 1, "avgDuration": 100, "totalDuration": 600
296
+ },
145
297
  "results": [
146
- { "method": "GET", "path": "/health", "status": 200, "duration": 3, "success": true, "slow": false },
147
- { "method": "GET", "path": "/users", "status": 200, "duration": 12, "success": true, "slow": false },
148
- { "method": "GET", "path": "/reports", "status": 200, "duration": 543,"success": true, "slow": true }
298
+ { "method": "GET", "path": "/users", "status": 200, "duration": 12, "success": true, "slow": false, "retries": 0 }
149
299
  ]
150
300
  }
151
301
  ```
152
302
 
153
- > **CI/CD tip:** Parse `summary.failed` and fail your pipeline build if it's greater than `0`!
303
+ > **CI/CD tip:** Check `summary.failed > 0` to fail your build.
154
304
 
155
305
  ---
156
306
 
157
- ## 🔧 How It Works
307
+ ## How It Works
158
308
 
159
- APISnap uses a two-part architecture:
309
+ 1. **Middleware** `apisnap.init(app)` registers `/__apisnap_discovery` and patches `app.use` so global auth middleware skips the discovery path automatically.
160
310
 
161
- 1. **Middleware (The Seeker)** — `apisnap.init(app)` injects a hidden endpoint `/__apisnap_discovery` into your Express app. When called, it recursively walks the Express router stack and returns a map of every registered route including nested sub-routers.
311
+ 2. **CLI** — Calls the discovery endpoint, gets the full route map, then pings each route with your headers/cookies. Smart defaults replace `:id` `1`, `:uuid` a valid UUID, `:slug` `"example"`, etc.
162
312
 
163
- 2. **CLI Runner (The Checker)** — `npx @umeshindu222/apisnap` calls the discovery endpoint, gets the route map, then "pings" each route using axios — injecting your headers, replacing path params with safe defaults (`:id` → `1`), and timing each response.
313
+ 3. **Reports** — Results are collected and can be exported as JSON (for CI/CD) or a self-contained HTML file (for humans).
164
314
 
165
315
  ---
166
316
 
167
- ## 🤝 Contributing
168
-
169
- Contributions, issues and feature requests are welcome!
317
+ ## Contributing
170
318
 
171
- 1. Fork the repo
172
- 2. Create your feature branch: `git checkout -b feat/amazing-feature`
173
- 3. Commit your changes: `git commit -m 'feat: add amazing feature'`
174
- 4. Push to the branch: `git push origin feat/amazing-feature`
175
- 5. Open a Pull Request
319
+ 1. Fork `git checkout -b feat/amazing` → commit → push → PR
176
320
 
177
321
  ---
178
322
 
179
- ## 📄 License
323
+ ## License
180
324
 
181
325
  MIT © [Umesh Induranga](https://github.com/Umeshinduranga)
@@ -4,157 +4,382 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const fs_1 = __importDefault(require("fs"));
7
+ const path_1 = __importDefault(require("path"));
7
8
  const axios_1 = __importDefault(require("axios"));
8
9
  const chalk_1 = __importDefault(require("chalk"));
9
10
  const ora_1 = __importDefault(require("ora"));
10
11
  const commander_1 = require("commander");
11
12
  const program = new commander_1.Command();
12
13
  const { version } = require('../../package.json');
14
+ // ─── Config File Loader ───────────────────────────────────────────────────────
15
+ function loadConfigFile() {
16
+ const configNames = ['.apisnaprc', '.apisnaprc.json', 'apisnap.config.json'];
17
+ for (const name of configNames) {
18
+ const filePath = path_1.default.resolve(process.cwd(), name);
19
+ if (fs_1.default.existsSync(filePath)) {
20
+ try {
21
+ // Strip BOM if present (fixes Windows PowerShell encoding issue)
22
+ let raw = fs_1.default.readFileSync(filePath, 'utf-8');
23
+ raw = raw.replace(/^\uFEFF/, '');
24
+ raw = raw.trim();
25
+ console.log(chalk_1.default.gray(` Config: ${name}\n`));
26
+ return JSON.parse(raw);
27
+ }
28
+ catch (e) {
29
+ console.warn(chalk_1.default.yellow(`⚠️ Could not parse config file: ${name}`));
30
+ }
31
+ }
32
+ }
33
+ return {};
34
+ }
35
+ // ─── Header Parser ─────────────────────────────────────────────────────────
36
+ function parseHeaders(headerArgs) {
37
+ const headers = {};
38
+ for (const h of headerArgs) {
39
+ const colonIdx = h.indexOf(':');
40
+ if (colonIdx > 0) {
41
+ const key = h.slice(0, colonIdx).trim();
42
+ const value = h.slice(colonIdx + 1).trim();
43
+ headers[key] = value;
44
+ }
45
+ else {
46
+ console.warn(chalk_1.default.yellow(`⚠️ Skipping malformed header: "${h}" (expected "Key: Value")`));
47
+ }
48
+ }
49
+ return headers;
50
+ }
51
+ // ─── Smart Path Param Replacement ────────────────────────────────────────────
52
+ function replacePath(rawPath, paramMap = {}) {
53
+ return rawPath.replace(/:([a-zA-Z0-9_]+)/g, (_, param) => {
54
+ if (paramMap[param])
55
+ return paramMap[param];
56
+ // Smart defaults based on param name
57
+ if (/id$/i.test(param))
58
+ return '1';
59
+ if (/slug$/i.test(param))
60
+ return 'example';
61
+ if (/uuid$/i.test(param))
62
+ return '00000000-0000-0000-0000-000000000001';
63
+ if (/name$/i.test(param))
64
+ return 'test';
65
+ if (/token$/i.test(param))
66
+ return 'abc123';
67
+ if (/page$/i.test(param))
68
+ return '1';
69
+ if (/limit$/i.test(param))
70
+ return '10';
71
+ return '1'; // fallback
72
+ });
73
+ }
74
+ // ─── HTML Report Generator ────────────────────────────────────────────────────
75
+ function generateHTMLReport(data) {
76
+ const passRate = data.summary.total > 0
77
+ ? Math.round((data.summary.passed / data.summary.total) * 100)
78
+ : 0;
79
+ const rowColor = (r) => {
80
+ if (!r.success)
81
+ return '#fee2e2';
82
+ if (r.slow)
83
+ return '#fef9c3';
84
+ return '#f0fdf4';
85
+ };
86
+ const rows = data.results.map(r => `
87
+ <tr style="background:${rowColor(r)}">
88
+ <td><span class="badge badge-${r.method.toLowerCase()}">${r.method}</span></td>
89
+ <td><code>${r.path}</code></td>
90
+ <td>${r.success
91
+ ? `<span class="ok">✔ ${r.status}</span>`
92
+ : `<span class="fail">✖ ${r.status || 'ERR'}</span>`}</td>
93
+ <td>${r.slow ? `<span class="slow">⚠️ ${r.duration}ms</span>` : `${r.duration}ms`}</td>
94
+ <td>${r.retries > 0 ? `${r.retries} retry` : '—'}</td>
95
+ <td>${r.error ? `<span class="errtext">${r.error}</span>` : '—'}</td>
96
+ </tr>
97
+ `).join('');
98
+ return `<!DOCTYPE html>
99
+ <html lang="en">
100
+ <head>
101
+ <meta charset="UTF-8"/>
102
+ <title>APISnap Report — ${data.generatedAt}</title>
103
+ <style>
104
+ *{box-sizing:border-box;margin:0;padding:0}
105
+ body{font-family:'Segoe UI',system-ui,sans-serif;background:#f8fafc;color:#1e293b;padding:2rem}
106
+ h1{font-size:1.8rem;margin-bottom:.25rem}
107
+ .sub{color:#64748b;font-size:.9rem;margin-bottom:2rem}
108
+ .cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:1rem;margin-bottom:2rem}
109
+ .card{background:#fff;border-radius:12px;padding:1.2rem;box-shadow:0 1px 4px rgba(0,0,0,.08);text-align:center}
110
+ .card .num{font-size:2rem;font-weight:700}
111
+ .card .lbl{font-size:.8rem;color:#64748b;margin-top:.25rem}
112
+ .green{color:#16a34a}.red{color:#dc2626}.yellow{color:#ca8a04}.blue{color:#2563eb}
113
+ table{width:100%;border-collapse:collapse;background:#fff;border-radius:12px;overflow:hidden;box-shadow:0 1px 4px rgba(0,0,0,.08)}
114
+ th{background:#1e293b;color:#f8fafc;padding:.8rem 1rem;text-align:left;font-size:.85rem}
115
+ td{padding:.75rem 1rem;font-size:.875rem;border-bottom:1px solid #f1f5f9}
116
+ .badge{display:inline-block;padding:.15rem .5rem;border-radius:6px;font-weight:600;font-size:.75rem;color:#fff}
117
+ .badge-get{background:#2563eb}.badge-post{background:#16a34a}.badge-put{background:#d97706}
118
+ .badge-delete{background:#dc2626}.badge-patch{background:#7c3aed}
119
+ .ok{color:#16a34a;font-weight:600}.fail{color:#dc2626;font-weight:600}.slow{color:#ca8a04;font-weight:600}
120
+ .errtext{color:#dc2626;font-size:.8rem}
121
+ .progress{background:#e2e8f0;border-radius:999px;height:10px;margin:1rem 0}
122
+ .progress-bar{background:#16a34a;height:10px;border-radius:999px;transition:width .3s}
123
+ footer{margin-top:2rem;color:#94a3b8;font-size:.8rem;text-align:center}
124
+ </style>
125
+ </head>
126
+ <body>
127
+ <h1>📸 APISnap Health Report</h1>
128
+ <p class="sub">Generated: ${data.generatedAt} &nbsp;|&nbsp; Port: ${data.config.port} &nbsp;|&nbsp; v${data.version}</p>
129
+
130
+ <div class="cards">
131
+ <div class="card"><div class="num blue">${data.summary.total}</div><div class="lbl">Total Endpoints</div></div>
132
+ <div class="card"><div class="num green">${data.summary.passed}</div><div class="lbl">Passed</div></div>
133
+ <div class="card"><div class="num red">${data.summary.failed}</div><div class="lbl">Failed</div></div>
134
+ <div class="card"><div class="num yellow">${data.summary.slow}</div><div class="lbl">Slow (&gt;${data.config.slowThreshold}ms)</div></div>
135
+ <div class="card"><div class="num blue">${data.summary.avgDuration}ms</div><div class="lbl">Avg Response</div></div>
136
+ <div class="card"><div class="num ${passRate === 100 ? 'green' : passRate >= 80 ? 'yellow' : 'red'}">${passRate}%</div><div class="lbl">Pass Rate</div></div>
137
+ </div>
138
+
139
+ <div class="progress"><div class="progress-bar" style="width:${passRate}%"></div></div>
140
+
141
+ <table>
142
+ <thead>
143
+ <tr><th>Method</th><th>Path</th><th>Status</th><th>Duration</th><th>Retries</th><th>Error</th></tr>
144
+ </thead>
145
+ <tbody>${rows}</tbody>
146
+ </table>
147
+
148
+ <footer>APISnap v${data.version} — MIT License</footer>
149
+ </body>
150
+ </html>`;
151
+ }
152
+ // ─── Main CLI ─────────────────────────────────────────────────────────────────
13
153
  program
14
154
  .name('apisnap')
15
155
  .description('Instant API health-check CLI for Express.js')
16
156
  .version(version)
17
- .option('-p, --port <number>', 'The port your server is running on', '3000')
18
- .option('-H, --header <string>', 'Add a custom header (e.g., "Authorization: Bearer token")')
19
- .option('-s, --slow <number>', 'Threshold for slow response warning (ms)', '200')
20
- .option('-e, --export <filename>', 'Export results to a JSON file (e.g., report.json)')
157
+ .option('-p, --port <number>', 'Port your server is running on')
158
+ .option('-H, --header <string>', 'Custom header can be used multiple times (e.g. -H "Authorization: Bearer TOKEN" -H "x-api-key: SECRET")', collect, [])
159
+ .option('-c, --cookie <string>', 'Cookie string (e.g. "sessionId=abc; token=xyz")')
160
+ .option('-s, --slow <number>', 'Slow response threshold in ms')
161
+ .option('-t, --timeout <number>', 'Request timeout in ms')
162
+ .option('-r, --retry <number>', 'Retry failed requests N times')
163
+ .option('-e, --export <filename>', 'Export JSON report (e.g. report)')
164
+ .option('--html <filename>', 'Export HTML report (e.g. report)')
165
+ .option('--only <methods>', 'Only test specific methods (e.g. "GET,POST")')
166
+ .option('--base-url <url>', 'Override base URL (e.g. https://staging.myapp.com)')
167
+ .option('--params <json>', 'JSON map of param overrides (e.g. \'{"id":"42"}\')')
168
+ .option('--fail-on-slow', 'Exit with code 1 if any slow routes are found')
21
169
  .action(async (options) => {
22
- const port = options.port;
23
- const slowThreshold = parseInt(options.slow);
170
+ // Merge config file with CLI options (CLI takes precedence)
171
+ const fileConfig = loadConfigFile();
172
+ const mergedOptions = { ...fileConfig, ...options };
173
+ const port = mergedOptions.port || '3000';
174
+ const slowThreshold = parseInt(mergedOptions.slow || '200');
175
+ const timeout = parseInt(mergedOptions.timeout || '5000');
176
+ const retryCount = parseInt(mergedOptions.retry || '0');
177
+ const onlyMethods = mergedOptions.only
178
+ ? mergedOptions.only.split(',').map((m) => m.trim().toUpperCase())
179
+ : null;
180
+ const paramOverrides = mergedOptions.params
181
+ ? JSON.parse(mergedOptions.params)
182
+ : (fileConfig.params || {});
183
+ const baseUrl = mergedOptions.baseUrl || mergedOptions['base-url'] || `http://localhost:${port}`;
24
184
  const discoveryUrl = `http://localhost:${port}/__apisnap_discovery`;
25
- const results = []; // Collect all test results here
26
- // Parse custom header from CLI flag
27
- const customHeaders = {};
28
- if (options.header) {
29
- const [key, ...value] = options.header.split(':');
30
- if (key && value) {
31
- customHeaders[key.trim()] = value.join(':').trim();
32
- }
185
+ // Build headers
186
+ const headerArgs = [
187
+ ...(Array.isArray(mergedOptions.header) ? mergedOptions.header : []),
188
+ ...(Array.isArray(fileConfig.headers) ? fileConfig.headers : []),
189
+ ];
190
+ const customHeaders = {
191
+ ...parseHeaders(headerArgs),
192
+ 'User-Agent': `APISnap/${version}`,
193
+ };
194
+ if (mergedOptions.cookie) {
195
+ customHeaders['Cookie'] = mergedOptions.cookie;
33
196
  }
197
+ // ── Banner ──────────────────────────────────────────────────────────────
34
198
  console.log(chalk_1.default.bold.cyan(`\n📸 APISnap v${version}`));
35
- // Show active options
36
- if (Object.keys(customHeaders).length > 0) {
37
- console.log(chalk_1.default.gray(` Headers: ${JSON.stringify(customHeaders)}`));
38
- }
39
- console.log(chalk_1.default.gray(` Slow threshold: ${slowThreshold}ms`));
40
- if (options.export) {
41
- console.log(chalk_1.default.gray(` Export: ${options.export}`));
199
+ console.log(chalk_1.default.gray(` Target: ${baseUrl}`));
200
+ console.log(chalk_1.default.gray(` Slow: >${slowThreshold}ms`));
201
+ console.log(chalk_1.default.gray(` Timeout: ${timeout}ms`));
202
+ if (retryCount > 0)
203
+ console.log(chalk_1.default.gray(` Retries: ${retryCount}`));
204
+ if (Object.keys(customHeaders).filter(k => k !== 'User-Agent').length > 0) {
205
+ const safeHeaders = { ...customHeaders };
206
+ // Mask auth tokens in output
207
+ Object.keys(safeHeaders).forEach(k => {
208
+ if (/auth|token|key|secret|cookie/i.test(k)) {
209
+ safeHeaders[k] = safeHeaders[k].slice(0, 8) + '••••••';
210
+ }
211
+ });
212
+ delete safeHeaders['User-Agent'];
213
+ console.log(chalk_1.default.gray(` Headers: ${JSON.stringify(safeHeaders)}`));
42
214
  }
215
+ if (onlyMethods)
216
+ console.log(chalk_1.default.gray(` Filter: ${onlyMethods.join(', ')}`));
43
217
  console.log();
44
- const spinner = (0, ora_1.default)('Connecting to your API...').start();
218
+ const spinner = (0, ora_1.default)('Connecting to discovery endpoint...').start();
219
+ const results = [];
45
220
  try {
46
- // 1. Fetch the route map from the middleware
47
- const response = await axios_1.default.get(discoveryUrl);
48
- const { endpoints } = response.data;
49
- spinner.succeed(chalk_1.default.green(`Connected! Found ${endpoints.length} endpoints.\n`));
50
- // Summary counters
51
- let passed = 0;
52
- let failed = 0;
53
- let slow = 0;
54
- // 2. Loop through each discovered endpoint
221
+ // ── Discovery ───────────────────────────────────────────────────────
222
+ const discovery = await axios_1.default.get(discoveryUrl, { timeout: 5000 });
223
+ let { endpoints } = discovery.data;
224
+ // Filter by method if --only flag provided
225
+ if (onlyMethods) {
226
+ endpoints = endpoints.filter((e) => e.methods.some((m) => onlyMethods.includes(m)));
227
+ }
228
+ spinner.succeed(chalk_1.default.green(`Connected! Found ${endpoints.length} endpoint${endpoints.length !== 1 ? 's' : ''} to test.\n`));
229
+ let passed = 0, failed = 0, slow = 0;
230
+ const allDurations = [];
231
+ // ── Test Each Endpoint ───────────────────────────────────────────────
55
232
  for (const endpoint of endpoints) {
56
- const method = endpoint.methods[0];
57
- let path = endpoint.path;
58
- // Smart Parameter Replacement — :id, :slug → 1
59
- if (path.includes(':')) {
60
- path = path.replace(/:[a-zA-Z0-9]+/g, '1');
61
- }
62
- const fullUrl = `http://localhost:${port}${path}`;
63
- const testSpinner = (0, ora_1.default)(`Testing ${chalk_1.default.bold(method)} ${path}`).start();
64
- // Step 8: Initialize result object for this endpoint
233
+ const method = endpoint.methods[0]; // primary method
234
+ const rawPath = endpoint.path;
235
+ const resolvedPath = replacePath(rawPath, paramOverrides);
236
+ const fullUrl = `${baseUrl}${resolvedPath}`;
65
237
  const testResult = {
66
- method,
67
- path,
68
- fullUrl,
69
- status: 0,
70
- duration: 0,
71
- success: false,
72
- slow: false,
238
+ method, path: rawPath, fullUrl,
239
+ status: 0, statusText: '', duration: 0,
240
+ success: false, slow: false, retries: 0,
73
241
  };
74
- try {
75
- const startTime = Date.now();
76
- const res = await (0, axios_1.default)({
77
- method: method,
78
- url: fullUrl,
79
- headers: {
80
- ...customHeaders,
81
- 'User-Agent': 'APISnap/1.0.0',
82
- },
83
- timeout: 5000,
84
- });
85
- const duration = Date.now() - startTime;
86
- // Step 8: Populate result
87
- testResult.duration = duration;
88
- testResult.status = res.status;
89
- testResult.success = true;
90
- // Step 7: Performance threshold check
91
- let statusIcon = chalk_1.default.green('✔');
92
- let durationColor = chalk_1.default.gray;
93
- if (duration > slowThreshold) {
94
- statusIcon = chalk_1.default.yellow('⚠️ ');
95
- durationColor = chalk_1.default.yellow.bold;
96
- testResult.slow = true;
97
- slow++;
242
+ const testSpinner = (0, ora_1.default)({ text: `${chalk_1.default.bold(method.padEnd(7))} ${chalk_1.default.dim(rawPath)}`, prefixText: ' ' }).start();
243
+ let lastError = null;
244
+ let attempt = 0;
245
+ while (attempt <= retryCount) {
246
+ try {
247
+ const start = Date.now();
248
+ const res = await (0, axios_1.default)({
249
+ method,
250
+ url: fullUrl,
251
+ headers: customHeaders,
252
+ timeout,
253
+ validateStatus: () => true, // Don't throw on 4xx/5xx — we judge ourselves
254
+ });
255
+ const duration = Date.now() - start;
256
+ testResult.duration = duration;
257
+ testResult.status = res.status;
258
+ testResult.statusText = res.statusText;
259
+ testResult.retries = attempt;
260
+ testResult.success = res.status < 400;
261
+ testResult.slow = duration > slowThreshold;
262
+ allDurations.push(duration);
263
+ if (testResult.success) {
264
+ const durationStr = testResult.slow
265
+ ? chalk_1.default.yellow.bold(`${duration}ms ← slow!`)
266
+ : chalk_1.default.gray(`${duration}ms`);
267
+ const msg = `${chalk_1.default.bold(method.padEnd(7))} ${chalk_1.default.white(rawPath.padEnd(35))} ` +
268
+ `${chalk_1.default.green(`[${res.status}]`)} ${durationStr}`;
269
+ if (testResult.slow) {
270
+ testSpinner.warn(msg);
271
+ }
272
+ else {
273
+ testSpinner.succeed(msg);
274
+ }
275
+ passed++;
276
+ if (testResult.slow)
277
+ slow++;
278
+ }
279
+ else {
280
+ testSpinner.fail(`${chalk_1.default.bold(method.padEnd(7))} ${chalk_1.default.white(rawPath.padEnd(35))} ` +
281
+ `${chalk_1.default.red(`[${res.status} ${res.statusText}]`)} ${chalk_1.default.gray(`${duration}ms`)}`);
282
+ // Helpful hint for auth errors
283
+ if (res.status === 401) {
284
+ console.log(chalk_1.default.yellow(` 💡 Hint: 401 Unauthorized — try adding -H "Authorization: Bearer YOUR_TOKEN" or --cookie "sessionId=abc"`));
285
+ }
286
+ else if (res.status === 403) {
287
+ console.log(chalk_1.default.yellow(` 💡 Hint: 403 Forbidden — your token may lack permission for this route`));
288
+ }
289
+ else if (res.status === 404) {
290
+ console.log(chalk_1.default.yellow(` 💡 Hint: 404 Not Found — path param replacement may need --params '{"id":"YOUR_ID"}'`));
291
+ }
292
+ failed++;
293
+ }
294
+ lastError = null;
295
+ break; // success, stop retrying
296
+ }
297
+ catch (err) {
298
+ lastError = err;
299
+ attempt++;
300
+ if (attempt <= retryCount) {
301
+ await new Promise(r => setTimeout(r, 500 * attempt)); // backoff
302
+ }
98
303
  }
99
- testSpinner.succeed(`${statusIcon} ${chalk_1.default.bold(method)} ${chalk_1.default.white(path)} ` +
100
- `${chalk_1.default.green(`[${res.status} OK]`)} ` +
101
- `${durationColor(`${duration}ms`)}`);
102
- passed++;
103
304
  }
104
- catch (err) {
105
- testResult.status = err.response?.status || 500;
305
+ if (lastError) {
106
306
  testResult.success = false;
107
- const status = err.response?.status || 'FAIL';
108
- testSpinner.fail(`${chalk_1.default.bold(method)} ${chalk_1.default.white(path)} ` +
109
- `${chalk_1.default.red(`[${status}]`)}`);
307
+ testResult.retries = attempt - 1;
308
+ testResult.error = lastError.code === 'ECONNABORTED' ? 'Timeout' : lastError.message;
309
+ testSpinner.fail(`${chalk_1.default.bold(method.padEnd(7))} ${chalk_1.default.white(rawPath.padEnd(35))} ` +
310
+ chalk_1.default.red(`[${testResult.error}]`));
110
311
  failed++;
111
312
  }
112
- results.push(testResult); // Step 8: Save result to list
313
+ results.push(testResult);
113
314
  }
114
- // Summary Statistics
315
+ // ── Summary ──────────────────────────────────────────────────────────
316
+ const avgDuration = allDurations.length > 0
317
+ ? Math.round(allDurations.reduce((a, b) => a + b, 0) / allDurations.length)
318
+ : 0;
319
+ const totalDuration = allDurations.reduce((a, b) => a + b, 0);
115
320
  console.log(chalk_1.default.bold('\n📊 Summary:'));
116
- console.log(chalk_1.default.green(` ✅ Passed: ${passed}`));
117
- console.log(chalk_1.default.red(` ❌ Failed: ${failed}`));
118
- console.log(chalk_1.default.yellow(` ⚠️ Slow: ${slow} (>${slowThreshold}ms)`));
321
+ console.log(` ${chalk_1.default.green('✅ Passed: ')} ${chalk_1.default.bold(passed)}`);
322
+ console.log(` ${chalk_1.default.red('❌ Failed: ')} ${chalk_1.default.bold(failed)}`);
323
+ console.log(` ${chalk_1.default.yellow('⚠️ Slow: ')} ${chalk_1.default.bold(slow)} (>${slowThreshold}ms)`);
324
+ console.log(` ${chalk_1.default.cyan('⏱ Avg: ')} ${chalk_1.default.bold(avgDuration + 'ms')}`);
325
+ console.log(` ${chalk_1.default.cyan('🕐 Total: ')} ${chalk_1.default.bold(totalDuration + 'ms')}`);
119
326
  if (failed > 0) {
120
327
  console.log(chalk_1.default.red.bold('\n⚠️ Some endpoints are unhealthy!'));
121
328
  }
122
329
  else if (slow > 0) {
123
- console.log(chalk_1.default.yellow.bold('\n🐢 All endpoints alive, but some are slow!'));
330
+ console.log(chalk_1.default.yellow.bold('\n🐢 All alive, but some routes are slow!'));
124
331
  }
125
332
  else {
126
333
  console.log(chalk_1.default.green.bold('\n✨ All systems nominal!'));
127
334
  }
128
- // Step 8: Export report to JSON file
129
- if (options.export) {
130
- const filePath = options.export.endsWith('.json')
131
- ? options.export
132
- : `${options.export}.json`;
133
- const reportData = {
134
- tool: 'APISnap',
135
- generatedAt: new Date().toISOString(),
136
- config: {
137
- port,
138
- slowThreshold,
139
- headers: customHeaders,
140
- },
141
- summary: {
142
- total: endpoints.length,
143
- passed,
144
- failed,
145
- slow,
146
- },
147
- results,
148
- };
335
+ // ── Auth Troubleshooting Summary ─────────────────────────────────────
336
+ const authFailures = results.filter(r => r.status === 401 || r.status === 403);
337
+ if (authFailures.length > 0 && !headerArgs.length && !mergedOptions.cookie) {
338
+ console.log(chalk_1.default.bgYellow.black.bold('\n🔐 Auth Help'));
339
+ console.log(chalk_1.default.yellow(' You have ' + authFailures.length + ' auth failure(s) and no credentials were provided.'));
340
+ console.log(chalk_1.default.yellow(' Solutions:'));
341
+ console.log(chalk_1.default.gray(' JWT: apisnap -H "Authorization: Bearer YOUR_JWT_TOKEN"'));
342
+ console.log(chalk_1.default.gray(' API Key: apisnap -H "x-api-key: YOUR_KEY"'));
343
+ console.log(chalk_1.default.gray(' Cookie: apisnap --cookie "sessionId=abc123"'));
344
+ console.log(chalk_1.default.gray(' Multi: apisnap -H "Authorization: Bearer TOKEN" -H "x-tenant: acme"'));
345
+ console.log(chalk_1.default.gray(' Config: create .apisnaprc.json (see README)\n'));
346
+ }
347
+ // ── Exports ──────────────────────────────────────────────────────────
348
+ const reportData = {
349
+ tool: 'APISnap', version,
350
+ generatedAt: new Date().toISOString(),
351
+ config: { port, baseUrl, slowThreshold, timeout, headers: Object.keys(customHeaders).filter(k => k !== 'User-Agent') },
352
+ summary: { total: endpoints.length, passed, failed, slow, avgDuration, totalDuration },
353
+ results,
354
+ };
355
+ if (mergedOptions.export) {
356
+ const filePath = mergedOptions.export.endsWith('.json') ? mergedOptions.export : `${mergedOptions.export}.json`;
149
357
  fs_1.default.writeFileSync(filePath, JSON.stringify(reportData, null, 2));
150
- console.log(chalk_1.default.cyan.bold(`\n💾 Report saved to: ${chalk_1.default.white(filePath)}`));
358
+ console.log(chalk_1.default.cyan(`\n💾 JSON report ${chalk_1.default.white(filePath)}`));
359
+ }
360
+ if (mergedOptions.html) {
361
+ const filePath = mergedOptions.html.endsWith('.html') ? mergedOptions.html : `${mergedOptions.html}.html`;
362
+ fs_1.default.writeFileSync(filePath, generateHTMLReport(reportData));
363
+ console.log(chalk_1.default.cyan(`🌐 HTML report → ${chalk_1.default.white(filePath)}`));
151
364
  }
365
+ console.log();
366
+ // Exit codes for CI/CD
367
+ const shouldFail = failed > 0 || (mergedOptions['fail-on-slow'] && slow > 0);
368
+ process.exit(shouldFail ? 1 : 0);
152
369
  }
153
370
  catch (error) {
154
- spinner.fail(chalk_1.default.red(`Failed to connect to ${discoveryUrl}`));
155
- console.log(chalk_1.default.yellow('Is your server running? Make sure apisnap.init(app) is added.\n'));
371
+ spinner.fail(chalk_1.default.red(`Cannot reach discovery endpoint: ${discoveryUrl}`));
372
+ console.log(chalk_1.default.yellow('\n Checklist:'));
373
+ console.log(chalk_1.default.gray(' 1. Is your server running? (e.g. node server.js)'));
374
+ console.log(chalk_1.default.gray(' 2. Did you call apisnap.init(app) in your server?'));
375
+ console.log(chalk_1.default.gray(' 3. Is the port correct? (default 3000, use -p PORT)'));
376
+ console.log(chalk_1.default.gray(' 4. Is apisnap.init(app) placed AFTER your routes?\n'));
156
377
  process.exit(1);
157
378
  }
158
379
  });
380
+ // Allows -H to be used multiple times
381
+ function collect(val, prev) {
382
+ return prev.concat([val]);
383
+ }
159
384
  program.parse(process.argv);
160
385
  //# sourceMappingURL=runner.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/core/runner.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/core/runner.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,12 @@
1
- export declare const init: (app: any) => void;
1
+ export interface APISnapOptions {
2
+ /** Routes to skip during health checks (e.g. ['/admin', '/internal']) */
3
+ skip?: string[];
4
+ /** Custom name shown in discovery response */
5
+ name?: string;
6
+ }
7
+ export declare const init: (app: any, options?: APISnapOptions) => void;
2
8
  declare const _default: {
3
- init: (app: any) => void;
9
+ init: (app: any, options?: APISnapOptions) => void;
4
10
  };
5
11
  export default _default;
6
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,IAAI,GAAI,KAAK,GAAG,SA4E5B,CAAC;;gBA5EwB,GAAG;;AA8E7B,wBAAwB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC3B,yEAAyE;IACzE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,8CAA8C;IAC9C,IAAI,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,IAAI,GAAI,KAAK,GAAG,EAAE,UAAS,cAAmB,SA6F1D,CAAC;;gBA7FwB,GAAG,YAAW,cAAc;;AA+FtD,wBAAwB"}
@@ -1,30 +1,33 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.init = void 0;
4
- const init = (app) => {
4
+ const init = (app, options = {}) => {
5
5
  const DISCOVERY_PATH = '/__apisnap_discovery';
6
- // Recursive function to find ALL routes, even in sub-routers
7
- const splitRoutes = (stack, prefix = '') => {
6
+ const skipList = options.skip || [];
7
+ // ─── Recursive Route Extractor ────────────────────────────────────────────
8
+ const extractRoutes = (stack, prefix = '') => {
8
9
  let routes = [];
9
10
  stack.forEach((layer) => {
10
11
  if (layer.route) {
11
- // Simple direct route
12
- const path = prefix + layer.route.path;
13
- const methods = Object.keys(layer.route.methods).map((m) => m.toUpperCase());
14
- routes.push({ path: path.replace('//', '/'), methods });
12
+ const path = (prefix + layer.route.path).replace('//', '/');
13
+ // Skip the discovery endpoint itself and any user-skipped paths
14
+ if (path === DISCOVERY_PATH)
15
+ return;
16
+ if (skipList.some((s) => path.startsWith(s)))
17
+ return;
18
+ const methods = Object.keys(layer.route.methods)
19
+ .filter((m) => m !== '_all')
20
+ .map((m) => m.toUpperCase());
21
+ routes.push({ path, methods });
15
22
  }
16
23
  else if (layer.handle && layer.handle.stack) {
17
- // Nested Router - GO DEEPER
18
- // Extract the prefix from the regexp (e.g. /api or /api/community)
19
24
  let rStr = layer.regexp.toString();
20
25
  let routerPrefix = '';
21
- // Try to match standard Express router regexp: /^\/api\/?(?=\/|$)/i
22
26
  const standardMatch = rStr.match(/^\/\^\\\/(.*?)\\\/\?\(\?\=\\\/\|\$\)\/i$/);
23
27
  if (standardMatch) {
24
28
  routerPrefix = standardMatch[1];
25
29
  }
26
30
  else {
27
- // Fallback for custom or older regexes
28
31
  const fallbackMatch = rStr.match(/^\/\^\\?(.*?)\\?\/?(?:\(\?=\\\/\|\$\))?\//);
29
32
  routerPrefix = fallbackMatch ? fallbackMatch[1] : '';
30
33
  }
@@ -32,32 +35,31 @@ const init = (app) => {
32
35
  if (routerPrefix && !routerPrefix.startsWith('/')) {
33
36
  routerPrefix = '/' + routerPrefix;
34
37
  }
35
- // Avoid double slashes in concatenation
36
38
  const newPrefix = (prefix + routerPrefix).replace(/\/\//g, '/');
37
- routes = routes.concat(splitRoutes(layer.handle.stack, newPrefix));
39
+ routes = routes.concat(extractRoutes(layer.handle.stack, newPrefix));
38
40
  }
39
41
  });
40
42
  return routes;
41
43
  };
44
+ // ─── Discovery Endpoint ───────────────────────────────────────────────────
45
+ // IMPORTANT: This is registered FIRST so auth middleware added later won't
46
+ // wrap it. If your auth is global (app.use), see the bypass middleware below.
42
47
  app.get(DISCOVERY_PATH, (req, res) => {
43
48
  try {
44
- // Safely get Express router (v4 uses _router, v5 uses router)
45
49
  let router = app._router;
46
50
  if (!router) {
47
51
  try {
48
52
  router = app.router;
49
53
  }
50
- catch (err) {
51
- // Ignore getter deprecation errors from Express 4
52
- }
54
+ catch (_) { }
53
55
  }
54
56
  if (!router) {
55
- res.status(500).json({ error: 'Router not initialized yet' });
56
- return;
57
+ return res.status(500).json({ error: 'Router not initialized yet. Make sure apisnap.init(app) is called after your routes.' });
57
58
  }
58
- const allRoutes = splitRoutes(router.stack);
59
+ const allRoutes = extractRoutes(router.stack);
59
60
  res.json({
60
- name: 'APISnap Discovery',
61
+ tool: 'APISnap',
62
+ appName: options.name || 'Express App',
61
63
  timestamp: new Date().toISOString(),
62
64
  total: allRoutes.length,
63
65
  endpoints: allRoutes,
@@ -67,7 +69,27 @@ const init = (app) => {
67
69
  res.status(500).json({ error: 'Failed to parse routes', detail: e.message });
68
70
  }
69
71
  });
70
- console.log(`\x1b[32m%s\x1b[0m`, `✅ [APISnap] Discovery active at ${DISCOVERY_PATH}`);
72
+ // ─── Auth Bypass Middleware ───────────────────────────────────────────────
73
+ // This intercepts requests to the discovery path and short-circuits any
74
+ // downstream auth middleware the user may have added globally.
75
+ // Works by monkey-patching app.use to detect auth-style middleware.
76
+ const originalUse = app.use.bind(app);
77
+ app.use = function (...args) {
78
+ // If it's a global middleware (no path), wrap it to skip discovery route
79
+ if (typeof args[0] === 'function') {
80
+ const originalMiddleware = args[0];
81
+ args[0] = (req, res, next) => {
82
+ if (req.path === DISCOVERY_PATH)
83
+ return next();
84
+ return originalMiddleware(req, res, next);
85
+ };
86
+ }
87
+ return originalUse(...args);
88
+ };
89
+ console.log(`\x1b[32m✅ [APISnap] Discovery active → http://localhost:PORT${DISCOVERY_PATH}\x1b[0m`);
90
+ if (skipList.length > 0) {
91
+ console.log(`\x1b[33m⏭ [APISnap] Skipping: ${skipList.join(', ')}\x1b[0m`);
92
+ }
71
93
  };
72
94
  exports.init = init;
73
95
  exports.default = { init: exports.init };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umeshindu222/apisnap",
3
- "version": "1.1.3",
3
+ "version": "1.1.4",
4
4
  "description": "Instant API auto-discovery and health-check CLI for Express.js",
5
5
  "main": "dist/middleware/index.js",
6
6
  "types": "dist/middleware/index.d.ts",