woodland 20.2.1 → 20.2.3
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 +78 -15
- package/dist/cli.cjs +1 -1
- package/dist/woodland.cjs +1 -1
- package/dist/woodland.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -289,7 +289,8 @@ app.always((req, res, next) => {
|
|
|
289
289
|
// Route-specific middleware
|
|
290
290
|
app.get("/protected", authenticate, authorize, handler);
|
|
291
291
|
|
|
292
|
-
//
|
|
292
|
+
// ❌ WRONG: Do NOT register error middleware with 'always'
|
|
293
|
+
// This will execute BEFORE route handlers, not after errors occur
|
|
293
294
|
app.always((error, req, res, next) => {
|
|
294
295
|
if (error) {
|
|
295
296
|
console.error(error);
|
|
@@ -298,8 +299,40 @@ app.always((error, req, res, next) => {
|
|
|
298
299
|
next();
|
|
299
300
|
}
|
|
300
301
|
});
|
|
302
|
+
|
|
303
|
+
// ✅ CORRECT: Register error middleware with specific routes LAST
|
|
304
|
+
app.get("/api/users",
|
|
305
|
+
authenticate, // Normal middleware
|
|
306
|
+
authorize, // Normal middleware
|
|
307
|
+
getUserHandler, // Route handler
|
|
308
|
+
(error, req, res, next) => { // Error middleware - LAST
|
|
309
|
+
if (error) {
|
|
310
|
+
console.error(error);
|
|
311
|
+
res.error(500);
|
|
312
|
+
} else {
|
|
313
|
+
next();
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
// ✅ CORRECT: Global error handling should be done with route patterns
|
|
319
|
+
app.use("/(.*)", (error, req, res, next) => {
|
|
320
|
+
if (error) {
|
|
321
|
+
console.error(`Global error for ${req.url}:`, error);
|
|
322
|
+
res.error(500, "Internal Server Error");
|
|
323
|
+
} else {
|
|
324
|
+
next();
|
|
325
|
+
}
|
|
326
|
+
});
|
|
301
327
|
```
|
|
302
328
|
|
|
329
|
+
**Important Notes:**
|
|
330
|
+
- **Error middleware** (functions with 4 parameters: `error, req, res, next`) should **never** be registered with `app.always()`
|
|
331
|
+
- Error middleware registered with `always` will execute **before** route handlers, making them ineffective for catching route errors
|
|
332
|
+
- **`.use()` without a method defaults to GET** - it behaves like `.get()`, not like `.always()`
|
|
333
|
+
- Always register error middleware **last** in the middleware chain for each route
|
|
334
|
+
- For global error handling, use `app.use("/(.*)", errorHandler)` as the **last route registration**
|
|
335
|
+
|
|
303
336
|
### Middleware Examples
|
|
304
337
|
|
|
305
338
|
```javascript
|
|
@@ -389,42 +422,72 @@ app.files("/", "./public");
|
|
|
389
422
|
|
|
390
423
|
## 🌐 CORS
|
|
391
424
|
|
|
392
|
-
|
|
425
|
+
**Woodland handles CORS automatically when you configure origins.** Here's what you get for free:
|
|
393
426
|
|
|
394
427
|
```javascript
|
|
395
428
|
const app = woodland({
|
|
396
429
|
origins: ["https://myapp.com", "https://api.myapp.com"],
|
|
397
430
|
corsExpose: "x-total-count,x-page-count"
|
|
398
431
|
});
|
|
432
|
+
|
|
433
|
+
// Woodland automatically provides:
|
|
434
|
+
// ✅ Preflight OPTIONS route for all paths
|
|
435
|
+
// ✅ Access-Control-Allow-Origin header (set to request origin if allowed)
|
|
436
|
+
// ✅ Access-Control-Allow-Credentials: true
|
|
437
|
+
// ✅ Access-Control-Allow-Methods (based on registered routes)
|
|
438
|
+
// ✅ Access-Control-Allow-Headers (for OPTIONS requests)
|
|
439
|
+
// ✅ Access-Control-Expose-Headers (for non-OPTIONS requests)
|
|
440
|
+
// ✅ Timing-Allow-Origin header
|
|
441
|
+
// ✅ Origin validation and security
|
|
399
442
|
```
|
|
400
443
|
|
|
401
|
-
###
|
|
444
|
+
### What Woodland Does Automatically
|
|
445
|
+
|
|
446
|
+
1. **Preflight Route Registration**: When origins are configured, Woodland automatically registers an OPTIONS handler that responds with 204 No Content
|
|
447
|
+
2. **CORS Headers**: For valid cross-origin requests, automatically sets all required CORS headers
|
|
448
|
+
3. **Origin Validation**: Checks request origin against configured allowed origins
|
|
449
|
+
4. **Method Detection**: Access-Control-Allow-Methods reflects actual registered routes
|
|
450
|
+
5. **Security**: Empty origins array denies all CORS requests by default
|
|
451
|
+
|
|
452
|
+
### Manual CORS Control (When Needed)
|
|
402
453
|
|
|
403
454
|
```javascript
|
|
404
|
-
//
|
|
455
|
+
// Override automatic behavior for specific routes
|
|
456
|
+
app.options("/api/special", (req, res) => {
|
|
457
|
+
res.header("access-control-allow-methods", "GET,POST"); // Restrict methods
|
|
458
|
+
res.header("access-control-allow-headers", "content-type"); // Restrict headers
|
|
459
|
+
res.header("access-control-max-age", "86400"); // Set cache duration
|
|
460
|
+
res.send("");
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
// Dynamic origin validation (replaces automatic validation)
|
|
405
464
|
app.always((req, res, next) => {
|
|
406
465
|
const origin = req.headers.origin;
|
|
407
|
-
const allowedOrigins = [
|
|
408
|
-
"https://myapp.com",
|
|
409
|
-
"https://admin.myapp.com"
|
|
410
|
-
];
|
|
411
466
|
|
|
412
|
-
|
|
467
|
+
// Custom logic for origin validation
|
|
468
|
+
if (isValidOriginForUser(origin, req.user)) {
|
|
413
469
|
res.header("access-control-allow-origin", origin);
|
|
414
470
|
}
|
|
415
471
|
|
|
416
472
|
next();
|
|
417
473
|
});
|
|
418
474
|
|
|
419
|
-
//
|
|
420
|
-
app
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
475
|
+
// Conditional CORS (disable automatic CORS, use manual)
|
|
476
|
+
const app = woodland({
|
|
477
|
+
origins: [] // Empty = no automatic CORS
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
app.always((req, res, next) => {
|
|
481
|
+
if (shouldAllowCORS(req)) {
|
|
482
|
+
res.header("access-control-allow-origin", req.headers.origin);
|
|
483
|
+
res.header("access-control-allow-credentials", "true");
|
|
484
|
+
}
|
|
485
|
+
next();
|
|
425
486
|
});
|
|
426
487
|
```
|
|
427
488
|
|
|
489
|
+
**Most applications only need to configure `origins` and `corsExpose` - manual CORS handling is rarely necessary.**
|
|
490
|
+
|
|
428
491
|
## ❌ Error Handling
|
|
429
492
|
|
|
430
493
|
### Built-in Error Handling
|
package/dist/cli.cjs
CHANGED
package/dist/woodland.cjs
CHANGED
package/dist/woodland.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @copyright 2025 Jason Mulligan <jason.mulligan@avoidwork.com>
|
|
5
5
|
* @license BSD-3-Clause
|
|
6
|
-
* @version 20.2.
|
|
6
|
+
* @version 20.2.3
|
|
7
7
|
*/
|
|
8
8
|
import {STATUS_CODES,METHODS}from'node:http';import {join,extname,resolve}from'node:path';import {EventEmitter}from'node:events';import {stat,readdir}from'node:fs/promises';import {readFileSync,createReadStream}from'node:fs';import {etag}from'tiny-etag';import {precise}from'precise';import {lru}from'tiny-lru';import {createRequire}from'node:module';import {fileURLToPath,URL}from'node:url';import {coerce}from'tiny-coerce';import mimeDb from'mime-db';const __dirname$1 = fileURLToPath(new URL(".", import.meta.url));
|
|
9
9
|
const require = createRequire(import.meta.url);
|