@futurewindai/wotchi 0.1.0-beta.6 → 1.0.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.
Files changed (51) hide show
  1. package/README.md +32 -31
  2. package/SECURITY.md +2 -2
  3. package/dist/cjs/integrations/express/error-handler.js +4 -2
  4. package/dist/cjs/integrations/express/state.js +7 -20
  5. package/dist/cjs/integrations/express/status-observer.js +3 -9
  6. package/dist/cjs/integrations/nest/exception-filter.js +2 -1
  7. package/dist/cjs/integrations/nest/filter-wrapper.js +3 -1
  8. package/dist/cjs/integrations/nest/register.js +3 -1
  9. package/dist/cjs/integrations/request-context.js +15 -23
  10. package/dist/esm/integrations/express/error-handler.js +3 -1
  11. package/dist/esm/integrations/express/state.js +7 -20
  12. package/dist/esm/integrations/express/status-observer.js +3 -9
  13. package/dist/esm/integrations/nest/exception-filter.js +2 -1
  14. package/dist/esm/integrations/nest/filter-wrapper.js +3 -1
  15. package/dist/esm/integrations/nest/register.js +3 -1
  16. package/dist/esm/integrations/request-context.js +14 -23
  17. package/dist/types/integrations/express/status-observer.d.ts +1 -1
  18. package/dist/types/integrations/nest/exception-filter.d.ts +2 -2
  19. package/dist/types/integrations/request-context.d.ts +1 -0
  20. package/dist/types-cjs/core/client.d.cts +1 -1
  21. package/dist/types-cjs/core/config.d.cts +2 -2
  22. package/dist/types-cjs/core/diagnostics.d.cts +1 -1
  23. package/dist/types-cjs/core/fingerprint.d.cts +1 -1
  24. package/dist/types-cjs/core/group-store.d.cts +1 -1
  25. package/dist/types-cjs/core/incident-builder.d.cts +2 -2
  26. package/dist/types-cjs/core/incident-policy.d.cts +1 -1
  27. package/dist/types-cjs/core/notification-queue.d.cts +1 -1
  28. package/dist/types-cjs/core/process-monitor.d.cts +1 -1
  29. package/dist/types-cjs/core/prometheus.d.cts +1 -1
  30. package/dist/types-cjs/core/redact.d.cts +1 -1
  31. package/dist/types-cjs/core/runtime-monitor.d.cts +1 -1
  32. package/dist/types-cjs/index.d.cts +14 -14
  33. package/dist/types-cjs/integrations/express/error-handler.d.cts +2 -2
  34. package/dist/types-cjs/integrations/express/index.d.cts +8 -8
  35. package/dist/types-cjs/integrations/express/request-context.d.cts +2 -2
  36. package/dist/types-cjs/integrations/express/status-observer.d.cts +2 -2
  37. package/dist/types-cjs/integrations/nest/capture.d.cts +2 -2
  38. package/dist/types-cjs/integrations/nest/exception-filter.d.cts +5 -5
  39. package/dist/types-cjs/integrations/nest/filter-wrapper.d.cts +2 -2
  40. package/dist/types-cjs/integrations/nest/index.d.cts +9 -9
  41. package/dist/types-cjs/integrations/nest/module.d.cts +1 -1
  42. package/dist/types-cjs/integrations/nest/register.d.cts +4 -4
  43. package/dist/types-cjs/integrations/nest/request-context.d.cts +2 -2
  44. package/dist/types-cjs/integrations/request-context.d.cts +2 -1
  45. package/dist/types-cjs/notifiers/alert-payload.d.cts +2 -2
  46. package/dist/types-cjs/notifiers/console.d.cts +1 -1
  47. package/dist/types-cjs/notifiers/telegram-format.d.cts +1 -1
  48. package/dist/types-cjs/notifiers/telegram.d.cts +2 -2
  49. package/dist/types-cjs/notifiers/webhook-http.d.cts +1 -1
  50. package/dist/types-cjs/notifiers/webhook.d.cts +2 -2
  51. package/package.json +12 -12
package/README.md CHANGED
@@ -2,20 +2,21 @@
2
2
 
3
3
  > Low-noise error alerts for Node.js services.
4
4
 
5
- > **Status:** Public beta (`0.1.0-beta.6`). The API may evolve before the first stable release.
5
+ > **Status:** Stable (`1.0.0`).
6
6
 
