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 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
- // Error handling middleware
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
- ### Basic CORS
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
- ### Advanced CORS
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
- // Dynamic CORS
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
- if (allowedOrigins.includes(origin)) {
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
- // Preflight handling
420
- app.options("*", (req, res) => {
421
- res.header("access-control-allow-methods", "GET,POST,PUT,DELETE,OPTIONS");
422
- res.header("access-control-allow-headers", "content-type,authorization");
423
- res.header("access-control-max-age", "86400");
424
- res.send("");
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
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * @copyright 2025 Jason Mulligan <jason.mulligan@avoidwork.com>
6
6
  * @license BSD-3-Clause
7
- * @version 20.2.1
7
+ * @version 20.2.3
8
8
  */
9
9
  'use strict';
10
10
 
package/dist/woodland.cjs 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.1
6
+ * @version 20.2.3
7
7
  */
8
8
  'use strict';
9
9
 
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.1
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "woodland",
3
- "version": "20.2.1",
3
+ "version": "20.2.3",
4
4
  "description": "High-performance HTTP framework",
5
5
  "type": "module",
6
6
  "types": "types/woodland.d.ts",