woodland 22.1.1 → 22.2.0

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
@@ -155,6 +155,22 @@ app.use((error, req, res, next) => {
155
155
  });
156
156
  ```
157
157
 
158
+ ### Global Error Handling
159
+
160
+ Set `app.error` to intercept all unhandled errors before the error middleware chain:
161
+
162
+ ```javascript
163
+ const app = woodland();
164
+
165
+ app.error = (err, _req, res) => {
166
+ console.error("Unhandled error:", err);
167
+ res.status(500).send("Internal server error");
168
+ };
169
+ ```
170
+
171
+ The handler receives 3 arguments `(err, req, res)` and must terminate the request itself. When set, the error middleware chain is skipped.
172
+
173
+
158
174
  ## Configuration
159
175
 
160
176
  ```javascript
@@ -196,6 +212,7 @@ req.cors; // CORS enabled
196
212
  req.body; // Request body
197
213
  req.host; // Hostname
198
214
  req.valid; // Request validity
215
+ req.app; // Woodland application instance (provides access to app.error)
199
216
  ```
200
217
 
201
218
  ## Event Handlers
package/dist/cli.cjs CHANGED
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * @copyright 2026 Jason Mulligan <jason.mulligan@avoidwork.com>
6
6
  * @license BSD-3-Clause
7
- * @version 22.1.1
7
+ * @version 22.2.0
8
8
  */
9
9
  'use strict';
10
10
 
package/dist/woodland.cjs CHANGED
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * @copyright 2026 Jason Mulligan <jason.mulligan@avoidwork.com>
5
5
  * @license BSD-3-Clause
6
- * @version 22.1.1
6
+ * @version 22.2.0
7
7
  */
8
8
  'use strict';
9
9
 
@@ -165,6 +165,7 @@ const NEWLINE = "\n";
165
165
  const ROUTE_PATTERN = "(/.*)?";
166
166
  const MSG_USE_MIDDLEWARE_REQUIRED =
167
167
  "useMiddleware is required or config.use must be a function";
168
+ const MSG_MIDDLEWARE_REQUIRED = "Expected a function in the parameters";
168
169
  const EXTRACT_PATH_REPLACE = "(?<$1>[^/]+)";
169
170
  const TPL_DIR = "tpl";
170
171
  const INDEX_HTML_FILE = "index.html";
@@ -1163,6 +1164,10 @@ function next(req, res, middleware, immediate = false) {
1163
1164
  */
1164
1165
  const execute = (err) => {
1165
1166
  if (err !== void 0) {
1167
+ if (typeof req.app?.error === FUNCTION) {
1168
+ req.app.error(err, req, res);
1169
+ return;
1170
+ }
1166
1171
  handleError(err, execute);
1167
1172
  } else {
1168
1173
  handleMiddleware(execute);
@@ -1884,6 +1889,7 @@ class Woodland extends node_events.EventEmitter {
1884
1889
  #logger;
1885
1890
  #fileServer;
1886
1891
  #middleware;
1892
+ #error;
1887
1893
 
1888
1894
  /**
1889
1895
  * Creates a new Woodland instance
@@ -1930,6 +1936,7 @@ class Woodland extends node_events.EventEmitter {
1930
1936
  this.#logger = this.#createLogger();
1931
1937
  this.#fileServer = this.#createFileServer();
1932
1938
  this.#middleware = createMiddlewareRegistry(this.#methods, this.#cache);
1939
+ this.#error = null;
1933
1940
 
1934
1941
  this.#setupMiddleware();
1935
1942
  this.#setupErrorHandling();
@@ -2103,6 +2110,10 @@ class Woodland extends node_events.EventEmitter {
2103
2110
  * @returns {Woodland} Returns self for chaining
2104
2111
  */
2105
2112
  #registerMethod(method, ...args) {
2113
+ if (args.length === INT_1 && typeof args[INT_0] === STRING) {
2114
+ throw new TypeError(MSG_MIDDLEWARE_REQUIRED);
2115
+ }
2116
+
2106
2117
  return this.use(...args, method);
2107
2118
  }
2108
2119
 
@@ -2133,7 +2144,7 @@ class Woodland extends node_events.EventEmitter {
2133
2144
  req.host = parsed.hostname;
2134
2145
  req.params = {};
2135
2146
  req.valid = true;
2136
- req.app = { get: (key) => (key === "trust proxy" ? false : undefined) };
2147
+ req.app = this;
2137
2148
 
2138
2149
  const allowString = this.#allows(parsed.pathname);
2139
2150
  const headersBatch = Object.create(null);
@@ -2550,6 +2561,19 @@ class Woodland extends node_events.EventEmitter {
2550
2561
  get logger() {
2551
2562
  return this.#logger;
2552
2563
  }
2564
+
2565
+ /**
2566
+ * Global error handler property
2567
+ * @param {Function} [fn] - Error handler function
2568
+ * @returns {Function|null} Current error handler or null
2569
+ */
2570
+ get error() {
2571
+ return this.#error;
2572
+ }
2573
+
2574
+ set error(fn) {
2575
+ this.#error = typeof fn === FUNCTION ? fn : null;
2576
+ }
2553
2577
  }
2554
2578
 
2555
2579
  /**
package/dist/woodland.js CHANGED
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * @copyright 2026 Jason Mulligan <jason.mulligan@avoidwork.com>
5
5
  * @license BSD-3-Clause
6
- * @version 22.1.1
6
+ * @version 22.2.0
7
7
  */
8
8
  import {STATUS_CODES}from'node:http';import {EventEmitter}from'node:events';import {readFileSync,createReadStream}from'node:fs';import {etag}from'tiny-etag';import {lru}from'tiny-lru';import {precise}from'precise';import {createRequire}from'node:module';import {join,extname,resolve,sep}from'node:path';import {fileURLToPath,URL as URL$1}from'node:url';import mimeDb from'mime-db';import {coerce}from'tiny-coerce';import {Validator}from'jsonschema';import {realpath,stat,readdir}from'node:fs/promises';const __dirname$2 = fileURLToPath(new URL$1(".", import.meta.url));
9
9
  const require$1 = createRequire(import.meta.url);
@@ -148,6 +148,7 @@ const NEWLINE = "\n";
148
148
  const ROUTE_PATTERN = "(/.*)?";
149
149
  const MSG_USE_MIDDLEWARE_REQUIRED =
150
150
  "useMiddleware is required or config.use must be a function";
151
+ const MSG_MIDDLEWARE_REQUIRED = "Expected a function in the parameters";
151
152
  const EXTRACT_PATH_REPLACE = "(?<$1>[^/]+)";
152
153
  const TPL_DIR = "tpl";
153
154
  const INDEX_HTML_FILE = "index.html";
@@ -1140,6 +1141,10 @@ function next(req, res, middleware, immediate = false) {
1140
1141
  */
1141
1142
  const execute = (err) => {
1142
1143
  if (err !== void 0) {
1144
+ if (typeof req.app?.error === FUNCTION) {
1145
+ req.app.error(err, req, res);
1146
+ return;
1147
+ }
1143
1148
  handleError(err, execute);
1144
1149
  } else {
1145
1150
  handleMiddleware(execute);
@@ -1853,6 +1858,7 @@ class Woodland extends EventEmitter {
1853
1858
  #logger;
1854
1859
  #fileServer;
1855
1860
  #middleware;
1861
+ #error;
1856
1862
 
1857
1863
  /**
1858
1864
  * Creates a new Woodland instance
@@ -1899,6 +1905,7 @@ class Woodland extends EventEmitter {
1899
1905
  this.#logger = this.#createLogger();
1900
1906
  this.#fileServer = this.#createFileServer();
1901
1907
  this.#middleware = createMiddlewareRegistry(this.#methods, this.#cache);
1908
+ this.#error = null;
1902
1909
 
1903
1910
  this.#setupMiddleware();
1904
1911
  this.#setupErrorHandling();
@@ -2072,6 +2079,10 @@ class Woodland extends EventEmitter {
2072
2079
  * @returns {Woodland} Returns self for chaining
2073
2080
  */
2074
2081
  #registerMethod(method, ...args) {
2082
+ if (args.length === INT_1 && typeof args[INT_0] === STRING) {
2083
+ throw new TypeError(MSG_MIDDLEWARE_REQUIRED);
2084
+ }
2085
+
2075
2086
  return this.use(...args, method);
2076
2087
  }
2077
2088
 
@@ -2102,7 +2113,7 @@ class Woodland extends EventEmitter {
2102
2113
  req.host = parsed.hostname;
2103
2114
  req.params = {};
2104
2115
  req.valid = true;
2105
- req.app = { get: (key) => (key === "trust proxy" ? false : undefined) };
2116
+ req.app = this;
2106
2117
 
2107
2118
  const allowString = this.#allows(parsed.pathname);
2108
2119
  const headersBatch = Object.create(null);
@@ -2519,6 +2530,19 @@ class Woodland extends EventEmitter {
2519
2530
  get logger() {
2520
2531
  return this.#logger;
2521
2532
  }
2533
+
2534
+ /**
2535
+ * Global error handler property
2536
+ * @param {Function} [fn] - Error handler function
2537
+ * @returns {Function|null} Current error handler or null
2538
+ */
2539
+ get error() {
2540
+ return this.#error;
2541
+ }
2542
+
2543
+ set error(fn) {
2544
+ this.#error = typeof fn === FUNCTION ? fn : null;
2545
+ }
2522
2546
  }
2523
2547
 
2524
2548
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "woodland",
3
- "version": "22.1.1",
3
+ "version": "22.2.0",
4
4
  "description": "Secure HTTP framework for Node.js. Express-compatible with built-in security, no performance tradeoff.",
5
5
  "keywords": [
6
6
  "api",
@@ -49,6 +49,9 @@ export class Woodland extends EventEmitter {
49
49
  clf: (...args: any[]) => string;
50
50
  }>;
51
51
 
52
+ // Public error handler property (getter/setter)
53
+ error: ((err: Error, req: any, res: any) => void) | null;
54
+
52
55
  constructor(config?: WoodlandConfig);
53
56
 
54
57
  // Public routing methods - with path