7
7
  [![CI](https://github.com/FutureWindAI/Wotchi/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/FutureWindAI/Wotchi/actions/workflows/ci.yml)
8
8
  [![CodeQL](https://github.com/FutureWindAI/Wotchi/actions/workflows/codeql.yml/badge.svg?branch=main)](https://github.com/FutureWindAI/Wotchi/actions/workflows/codeql.yml)
9
- [![npm](https://img.shields.io/npm/v/%40futurewindai%2Fwotchi?label=npm%20beta)](https://www.npmjs.com/package/@futurewindai/wotchi)
10
- [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://github.com/FutureWindAI/Wotchi/blob/v0.1.0-beta.6/LICENSE)
11
- [![Node.js 18–26](https://img.shields.io/badge/node-18%E2%80%9326-339933?logo=node.js&logoColor=white)](https://nodejs.org/en/about/previous-releases)
9
+ [![npm](https://img.shields.io/npm/v/%40futurewindai%2Fwotchi?label=npm)](https://www.npmjs.com/package/@futurewindai/wotchi)
10
+ [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://github.com/FutureWindAI/Wotchi/blob/v1.0.0/LICENSE)
11
+ [![Node.js 22.14+](https://img.shields.io/badge/node-%3E%3D22.14.0-339933?logo=node.js&logoColor=white)](https://nodejs.org/en/about/previous-releases)
12
12
 
13
+ [Read the Wotchi story on Medium](https://medium.com/@alkazavr94/wotchi-low-noise-error-alerts-for-node-js-services-83cac7cf3f50)
13
14
  Wotchi captures application errors in-process, removes sensitive values, groups repeated failures, and delivers bounded console, Telegram, or HTTPS webhook alerts without changing framework response handling. It is a signal-conditioning layer, not a replacement for a full observability platform.
14
15
 
15
16
  ## Quick start
16
17
 
17
18
  ```bash
18
- npm install @futurewindai/wotchi@beta
19
+ npm install @futurewindai/wotchi
19
20
  ```
20
21
 
21
22
  ```ts
@@ -61,7 +62,7 @@ notifier.
61
62
  | Bounded capture and queueing | Repeated failures cannot create unbounded in-memory work. |
62
63
  | Redaction before processing | Sensitive values are removed before grouping, logging, or transmission. |
63
64
  | Grouping and cooldowns | Repeated failures produce a small number of useful alerts. |
64
- | Express 4/5 and NestJS 10/11 adapters | Errors are observed while the framework keeps response ownership. |
65
+ | Express 4/5 and NestJS 10/11/12 adapters | Errors are observed while the framework keeps response ownership. |
65
66
  | ESM, CommonJS, and TypeScript types | Use the package with common Node.js module setups. |
66
67
  | Console and optional Telegram notifiers | Start locally or self-host delivery without a Wotchi control plane. |
67
68
  | Generic HTTPS webhook notifier | Route bounded JSON alerts to an existing internal alert destination. |
@@ -81,12 +82,12 @@ durable incident history, cross-replica deduplication, dashboards, traces, or pa
81
82
  ## Performance evidence
82
83
 
83
84
  The package has zero direct runtime dependencies and keeps capture, grouping, and notification work
84
- bounded. On a Node.js 22/macOS arm64 benchmark run of beta.6, capture p95/p99 was
85
- `0.036 ms`/`0.036 ms`, duplicate-storm retained heap was `0.194 MiB`, and the packed artifact was
86
- `60,534 bytes`. Prometheus rendering p95 was `0.0048 ms`. The queue benchmark admitted `101` alerts while a notifier was blocked, capped
85
+ bounded. On a Node.js 22.14/macOS arm64 stable-v1 reference run, capture p95/p99 was
86
+ `0.0372 ms`/`0.0372 ms`, duplicate-storm retained heap was `0.192 MiB`, and the packed artifact was
87
+ about `60.8 KB`. Prometheus rendering p95 was `0.0058 ms`. The queue benchmark admitted `101` alerts while a notifier was blocked, capped
87
88
  pending work at `100`, and kept Express and NestJS responses completing. These are reproducible local
88
89
  measurements, not production guarantees; repeat `npm run benchmark` and `npm run benchmark:queue`
89
- on your runtime and workload. See [performance evidence](docs/PERFORMANCE.md).
90
+ on your runtime and workload. See [performance evidence](https://github.com/FutureWindAI/Wotchi/blob/v1.0.0/docs/PERFORMANCE.md).
90
91
 
91
92
  The exporter does not open a server or send data. Attach it to a protected endpoint owned
92
93
  by the host application:
@@ -101,7 +102,7 @@ app.get("/metrics", (_request, response) => {
101
102
  });
102
103
  ```
103
104
 
104
- Beta.6 also supports opt-in admission control, notifier timeouts/circuits, graceful shutdown, and a
105
+ Stable v1 also supports opt-in admission control, notifier timeouts/circuits, graceful shutdown, and a
105
106
  numeric-only runtime watcher.
106
107
 
107
108
  It emits only aggregate counters and queue/group gauges. Prometheus, Grafana, or another existing
@@ -162,9 +163,9 @@ export class AppModule {}
162
163
  ```
163
164
 
164
165
  For a custom dependency-injected `APP_FILTER`, keep its response and logging behavior, disable the
165
- automatic Wotchi filter, and wrap it with `withWotchiNestFilter`. See the [NestJS API](docs/API.md#nestjs-entry-point).
166
+ automatic Wotchi filter, and wrap it with `withWotchiNestFilter`. See the [NestJS API](https://github.com/FutureWindAI/Wotchi/blob/v1.0.0/docs/API.md#nestjs-entry-point).
166
167
 
167
- The package keeps compatibility with Node.js `>=18.18.0` and is tested across Node.js 18–26. Node.js 22 or 24 LTS is recommended for production. Express and NestJS adapters are optional subpath integrations, so applications only load the framework adapter they use.
168
+ The package requires Node.js `>=22.14.0` and tests the Node.js 22, 24, and 26 release lines. Use a maintained LTS line—Node.js 22 or 24for production. Express and NestJS adapters are optional subpath integrations, so applications only load the framework adapter they use.
168
169
 
169
170
  ## Telegram alerts
170
171
 
@@ -194,7 +195,7 @@ function requiredEnv(name: string): string {
194
195
  }
195
196
  ```
196
197
 
197
- Wotchi does not ship a shared bot token. Delivery is queued outside the request path and sends only the sanitized incident alert. See [configuration](https://github.com/FutureWindAI/Wotchi/blob/main/docs/CONFIGURATION.md) for notifier and security options.
198
+ Wotchi does not ship a shared bot token. Delivery is queued outside the request path and sends only the sanitized incident alert. See [configuration](https://github.com/FutureWindAI/Wotchi/blob/v1.0.0/docs/CONFIGURATION.md) for notifier and security options.
198
199
 
199
200
  ## HTTPS webhook alerts
200
201
 
@@ -260,7 +261,7 @@ Wotchi keeps grouping and cooldown state in one process. Replicas have independe
260
261
  reset groups, and a serverless instance can terminate before asynchronous delivery completes. An
261
262
  in-process SDK cannot reliably detect an OOM kill, frozen event loop, host failure, or unavailable
262
263
  network. Pair it with an external uptime monitor and keep graceful shutdown explicit; see the
263
- [production recipe](https://github.com/FutureWindAI/Wotchi/blob/main/examples/production-recipe/README.md).
264
+ [production recipe](https://github.com/FutureWindAI/Wotchi/blob/v1.0.0/examples/production-recipe/README.md).
264
265
 
265
266
  ## Process monitoring
266
267
 
@@ -284,25 +285,25 @@ const monitor = registerWotchiProcessMonitor(wotchi);
284
285
 
285
286
  ## Documentation
286
287
 
287
- - [Getting started](https://github.com/FutureWindAI/Wotchi/blob/main/docs/GETTING_STARTED.md)
288
- - [Examples](https://github.com/FutureWindAI/Wotchi/blob/main/docs/EXAMPLES.md)
289
- - [API reference](https://github.com/FutureWindAI/Wotchi/blob/main/docs/API.md)
290
- - [Configuration](https://github.com/FutureWindAI/Wotchi/blob/main/docs/CONFIGURATION.md)
291
- - [Compatibility](https://github.com/FutureWindAI/Wotchi/blob/main/docs/COMPATIBILITY.md)
292
- - [Performance](https://github.com/FutureWindAI/Wotchi/blob/main/docs/PERFORMANCE.md)
293
- - [Architecture](https://github.com/FutureWindAI/Wotchi/blob/main/docs/ARCHITECTURE.md)
294
- - [Roadmap](https://github.com/FutureWindAI/Wotchi/blob/main/docs/ROADMAP.md)
295
- - [Troubleshooting](https://github.com/FutureWindAI/Wotchi/blob/main/docs/TROUBLESHOOTING.md)
296
- - [FAQ](https://github.com/FutureWindAI/Wotchi/blob/main/docs/FAQ.md)
297
- - [Security and privacy](https://github.com/FutureWindAI/Wotchi/blob/main/docs/SECURITY.md)
298
- - [Threat model](https://github.com/FutureWindAI/Wotchi/blob/main/docs/THREAT_MODEL.md)
299
- - [Contributing](https://github.com/FutureWindAI/Wotchi/blob/main/CONTRIBUTING.md)
300
- - [Changelog](https://github.com/FutureWindAI/Wotchi/blob/main/CHANGELOG.md)
288
+ - [Getting started](https://github.com/FutureWindAI/Wotchi/blob/v1.0.0/docs/GETTING_STARTED.md)
289
+ - [Examples](https://github.com/FutureWindAI/Wotchi/blob/v1.0.0/docs/EXAMPLES.md)
290
+ - [API reference](https://github.com/FutureWindAI/Wotchi/blob/v1.0.0/docs/API.md)
291
+ - [Configuration](https://github.com/FutureWindAI/Wotchi/blob/v1.0.0/docs/CONFIGURATION.md)
292
+ - [Compatibility](https://github.com/FutureWindAI/Wotchi/blob/v1.0.0/docs/COMPATIBILITY.md)
293
+ - [Performance](https://github.com/FutureWindAI/Wotchi/blob/v1.0.0/docs/PERFORMANCE.md)
294
+ - [Architecture](https://github.com/FutureWindAI/Wotchi/blob/v1.0.0/docs/ARCHITECTURE.md)
295
+ - [Roadmap](https://github.com/FutureWindAI/Wotchi/blob/v1.0.0/docs/ROADMAP.md)
296
+ - [Troubleshooting](https://github.com/FutureWindAI/Wotchi/blob/v1.0.0/docs/TROUBLESHOOTING.md)
297
+ - [FAQ](https://github.com/FutureWindAI/Wotchi/blob/v1.0.0/docs/FAQ.md)
298
+ - [Security and privacy](https://github.com/FutureWindAI/Wotchi/blob/v1.0.0/docs/SECURITY.md)
299
+ - [Threat model](https://github.com/FutureWindAI/Wotchi/blob/v1.0.0/docs/THREAT_MODEL.md)
300
+ - [Contributing](https://github.com/FutureWindAI/Wotchi/blob/v1.0.0/CONTRIBUTING.md)
301
+ - [Changelog](https://github.com/FutureWindAI/Wotchi/blob/v1.0.0/CHANGELOG.md)
301
302
  - [GitHub releases](https://github.com/FutureWindAI/Wotchi/releases)
302
- - [Apache License 2.0](https://github.com/FutureWindAI/Wotchi/blob/main/LICENSE)
303
+ - [Apache License 2.0](https://github.com/FutureWindAI/Wotchi/blob/v1.0.0/LICENSE)
303
304
 
304
305
  ## Security
305
306
 
306
- Do not include real secrets or customer error data in issues, examples, or test fixtures. Report security vulnerabilities privately using the [security policy](https://github.com/FutureWindAI/Wotchi/blob/main/SECURITY.md).
307
+ Do not include real secrets or customer error data in issues, examples, or test fixtures. Report security vulnerabilities privately using the [security policy](https://github.com/FutureWindAI/Wotchi/blob/v1.0.0/SECURITY.md).
307
308
 
308
309
  Wotchi is open source and maintained by FutureWind AI.
package/SECURITY.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  ## Supported Versions
4
4
 
5
- Wotchi targets applications running Node.js `>=18.18.0` and is built and tested across the Node.js 18–26 matrix. Node.js versions below 18.18 are not supported.
5
+ Wotchi targets applications running Node.js `>=22.14.0` and tests the Node.js 22, 24, and 26 release lines. Node.js versions below 22.14 are not supported; use a maintained LTS line for production.
6
6
 
7
- The current package contains the framework-independent capture core, console, Telegram, and generic HTTPS webhook notifiers, Express 4/5 middleware, NestJS 10/11 exception-filter integration, optional HTTP status observation, and opt-in process monitoring. Supported runtime and framework combinations are documented after packed-tarball compatibility and release-security checks.
7
+ The current package contains the framework-independent capture core, console, Telegram, and generic HTTPS webhook notifiers, Express 4/5 middleware, NestJS 10/11/12 exception-filter integration, optional HTTP status observation, and opt-in process monitoring. Supported runtime and framework combinations are documented after packed-tarball compatibility and release-security checks.
8
8
 
9
9
  ## Reporting a Vulnerability
10
10
 
@@ -1,9 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createExpressErrorHandler = createExpressErrorHandler;
4
- const request_context_js_1 = require("./request-context.js");
4
+ const request_context_js_1 = require("../request-context.js");
5
+ const request_context_js_2 = require("./request-context.js");
5
6
  const state_js_1 = require("./state.js");
6
7
  function createExpressErrorHandler(client, options) {
8
+ const normalizedOptions = (0, request_context_js_1.normalizeRequestContextOptions)(options);
7
9
  return (error, request, response, next) => {
8
10
  (0, state_js_1.markExpressErrorCaptured)(request);
9
11
  let captured = false;
@@ -15,7 +17,7 @@ function createExpressErrorHandler(client, options) {
15
17
  response.off("finish", captureAfterResponse);
16
18
  response.off("close", captureAfterResponse);
17
19
  try {
18
- const requestContext = (0, request_context_js_1.getExpressRequestContext)(request, response, options);
20
+ const requestContext = (0, request_context_js_2.getExpressRequestContext)(request, response, normalizedOptions);
19
21
  client.captureException(error, undefined, requestContext === undefined ? undefined : { request: requestContext });
20
22
  }
21
23
  catch {
@@ -2,29 +2,16 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.markExpressErrorCaptured = markExpressErrorCaptured;
4
4
  exports.wasExpressErrorCaptured = wasExpressErrorCaptured;
5
- const WOTCHI_ERROR_CAPTURED = Symbol("wotchi.errorCaptured");
6
- const asMarkedRequest = (request) => typeof request === "object" && request !== null ? request : undefined;
5
+ const capturedRequests = new WeakSet();
6
+ const asRequestObject = (request) => typeof request === "object" && request !== null ? request : undefined;
7
7
  function markExpressErrorCaptured(request) {
8
- const markedRequest = asMarkedRequest(request);
9
- if (markedRequest === undefined) {
8
+ const requestObject = asRequestObject(request);
9
+ if (requestObject === undefined) {
10
10
  return;
11
11
  }
12
- try {
13
- markedRequest[WOTCHI_ERROR_CAPTURED] = true;
14
- }
15
- catch {
16
- // A request object can be frozen by host middleware; the observer remains best-effort.
17
- }
12
+ capturedRequests.add(requestObject);
18
13
  }
19
14
  function wasExpressErrorCaptured(request) {
20
- const markedRequest = asMarkedRequest(request);
21
- if (markedRequest === undefined) {
22
- return false;
23
- }
24
- try {
25
- return markedRequest[WOTCHI_ERROR_CAPTURED] === true;
26
- }
27
- catch {
28
- return false;
29
- }
15
+ const requestObject = asRequestObject(request);
16
+ return requestObject === undefined ? false : capturedRequests.has(requestObject);
30
17
  }
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.wotchiStatusObserver = wotchiStatusObserver;
4
4
  const request_context_js_1 = require("./request-context.js");
5
+ const request_context_js_2 = require("../request-context.js");
5
6
  const state_js_1 = require("./state.js");
6
7
  const DEFAULT_STATUS_CODES = [401, 403, 429];
7
8
  const DEFAULT_STATUS_CLASSES = ["5xx"];
@@ -38,16 +39,9 @@ const normalizeAlertThreshold = (value) => {
38
39
  };
39
40
  const normalizeOptions = (options) => {
40
41
  const alertThreshold = normalizeAlertThreshold(options?.alertThreshold);
42
+ const requestContextOptions = (0, request_context_js_2.normalizeRequestContextOptions)(options);
41
43
  return {
42
- ...(options?.requestIdProperty === undefined
43
- ? {}
44
- : { requestIdProperty: options.requestIdProperty }),
45
- ...(options?.correlationIdProperty === undefined
46
- ? {}
47
- : { correlationIdProperty: options.correlationIdProperty }),
48
- ...(options?.traceContextProperty === undefined
49
- ? {}
50
- : { traceContextProperty: options.traceContextProperty }),
44
+ ...requestContextOptions,
51
45
  statusCodes: normalizeStatuses(options?.statusCodes, DEFAULT_STATUS_CODES),
52
46
  statusClasses: normalizeStatusClasses(options?.statusClasses),
53
47
  ignoreStatusCodes: normalizeStatuses(options?.ignoreStatusCodes, []),
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.WotchiNestExceptionFilter = void 0;
13
13
  const common_1 = require("@nestjs/common");
14
14
  const core_1 = require("@nestjs/core");
15
+ const request_context_js_1 = require("../request-context.js");
15
16
  const capture_js_1 = require("./capture.js");
16
17
  const FILTER_CATCH_EXCEPTIONS = "__filterCatchExceptions__";
17
18
  const matchesException = (filter, exception) => {
@@ -27,8 +28,8 @@ let WotchiNestExceptionFilter = class WotchiNestExceptionFilter extends core_1.B
27
28
  constructor(client, applicationRef, options, previousGlobalFilters = []) {
28
29
  super(applicationRef);
29
30
  this.client = client;
30
- this.options = options;
31
31
  this.previousGlobalFilters = previousGlobalFilters;
32
+ this.options = (0, request_context_js_1.normalizeRequestContextOptions)(options);
32
33
  }
33
34
  catch(exception, host) {
34
35
  (0, capture_js_1.captureWotchiNestException)(this.client, exception, host, this.options);
@@ -1,14 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.withWotchiNestFilter = withWotchiNestFilter;
4
+ const request_context_js_1 = require("../request-context.js");
4
5
  const capture_js_1 = require("./capture.js");
5
6
  function withWotchiNestFilter(client, filter, options) {
7
+ const normalizedOptions = (0, request_context_js_1.normalizeRequestContextOptions)(options);
6
8
  const wrapped = Object.create(filter);
7
9
  Object.defineProperty(wrapped, "catch", {
8
10
  configurable: false,
9
11
  enumerable: false,
10
12
  value: (exception, host) => {
11
- (0, capture_js_1.captureWotchiNestException)(client, exception, host, options);
13
+ (0, capture_js_1.captureWotchiNestException)(client, exception, host, normalizedOptions);
12
14
  filter.catch(exception, host);
13
15
  },
14
16
  writable: false,
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.registerWotchiNest = registerWotchiNest;
4
4
  exports.registerWotchiNestStatusObserver = registerWotchiNestStatusObserver;
5
5
  const core_1 = require("@nestjs/core");
6
+ const request_context_js_1 = require("../request-context.js");
6
7
  const exception_filter_js_1 = require("./exception-filter.js");
7
8
  const status_observer_js_1 = require("../express/status-observer.js");
8
9
  const isNestExceptionFilter = (value) => typeof value === "object" &&
@@ -20,12 +21,13 @@ const getPreviousGlobalFilters = (application) => {
20
21
  .reverse();
21
22
  };
22
23
  function registerWotchiNest(app, client, options) {
24
+ const normalizedOptions = (0, request_context_js_1.normalizeRequestContextOptions)(options);
23
25
  const application = app;
24
26
  const directAdapter = application.getHttpAdapter?.();
25
27
  const httpAdapter = directAdapter === undefined
26
28
  ? application.get(core_1.HttpAdapterHost).httpAdapter
27
29
  : directAdapter;
28
- application.useGlobalFilters(new exception_filter_js_1.WotchiNestExceptionFilter(client, httpAdapter, options, getPreviousGlobalFilters(application)));
30
+ application.useGlobalFilters(new exception_filter_js_1.WotchiNestExceptionFilter(client, httpAdapter, normalizedOptions, getPreviousGlobalFilters(application)));
29
31
  }
30
32
  function registerWotchiNestStatusObserver(app, client, options) {
31
33
  const application = app;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.normalizeRoutePath = normalizeRoutePath;
4
+ exports.normalizeRequestContextOptions = normalizeRequestContextOptions;
4
5
  exports.buildRequestContext = buildRequestContext;
5
6
  const MAX_ROUTE_LENGTH = 500;
6
7
  const MAX_REQUEST_ID_LENGTH = 200;
@@ -24,33 +25,25 @@ function normalizeRoutePath(value) {
24
25
  }
25
26
  return path.split("/").map(dynamicSegment).join("/").slice(0, MAX_ROUTE_LENGTH);
26
27
  }
27
- const normalizeRequestIdProperty = (property) => {
28
+ const normalizePropertyName = (property, optionName) => {
28
29
  if (property === undefined) {
29
30
  return undefined;
30
31
  }
31
32
  if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(property)) {
32
- throw new TypeError("requestIdProperty must be a simple property name");
33
- }
34
- return property;
35
- };
36
- const normalizeTraceContextProperty = (property) => {
37
- if (property === undefined) {
38
- return undefined;
39
- }
40
- if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(property)) {
41
- throw new TypeError("traceContextProperty must be a simple property name");
42
- }
43
- return property;
44
- };
45
- const normalizeCorrelationIdProperty = (property) => {
46
- if (property === undefined) {
47
- return undefined;
48
- }
49
- if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(property)) {
50
- throw new TypeError("correlationIdProperty must be a simple property name");
33
+ throw new TypeError(`${optionName} must be a simple property name`);
51
34
  }
52
35
  return property;
53
36
  };
37
+ function normalizeRequestContextOptions(options) {
38
+ const requestIdProperty = normalizePropertyName(options?.requestIdProperty, "requestIdProperty");
39
+ const correlationIdProperty = normalizePropertyName(options?.correlationIdProperty, "correlationIdProperty");
40
+ const traceContextProperty = normalizePropertyName(options?.traceContextProperty, "traceContextProperty");
41
+ return Object.freeze({
42
+ ...(requestIdProperty === undefined ? {} : { requestIdProperty }),
43
+ ...(correlationIdProperty === undefined ? {} : { correlationIdProperty }),
44
+ ...(traceContextProperty === undefined ? {} : { traceContextProperty }),
45
+ });
46
+ }
54
47
  const readRequestId = (request, property) => {
55
48
  if (property === undefined || !isRecord(request)) {
56
49
  return undefined;
@@ -108,9 +101,8 @@ const readCorrelationId = (request, property) => {
108
101
  }
109
102
  };
110
103
  function buildRequestContext(input) {
111
- const requestIdProperty = normalizeRequestIdProperty(input.options?.requestIdProperty);
112
- const correlationIdProperty = normalizeCorrelationIdProperty(input.options?.correlationIdProperty);
113
- const traceContextProperty = normalizeTraceContextProperty(input.options?.traceContextProperty);
104
+ const options = normalizeRequestContextOptions(input.options);
105
+ const { requestIdProperty, correlationIdProperty, traceContextProperty } = options;
114
106
  let requestId;
115
107
  try {
116
108
  requestId = readRequestId(input.request, requestIdProperty);
@@ -1,6 +1,8 @@
1
+ import { normalizeRequestContextOptions } from "../request-context.js";
1
2
  import { getExpressRequestContext } from "./request-context.js";
2
3
  import { markExpressErrorCaptured } from "./state.js";
3
4
  export function createExpressErrorHandler(client, options) {
5
+ const normalizedOptions = normalizeRequestContextOptions(options);
4
6
  return (error, request, response, next) => {
5
7
  markExpressErrorCaptured(request);
6
8
  let captured = false;
@@ -12,7 +14,7 @@ export function createExpressErrorHandler(client, options) {
12
14
  response.off("finish", captureAfterResponse);
13
15
  response.off("close", captureAfterResponse);
14
16
  try {
15
- const requestContext = getExpressRequestContext(request, response, options);
17
+ const requestContext = getExpressRequestContext(request, response, normalizedOptions);
16
18
  client.captureException(error, undefined, requestContext === undefined ? undefined : { request: requestContext });
17
19
  }
18
20
  catch {
@@ -1,26 +1,13 @@
1
- const WOTCHI_ERROR_CAPTURED = Symbol("wotchi.errorCaptured");
2
- const asMarkedRequest = (request) => typeof request === "object" && request !== null ? request : undefined;
1
+ const capturedRequests = new WeakSet();
2
+ const asRequestObject = (request) => typeof request === "object" && request !== null ? request : undefined;
3
3
  export function markExpressErrorCaptured(request) {
4
- const markedRequest = asMarkedRequest(request);
5
- if (markedRequest === undefined) {
4
+ const requestObject = asRequestObject(request);
5
+ if (requestObject === undefined) {
6
6
  return;
7
7
  }
8
- try {
9
- markedRequest[WOTCHI_ERROR_CAPTURED] = true;
10
- }
11
- catch {
12
- // A request object can be frozen by host middleware; the observer remains best-effort.
13
- }
8
+ capturedRequests.add(requestObject);
14
9
  }
15
10
  export function wasExpressErrorCaptured(request) {
16
- const markedRequest = asMarkedRequest(request);
17
- if (markedRequest === undefined) {
18
- return false;
19
- }
20
- try {
21
- return markedRequest[WOTCHI_ERROR_CAPTURED] === true;
22
- }
23
- catch {
24
- return false;
25
- }
11
+ const requestObject = asRequestObject(request);
12
+ return requestObject === undefined ? false : capturedRequests.has(requestObject);
26
13
  }
@@ -1,4 +1,5 @@
1
1
  import { getExpressRequestContext } from "./request-context.js";
2
+ import { normalizeRequestContextOptions } from "../request-context.js";
2
3
  import { wasExpressErrorCaptured } from "./state.js";
3
4
  const DEFAULT_STATUS_CODES = [401, 403, 429];
4
5
  const DEFAULT_STATUS_CLASSES = ["5xx"];
@@ -35,16 +36,9 @@ const normalizeAlertThreshold = (value) => {
35
36
  };
36
37
  const normalizeOptions = (options) => {
37
38
  const alertThreshold = normalizeAlertThreshold(options?.alertThreshold);
39
+ const requestContextOptions = normalizeRequestContextOptions(options);
38
40
  return {
39
- ...(options?.requestIdProperty === undefined
40
- ? {}
41
- : { requestIdProperty: options.requestIdProperty }),
42
- ...(options?.correlationIdProperty === undefined
43
- ? {}
44
- : { correlationIdProperty: options.correlationIdProperty }),
45
- ...(options?.traceContextProperty === undefined
46
- ? {}
47
- : { traceContextProperty: options.traceContextProperty }),
41
+ ...requestContextOptions,
48
42
  statusCodes: normalizeStatuses(options?.statusCodes, DEFAULT_STATUS_CODES),
49
43
  statusClasses: normalizeStatusClasses(options?.statusClasses),
50
44
  ignoreStatusCodes: normalizeStatuses(options?.ignoreStatusCodes, []),
@@ -9,6 +9,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
9
9
  };
10
10
  import { Catch, HttpException } from "@nestjs/common";
11
11
  import { BaseExceptionFilter } from "@nestjs/core";
12
+ import { normalizeRequestContextOptions } from "../request-context.js";
12
13
  import { captureWotchiNestException } from "./capture.js";
13
14
  const FILTER_CATCH_EXCEPTIONS = "__filterCatchExceptions__";
14
15
  const matchesException = (filter, exception) => {
@@ -24,8 +25,8 @@ let WotchiNestExceptionFilter = class WotchiNestExceptionFilter extends BaseExce
24
25
  constructor(client, applicationRef, options, previousGlobalFilters = []) {
25
26
  super(applicationRef);
26
27
  this.client = client;
27
- this.options = options;
28
28
  this.previousGlobalFilters = previousGlobalFilters;
29
+ this.options = normalizeRequestContextOptions(options);
29
30
  }
30
31
  catch(exception, host) {
31
32
  captureWotchiNestException(this.client, exception, host, this.options);
@@ -1,11 +1,13 @@
1
+ import { normalizeRequestContextOptions } from "../request-context.js";
1
2
  import { captureWotchiNestException } from "./capture.js";
2
3
  export function withWotchiNestFilter(client, filter, options) {
4
+ const normalizedOptions = normalizeRequestContextOptions(options);
3
5
  const wrapped = Object.create(filter);
4
6
  Object.defineProperty(wrapped, "catch", {
5
7
  configurable: false,
6
8
  enumerable: false,
7
9
  value: (exception, host) => {
8
- captureWotchiNestException(client, exception, host, options);
10
+ captureWotchiNestException(client, exception, host, normalizedOptions);
9
11
  filter.catch(exception, host);
10
12
  },
11
13
  writable: false,
@@ -1,4 +1,5 @@
1
1
  import { HttpAdapterHost } from "@nestjs/core";
2
+ import { normalizeRequestContextOptions } from "../request-context.js";
2
3
  import { WotchiNestExceptionFilter } from "./exception-filter.js";
3
4
  import { wotchiStatusObserver } from "../express/status-observer.js";
4
5
  const isNestExceptionFilter = (value) => typeof value === "object" &&
@@ -16,12 +17,13 @@ const getPreviousGlobalFilters = (application) => {
16
17
  .reverse();
17
18
  };
18
19
  export function registerWotchiNest(app, client, options) {
20
+ const normalizedOptions = normalizeRequestContextOptions(options);
19
21
  const application = app;
20
22
  const directAdapter = application.getHttpAdapter?.();
21
23
  const httpAdapter = directAdapter === undefined
22
24
  ? application.get(HttpAdapterHost).httpAdapter
23
25
  : directAdapter;
24
- application.useGlobalFilters(new WotchiNestExceptionFilter(client, httpAdapter, options, getPreviousGlobalFilters(application)));
26
+ application.useGlobalFilters(new WotchiNestExceptionFilter(client, httpAdapter, normalizedOptions, getPreviousGlobalFilters(application)));
25
27
  }
26
28
  export function registerWotchiNestStatusObserver(app, client, options) {
27
29
  const application = app;
@@ -20,33 +20,25 @@ export function normalizeRoutePath(value) {
20
20
  }
21
21
  return path.split("/").map(dynamicSegment).join("/").slice(0, MAX_ROUTE_LENGTH);
22
22
  }
23
- const normalizeRequestIdProperty = (property) => {
23
+ const normalizePropertyName = (property, optionName) => {
24
24
  if (property === undefined) {
25
25
  return undefined;
26
26
  }
27
27
  if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(property)) {
28
- throw new TypeError("requestIdProperty must be a simple property name");
29
- }
30
- return property;
31
- };
32
- const normalizeTraceContextProperty = (property) => {
33
- if (property === undefined) {
34
- return undefined;
35
- }
36
- if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(property)) {
37
- throw new TypeError("traceContextProperty must be a simple property name");
38
- }
39
- return property;
40
- };
41
- const normalizeCorrelationIdProperty = (property) => {
42
- if (property === undefined) {
43
- return undefined;
44
- }
45
- if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(property)) {
46
- throw new TypeError("correlationIdProperty must be a simple property name");
28
+ throw new TypeError(`${optionName} must be a simple property name`);
47
29
  }
48
30
  return property;
49
31
  };
32
+ export function normalizeRequestContextOptions(options) {
33
+ const requestIdProperty = normalizePropertyName(options?.requestIdProperty, "requestIdProperty");
34
+ const correlationIdProperty = normalizePropertyName(options?.correlationIdProperty, "correlationIdProperty");
35
+ const traceContextProperty = normalizePropertyName(options?.traceContextProperty, "traceContextProperty");
36
+ return Object.freeze({
37
+ ...(requestIdProperty === undefined ? {} : { requestIdProperty }),
38
+ ...(correlationIdProperty === undefined ? {} : { correlationIdProperty }),
39
+ ...(traceContextProperty === undefined ? {} : { traceContextProperty }),
40
+ });
41
+ }
50
42
  const readRequestId = (request, property) => {
51
43
  if (property === undefined || !isRecord(request)) {
52
44
  return undefined;
@@ -104,9 +96,8 @@ const readCorrelationId = (request, property) => {
104
96
  }
105
97
  };
106
98
  export function buildRequestContext(input) {
107
- const requestIdProperty = normalizeRequestIdProperty(input.options?.requestIdProperty);
108
- const correlationIdProperty = normalizeCorrelationIdProperty(input.options?.correlationIdProperty);
109
- const traceContextProperty = normalizeTraceContextProperty(input.options?.traceContextProperty);
99
+ const options = normalizeRequestContextOptions(input.options);
100
+ const { requestIdProperty, correlationIdProperty, traceContextProperty } = options;
110
101
  let requestId;
111
102
  try {
112
103
  requestId = readRequestId(input.request, requestIdProperty);
@@ -1,6 +1,6 @@
1
1
  import type { RequestHandler } from "express";
2
2
  import type { WotchiClient } from "../../core/types.js";
3
- import type { RequestContextOptions } from "../request-context.js";
3
+ import { type RequestContextOptions } from "../request-context.js";
4
4
  export type WotchiStatusClass = "4xx" | "5xx";
5
5
  export interface WotchiStatusObserverOptions extends RequestContextOptions {
6
6
  statusCodes?: readonly number[];
@@ -5,9 +5,9 @@ import type { NestExceptionFilterLike } from "./filter-wrapper.js";
5
5
  import type { NestWotchiOptions } from "./request-context.js";
6
6
  export declare class WotchiNestExceptionFilter extends BaseExceptionFilter<unknown> {
7
7
  private readonly client;
8
- private readonly options?;
9
8
  private readonly previousGlobalFilters;
10
- constructor(client: WotchiClient, applicationRef?: HttpServer, options?: NestWotchiOptions | undefined, previousGlobalFilters?: readonly NestExceptionFilterLike[]);
9
+ private readonly options;
10
+ constructor(client: WotchiClient, applicationRef?: HttpServer, options?: NestWotchiOptions, previousGlobalFilters?: readonly NestExceptionFilterLike[]);
11
11
  catch(exception: unknown, host: ArgumentsHost): void;
12
12
  private respondWithoutLoggingRawUnknownException;
13
13
  private respondThroughExpressAdapter;
@@ -5,6 +5,7 @@ export interface RequestContextOptions {
5
5
  traceContextProperty?: string;
6
6
  }
7
7
  export declare function normalizeRoutePath(value: unknown): string | undefined;
8
+ export declare function normalizeRequestContextOptions(options?: RequestContextOptions): Readonly<RequestContextOptions>;
8
9
  export interface RequestContextInput {
9
10
  request?: unknown;
10
11
  method?: unknown;
@@ -1,2 +1,2 @@
1
- import type { WotchiClient, WotchiConfig } from "./types.js";
1
+ import type { WotchiClient, WotchiConfig } from "./types.cjs";
2
2
  export declare function createWotchi(config: WotchiConfig): WotchiClient;
@@ -1,5 +1,5 @@
1
- import type { WotchiConfig, WotchiIncidentRule, WotchiLinkTemplates, WotchiNotifier } from "./types.js";
2
- import { WotchiConfigurationError } from "./errors.js";
1
+ import type { WotchiConfig, WotchiIncidentRule, WotchiLinkTemplates, WotchiNotifier } from "./types.cjs";
2
+ import { WotchiConfigurationError } from "./errors.cjs";
3
3
  export { WotchiConfigurationError };
4
4
  export interface NormalizedWotchiConfig {
5
5
  readonly service: string;
@@ -1,4 +1,4 @@
1
- import type { WotchiDiagnostics } from "./types.js";
1
+ import type { WotchiDiagnostics } from "./types.cjs";
2
2
  export interface DiagnosticsState {
3
3
  capturedEvents: number;
4
4
  captureFailures: number;
@@ -1,3 +1,3 @@
1
- import type { SafeErrorEvent } from "./types.js";
1
+ import type { SafeErrorEvent } from "./types.cjs";
2
2
  export declare function normalizeDynamicSegments(value: string): string;
3
3
  export declare function fingerprintSafeErrorEvent(event: SafeErrorEvent): string;
@@ -1,4 +1,4 @@
1
- import type { IncidentGroup, IncidentSeverity, SafeErrorEvent } from "./types.js";
1
+ import type { IncidentGroup, IncidentSeverity, SafeErrorEvent } from "./types.cjs";
2
2
  export interface GroupStoreOptions {
3
3
  maxGroups: number;
4
4
  maxEventsPerWindow: number;
@@ -1,3 +1,3 @@
1
- import type { IncidentAlert, IncidentGroup } from "./types.js";
2
- import type { IncidentPolicyDecision } from "./incident-policy.js";
1
+ import type { IncidentAlert, IncidentGroup } from "./types.cjs";
2
+ import type { IncidentPolicyDecision } from "./incident-policy.cjs";
3
3
  export declare function buildIncidentAlert(group: IncidentGroup, decision: IncidentPolicyDecision): IncidentAlert;
@@ -1,4 +1,4 @@
1
- import type { IncidentGroup, IncidentSeverity } from "./types.js";
1
+ import type { IncidentGroup, IncidentSeverity } from "./types.cjs";
2
2
  export type IncidentEventKind = "error" | "manual" | "process-monitor" | "runtime-monitor";
3
3
  export interface IncidentPolicyInput {
4
4
  group: IncidentGroup;
@@ -1,4 +1,4 @@
1
- import type { IncidentAlert, WotchiNotifier } from "./types.js";
1
+ import type { IncidentAlert, WotchiNotifier } from "./types.cjs";
2
2
  export interface NotificationQueueOptions {
3
3
  maxPendingAlerts: number;
4
4
  concurrency: 1;
@@ -1,4 +1,4 @@
1
- import type { WotchiClient } from "./types.js";
1
+ import type { WotchiClient } from "./types.cjs";
2
2
  export interface ProcessMonitorHandle {
3
3
  unregister(): void;
4
4
  }
@@ -1,4 +1,4 @@
1
- import type { WotchiClient } from "./types.js";
1
+ import type { WotchiClient } from "./types.cjs";
2
2
  export declare const PROMETHEUS_CONTENT_TYPE = "text/plain; version=0.0.4; charset=utf-8";
3
3
  export interface WotchiPrometheusExporter {
4
4
  readonly contentType: typeof PROMETHEUS_CONTENT_TYPE;
@@ -1,4 +1,4 @@
1
- import type { NormalizedError, SafeNormalizedValue } from "./normalize.js";
1
+ import type { NormalizedError, SafeNormalizedValue } from "./normalize.cjs";
2
2
  export declare const REDACTED = "[REDACTED]";
3
3
  export interface RedactionOptions {
4
4
  redactKeys?: readonly string[];
@@ -1,4 +1,4 @@
1
- import type { WotchiClient } from "./types.js";
1
+ import type { WotchiClient } from "./types.cjs";
2
2
  export interface WotchiRuntimeWatcherOptions {
3
3
  intervalMs?: number;
4
4
  cpuPercent?: number;
@@ -1,15 +1,15 @@
1
- import { createWotchi } from "./core/client.js";
2
- import { registerWotchiProcessMonitor } from "./core/process-monitor.js";
3
- import { createWotchiPrometheusExporter, PROMETHEUS_CONTENT_TYPE } from "./core/prometheus.js";
4
- import { registerWotchiRuntimeWatcher } from "./core/runtime-monitor.js";
5
- import { consoleNotifier } from "./notifiers/console.js";
6
- import { telegramNotifier } from "./notifiers/telegram.js";
7
- import { webhookNotifier } from "./notifiers/webhook.js";
8
- export { WotchiConfigurationError } from "./core/errors.js";
9
- export type { ProcessMonitorHandle } from "./core/process-monitor.js";
10
- export type { NormalizedWotchiConfig } from "./core/config.js";
11
- export type { WotchiPrometheusExporter } from "./core/prometheus.js";
12
- export type { WotchiRuntimeWatcherHandle, WotchiRuntimeWatcherOptions, } from "./core/runtime-monitor.js";
13
- export type { ConsoleNotifierOptions, IncidentAlert, IncidentGroup, IncidentSeverity, SafeErrorEvent, TelegramNotifierOptions, WebhookNotifierOptions, WotchiBeforeSend, WotchiCaptureOptions, WotchiClient, WotchiConfig, WotchiDiagnostics, WotchiEventFilter, WotchiEventInput, WotchiEventKind, WotchiFingerprintOverride, WotchiFingerprintCallback, WotchiIncidentRule, WotchiLinkTemplates, WotchiLinks, WotchiNotifier, WotchiRequestContext, WotchiTags, WotchiTestAlertResult, WotchiTestAlertStatus, WotchiTraceContext, } from "./core/types.js";
14
- export type { WebhookRequestFunction, WebhookRequestOptions, WebhookResponse, } from "./notifiers/webhook-http.js";
1
+ import { createWotchi } from "./core/client.cjs";
2
+ import { registerWotchiProcessMonitor } from "./core/process-monitor.cjs";
3
+ import { createWotchiPrometheusExporter, PROMETHEUS_CONTENT_TYPE } from "./core/prometheus.cjs";
4
+ import { registerWotchiRuntimeWatcher } from "./core/runtime-monitor.cjs";
5
+ import { consoleNotifier } from "./notifiers/console.cjs";
6
+ import { telegramNotifier } from "./notifiers/telegram.cjs";
7
+ import { webhookNotifier } from "./notifiers/webhook.cjs";
8
+ export { WotchiConfigurationError } from "./core/errors.cjs";
9
+ export type { ProcessMonitorHandle } from "./core/process-monitor.cjs";
10
+ export type { NormalizedWotchiConfig } from "./core/config.cjs";
11
+ export type { WotchiPrometheusExporter } from "./core/prometheus.cjs";
12
+ export type { WotchiRuntimeWatcherHandle, WotchiRuntimeWatcherOptions, } from "./core/runtime-monitor.cjs";
13
+ export type { ConsoleNotifierOptions, IncidentAlert, IncidentGroup, IncidentSeverity, SafeErrorEvent, TelegramNotifierOptions, WebhookNotifierOptions, WotchiBeforeSend, WotchiCaptureOptions, WotchiClient, WotchiConfig, WotchiDiagnostics, WotchiEventFilter, WotchiEventInput, WotchiEventKind, WotchiFingerprintOverride, WotchiFingerprintCallback, WotchiIncidentRule, WotchiLinkTemplates, WotchiLinks, WotchiNotifier, WotchiRequestContext, WotchiTags, WotchiTestAlertResult, WotchiTestAlertStatus, WotchiTraceContext, } from "./core/types.cjs";
14
+ export type { WebhookRequestFunction, WebhookRequestOptions, WebhookResponse, } from "./notifiers/webhook-http.cjs";
15
15
  export { consoleNotifier, createWotchi, createWotchiPrometheusExporter, PROMETHEUS_CONTENT_TYPE, registerWotchiProcessMonitor, registerWotchiRuntimeWatcher, telegramNotifier, webhookNotifier, };
@@ -1,4 +1,4 @@
1
1
  import type { ErrorRequestHandler } from "express";
2
- import type { WotchiClient } from "../../core/types.js";
3
- import { type ExpressWotchiOptions } from "./request-context.js";
2
+ import type { WotchiClient } from "../../core/types.cjs";
3
+ import { type ExpressWotchiOptions } from "./request-context.cjs";
4
4
  export declare function createExpressErrorHandler(client: WotchiClient, options?: ExpressWotchiOptions): ErrorRequestHandler;
@@ -1,9 +1,9 @@
1
- import type { WotchiClient } from "../../core/types.js";
2
- import { createExpressErrorHandler } from "./error-handler.js";
3
- import type { ExpressWotchiOptions } from "./request-context.js";
4
- export { consoleNotifier, createWotchi, telegramNotifier, webhookNotifier } from "../../index.js";
5
- export type { ConsoleNotifierOptions, IncidentAlert, IncidentGroup, IncidentSeverity, SafeErrorEvent, TelegramNotifierOptions, WebhookNotifierOptions, WotchiBeforeSend, WotchiCaptureOptions, WotchiClient, WotchiConfig, WotchiDiagnostics, WotchiEventFilter, WotchiEventInput, WotchiFingerprintCallback, WotchiFingerprintOverride, WotchiIncidentRule, WotchiLinkTemplates, WotchiLinks, WotchiNotifier, WotchiRequestContext, WotchiTags, WotchiTestAlertResult, WotchiTestAlertStatus, WotchiTraceContext, } from "../../index.js";
6
- export type { ExpressWotchiOptions } from "./request-context.js";
7
- export type { WotchiStatusClass, WotchiStatusObserverOptions } from "./status-observer.js";
8
- export { wotchiStatusObserver } from "./status-observer.js";
1
+ import type { WotchiClient } from "../../core/types.cjs";
2
+ import { createExpressErrorHandler } from "./error-handler.cjs";
3
+ import type { ExpressWotchiOptions } from "./request-context.cjs";
4
+ export { consoleNotifier, createWotchi, telegramNotifier, webhookNotifier } from "../../index.cjs";
5
+ export type { ConsoleNotifierOptions, IncidentAlert, IncidentGroup, IncidentSeverity, SafeErrorEvent, TelegramNotifierOptions, WebhookNotifierOptions, WotchiBeforeSend, WotchiCaptureOptions, WotchiClient, WotchiConfig, WotchiDiagnostics, WotchiEventFilter, WotchiEventInput, WotchiFingerprintCallback, WotchiFingerprintOverride, WotchiIncidentRule, WotchiLinkTemplates, WotchiLinks, WotchiNotifier, WotchiRequestContext, WotchiTags, WotchiTestAlertResult, WotchiTestAlertStatus, WotchiTraceContext, } from "../../index.cjs";
6
+ export type { ExpressWotchiOptions } from "./request-context.cjs";
7
+ export type { WotchiStatusClass, WotchiStatusObserverOptions } from "./status-observer.cjs";
8
+ export { wotchiStatusObserver } from "./status-observer.cjs";
9
9
  export declare function wotchiErrorHandler(client: WotchiClient, options?: ExpressWotchiOptions): ReturnType<typeof createExpressErrorHandler>;
@@ -1,5 +1,5 @@
1
1
  import type { Request, Response } from "express";
2
- import { type RequestContextOptions } from "../request-context.js";
3
- import type { WotchiRequestContext } from "../../core/types.js";
2
+ import { type RequestContextOptions } from "../request-context.cjs";
3
+ import type { WotchiRequestContext } from "../../core/types.cjs";
4
4
  export type ExpressWotchiOptions = RequestContextOptions;
5
5
  export declare function getExpressRequestContext(request: Request, response: Response, options?: ExpressWotchiOptions): WotchiRequestContext | undefined;
@@ -1,6 +1,6 @@
1
1
  import type { RequestHandler } from "express";
2
- import type { WotchiClient } from "../../core/types.js";
3
- import type { RequestContextOptions } from "../request-context.js";
2
+ import type { WotchiClient } from "../../core/types.cjs";
3
+ import { type RequestContextOptions } from "../request-context.cjs";
4
4
  export type WotchiStatusClass = "4xx" | "5xx";
5
5
  export interface WotchiStatusObserverOptions extends RequestContextOptions {
6
6
  statusCodes?: readonly number[];
@@ -1,4 +1,4 @@
1
1
  import type { ArgumentsHost } from "@nestjs/common";
2
- import type { WotchiClient } from "../../core/types.js";
3
- import { type NestWotchiOptions } from "./request-context.js";
2
+ import type { WotchiClient } from "../../core/types.cjs";
3
+ import { type NestWotchiOptions } from "./request-context.cjs";
4
4
  export declare const captureWotchiNestException: (client: WotchiClient, exception: unknown, host: ArgumentsHost, options?: NestWotchiOptions) => void;
@@ -1,13 +1,13 @@
1
1
  import { type ArgumentsHost, type HttpServer } from "@nestjs/common";
2
2
  import { BaseExceptionFilter } from "@nestjs/core";
3
- import type { WotchiClient } from "../../core/types.js";
4
- import type { NestExceptionFilterLike } from "./filter-wrapper.js";
5
- import type { NestWotchiOptions } from "./request-context.js";
3
+ import type { WotchiClient } from "../../core/types.cjs";
4
+ import type { NestExceptionFilterLike } from "./filter-wrapper.cjs";
5
+ import type { NestWotchiOptions } from "./request-context.cjs";
6
6
  export declare class WotchiNestExceptionFilter extends BaseExceptionFilter<unknown> {
7
7
  private readonly client;
8
- private readonly options?;
9
8
  private readonly previousGlobalFilters;
10
- constructor(client: WotchiClient, applicationRef?: HttpServer, options?: NestWotchiOptions | undefined, previousGlobalFilters?: readonly NestExceptionFilterLike[]);
9
+ private readonly options;
10
+ constructor(client: WotchiClient, applicationRef?: HttpServer, options?: NestWotchiOptions, previousGlobalFilters?: readonly NestExceptionFilterLike[]);
11
11
  catch(exception: unknown, host: ArgumentsHost): void;
12
12
  private respondWithoutLoggingRawUnknownException;
13
13
  private respondThroughExpressAdapter;
@@ -1,6 +1,6 @@
1
1
  import type { ArgumentsHost } from "@nestjs/common";
2
- import type { WotchiClient } from "../../core/types.js";
3
- import type { NestWotchiOptions } from "./request-context.js";
2
+ import type { WotchiClient } from "../../core/types.cjs";
3
+ import type { NestWotchiOptions } from "./request-context.cjs";
4
4
  export interface NestExceptionFilterLike {
5
5
  catch(exception: unknown, host: ArgumentsHost): void;
6
6
  }
@@ -1,9 +1,9 @@
1
- export { consoleNotifier, createWotchi, telegramNotifier, webhookNotifier } from "../../index.js";
2
- export { withWotchiNestFilter } from "./filter-wrapper.js";
3
- export { WotchiModule, WOTCHI_CLIENT } from "./module.js";
4
- export { registerWotchiNest, registerWotchiNestStatusObserver } from "./register.js";
5
- export type { ConsoleNotifierOptions, IncidentAlert, IncidentGroup, IncidentSeverity, SafeErrorEvent, TelegramNotifierOptions, WebhookNotifierOptions, WotchiBeforeSend, WotchiCaptureOptions, WotchiClient, WotchiConfig, WotchiDiagnostics, WotchiEventFilter, WotchiEventInput, WotchiFingerprintCallback, WotchiFingerprintOverride, WotchiIncidentRule, WotchiLinkTemplates, WotchiLinks, WotchiNotifier, WotchiRequestContext, WotchiTags, WotchiTestAlertResult, WotchiTestAlertStatus, WotchiTraceContext, } from "../../index.js";
6
- export type { NestWotchiApplication, NestWotchiOptions } from "./register.js";
7
- export type { NestExceptionFilterLike } from "./filter-wrapper.js";
8
- export type { WotchiNestModuleOptions } from "./module.js";
9
- export type { WotchiStatusClass, WotchiStatusObserverOptions } from "../express/status-observer.js";
1
+ export { consoleNotifier, createWotchi, telegramNotifier, webhookNotifier } from "../../index.cjs";
2
+ export { withWotchiNestFilter } from "./filter-wrapper.cjs";
3
+ export { WotchiModule, WOTCHI_CLIENT } from "./module.cjs";
4
+ export { registerWotchiNest, registerWotchiNestStatusObserver } from "./register.cjs";
5
+ export type { ConsoleNotifierOptions, IncidentAlert, IncidentGroup, IncidentSeverity, SafeErrorEvent, TelegramNotifierOptions, WebhookNotifierOptions, WotchiBeforeSend, WotchiCaptureOptions, WotchiClient, WotchiConfig, WotchiDiagnostics, WotchiEventFilter, WotchiEventInput, WotchiFingerprintCallback, WotchiFingerprintOverride, WotchiIncidentRule, WotchiLinkTemplates, WotchiLinks, WotchiNotifier, WotchiRequestContext, WotchiTags, WotchiTestAlertResult, WotchiTestAlertStatus, WotchiTraceContext, } from "../../index.cjs";
6
+ export type { NestWotchiApplication, NestWotchiOptions } from "./register.cjs";
7
+ export type { NestExceptionFilterLike } from "./filter-wrapper.cjs";
8
+ export type { WotchiNestModuleOptions } from "./module.cjs";
9
+ export type { WotchiStatusClass, WotchiStatusObserverOptions } from "../express/status-observer.cjs";
@@ -1,5 +1,5 @@
1
1
  import { type DynamicModule } from "@nestjs/common";
2
- import type { WotchiConfig } from "../../core/types.js";
2
+ import type { WotchiConfig } from "../../core/types.cjs";
3
3
  export declare const WOTCHI_CLIENT: unique symbol;
4
4
  export interface WotchiNestModuleOptions {
5
5
  registerGlobalFilter?: boolean;
@@ -1,8 +1,8 @@
1
1
  import { HttpAdapterHost } from "@nestjs/core";
2
- import type { WotchiClient } from "../../core/types.js";
3
- import type { NestWotchiOptions } from "./request-context.js";
4
- import type { WotchiStatusObserverOptions } from "../express/status-observer.js";
5
- export type { NestWotchiOptions } from "./request-context.js";
2
+ import type { WotchiClient } from "../../core/types.cjs";
3
+ import type { NestWotchiOptions } from "./request-context.cjs";
4
+ import type { WotchiStatusObserverOptions } from "../express/status-observer.cjs";
5
+ export type { NestWotchiOptions } from "./request-context.cjs";
6
6
  export interface NestWotchiApplication {
7
7
  get(token: typeof HttpAdapterHost): InstanceType<typeof HttpAdapterHost>;
8
8
  getHttpAdapter?: () => unknown;
@@ -1,5 +1,5 @@
1
1
  import type { ArgumentsHost } from "@nestjs/common";
2
- import { type RequestContextOptions } from "../request-context.js";
3
- import type { WotchiRequestContext } from "../../core/types.js";
2
+ import { type RequestContextOptions } from "../request-context.cjs";
3
+ import type { WotchiRequestContext } from "../../core/types.cjs";
4
4
  export type NestWotchiOptions = RequestContextOptions;
5
5
  export declare function getNestRequestContext(host: ArgumentsHost, exception: unknown, options?: NestWotchiOptions): WotchiRequestContext | undefined;
@@ -1,10 +1,11 @@
1
- import type { WotchiRequestContext } from "../core/types.js";
1
+ import type { WotchiRequestContext } from "../core/types.cjs";
2
2
  export interface RequestContextOptions {
3
3
  requestIdProperty?: string;
4
4
  correlationIdProperty?: string;
5
5
  traceContextProperty?: string;
6
6
  }
7
7
  export declare function normalizeRoutePath(value: unknown): string | undefined;
8
+ export declare function normalizeRequestContextOptions(options?: RequestContextOptions): Readonly<RequestContextOptions>;
8
9
  export interface RequestContextInput {
9
10
  request?: unknown;
10
11
  method?: unknown;
@@ -1,5 +1,5 @@
1
- import type { SafeNormalizedValue } from "../core/normalize.js";
2
- import type { IncidentAlert } from "../core/types.js";
1
+ import type { SafeNormalizedValue } from "../core/normalize.cjs";
2
+ import type { IncidentAlert } from "../core/types.cjs";
3
3
  export type BoundedAlertPayload = Record<string, SafeNormalizedValue>;
4
4
  export declare function toBoundedPayload(value: unknown): BoundedAlertPayload;
5
5
  export declare function toBoundedAlertPayload(alert: IncidentAlert): BoundedAlertPayload;
@@ -1,3 +1,3 @@
1
- import type { ConsoleNotifierOptions, IncidentAlert, WotchiNotifier } from "../core/types.js";
1
+ import type { ConsoleNotifierOptions, IncidentAlert, WotchiNotifier } from "../core/types.cjs";
2
2
  export declare function formatConsoleAlert(alert: IncidentAlert, format?: "text" | "json"): string;
3
3
  export declare function consoleNotifier(options?: ConsoleNotifierOptions): WotchiNotifier;
@@ -1,2 +1,2 @@
1
- import type { IncidentAlert } from "../core/types.js";
1
+ import type { IncidentAlert } from "../core/types.cjs";
2
2
  export declare function formatTelegramAlert(alert: IncidentAlert): string;
@@ -1,4 +1,4 @@
1
- import type { TelegramNotifierOptions, WotchiNotifier } from "../core/types.js";
2
- import { type TelegramRequestFunction } from "./telegram-http.js";
1
+ import type { TelegramNotifierOptions, WotchiNotifier } from "../core/types.cjs";
2
+ import { type TelegramRequestFunction } from "./telegram-http.cjs";
3
3
  export declare function createTelegramNotifier(options: TelegramNotifierOptions, request?: TelegramRequestFunction): WotchiNotifier;
4
4
  export declare function telegramNotifier(options: TelegramNotifierOptions): WotchiNotifier;
@@ -1,4 +1,4 @@
1
- import type { IncidentAlert, WebhookNotifierOptions } from "../core/types.js";
1
+ import type { IncidentAlert, WebhookNotifierOptions } from "../core/types.cjs";
2
2
  export interface WebhookRequestOptions {
3
3
  protocol: "http:" | "https:";
4
4
  hostname: string;
@@ -1,4 +1,4 @@
1
- import type { WebhookNotifierOptions, WotchiNotifier } from "../core/types.js";
2
- import { type WebhookRequestFunction } from "./webhook-http.js";
1
+ import type { WebhookNotifierOptions, WotchiNotifier } from "../core/types.cjs";
2
+ import { type WebhookRequestFunction } from "./webhook-http.cjs";
3
3
  export declare function createWebhookNotifier(options: WebhookNotifierOptions, request?: WebhookRequestFunction): WotchiNotifier;
4
4
  export declare function webhookNotifier(options: WebhookNotifierOptions): WotchiNotifier;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@futurewindai/wotchi",
3
- "version": "0.1.0-beta.6",
3
+ "version": "1.0.0",
4
4
  "description": "Low-noise error alerts for Node.js, Express, and NestJS applications",
5
5
  "keywords": [
6
6
  "nodejs",
@@ -17,7 +17,7 @@
17
17
  "license": "Apache-2.0",
18
18
  "type": "module",
19
19
  "engines": {
20
- "node": ">=18.18.0"
20
+ "node": ">=22.14.0"
21
21
  },
22
22
  "sideEffects": false,
23
23
  "publishConfig": {
@@ -86,16 +86,16 @@
86
86
  "stand": "node scripts/start-test-stand.mjs",
87
87
  "stand:production": "node scripts/start-nest-production-stand.mjs",
88
88
  "typecheck": "tsc -p tsconfig.base.json --noEmit",
89
- "lint": "eslint .",
90
- "format": "prettier --write .",
91
- "format:check": "prettier --check .",
89
+ "lint": "eslint src test scripts examples eslint.config.mjs",
90
+ "format": "prettier --write .github CHANGELOG.md CODE_OF_CONDUCT.md CONTRIBUTING.md README.md SECURITY.md docs examples eslint.config.mjs package-lock.json package.json scripts src test tsconfig.base.json tsconfig.cjs.json tsconfig.esm.json tsconfig.test.json tsconfig.types.json",
91
+ "format:check": "prettier --check .github CHANGELOG.md CODE_OF_CONDUCT.md CONTRIBUTING.md README.md SECURITY.md docs examples eslint.config.mjs package-lock.json package.json scripts src test tsconfig.base.json tsconfig.cjs.json tsconfig.esm.json tsconfig.test.json tsconfig.types.json",
92
92
  "test:compile": "tsc -p tsconfig.test.json",
93
93
  "test": "npm run test:compile && node --test .test-dist/test/unit/*.test.js .test-dist/test/integration/express/*.test.js .test-dist/test/integration/nest/*.test.js .test-dist/test/security/*.test.js",
94
94
  "pack:dry-run": "npm run build && npm pack --dry-run --json",
95
95
  "verify:package": "npm run build && node scripts/verify-package.mjs",
96
96
  "verify:published-metadata": "node scripts/verify-published-metadata.mjs",
97
97
  "test:compat": "npm run build && node test/compatibility/package-exports.smoke.cjs && node test/compatibility/framework-isolation.cjs",
98
- "test:compat:matrix": "npm run build && node scripts/run-compatibility-matrix.mjs",
98
+ "test:compat:matrix": "npm run build && node scripts/run-compatibility-matrix.mjs --framework all --module both",
99
99
  "benchmark": "npm run build && node --expose-gc scripts/run-benchmarks.mjs",
100
100
  "benchmark:queue": "npm run test:compile && node --test .test-dist/test/performance/queue-saturation.bench.js",
101
101
  "security:audit": "npm audit --audit-level=moderate",
@@ -103,8 +103,8 @@
103
103
  "security:all": "npm run security:audit && npm run security:osv"
104
104
  },
105
105
  "peerDependencies": {
106
- "@nestjs/common": "^10.4.0 || ^11.0.0",
107
- "@nestjs/core": "^10.4.0 || ^11.0.0",
106
+ "@nestjs/common": "^10.4.0 || ^11.0.0 || ^12.0.1",
107
+ "@nestjs/core": "^10.4.0 || ^11.0.0 || ^12.0.1",
108
108
  "express": "^4.18.0 || ^5.0.0",
109
109
  "reflect-metadata": "^0.2.2",
110
110
  "rxjs": "^7.8.0"
@@ -128,11 +128,11 @@
128
128
  },
129
129
  "devDependencies": {
130
130
  "@eslint/js": "^10.0.1",
131
- "@nestjs/common": "^11.1.28",
132
- "@nestjs/core": "^11.1.28",
133
- "@nestjs/platform-express": "^11.1.28",
131
+ "@nestjs/common": "^12.0.1",
132
+ "@nestjs/core": "^12.0.1",
133
+ "@nestjs/platform-express": "^12.0.1",
134
134
  "@types/express": "^5.0.6",
135
- "@types/node": "^16.18.126",
135
+ "@types/node": "^22.20.1",
136
136
  "eslint": "^10.8.0",
137
137
  "express": "^5.2.1",
138
138
  "prettier": "^3.9.6",