@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.
- package/README.md +32 -31
- package/SECURITY.md +2 -2
- package/dist/cjs/integrations/express/error-handler.js +4 -2
- package/dist/cjs/integrations/express/state.js +7 -20
- package/dist/cjs/integrations/express/status-observer.js +3 -9
- package/dist/cjs/integrations/nest/exception-filter.js +2 -1
- package/dist/cjs/integrations/nest/filter-wrapper.js +3 -1
- package/dist/cjs/integrations/nest/register.js +3 -1
- package/dist/cjs/integrations/request-context.js +15 -23
- package/dist/esm/integrations/express/error-handler.js +3 -1
- package/dist/esm/integrations/express/state.js +7 -20
- package/dist/esm/integrations/express/status-observer.js +3 -9
- package/dist/esm/integrations/nest/exception-filter.js +2 -1
- package/dist/esm/integrations/nest/filter-wrapper.js +3 -1
- package/dist/esm/integrations/nest/register.js +3 -1
- package/dist/esm/integrations/request-context.js +14 -23
- package/dist/types/integrations/express/status-observer.d.ts +1 -1
- package/dist/types/integrations/nest/exception-filter.d.ts +2 -2
- package/dist/types/integrations/request-context.d.ts +1 -0
- package/dist/types-cjs/core/client.d.cts +1 -1
- package/dist/types-cjs/core/config.d.cts +2 -2
- package/dist/types-cjs/core/diagnostics.d.cts +1 -1
- package/dist/types-cjs/core/fingerprint.d.cts +1 -1
- package/dist/types-cjs/core/group-store.d.cts +1 -1
- package/dist/types-cjs/core/incident-builder.d.cts +2 -2
- package/dist/types-cjs/core/incident-policy.d.cts +1 -1
- package/dist/types-cjs/core/notification-queue.d.cts +1 -1
- package/dist/types-cjs/core/process-monitor.d.cts +1 -1
- package/dist/types-cjs/core/prometheus.d.cts +1 -1
- package/dist/types-cjs/core/redact.d.cts +1 -1
- package/dist/types-cjs/core/runtime-monitor.d.cts +1 -1
- package/dist/types-cjs/index.d.cts +14 -14
- package/dist/types-cjs/integrations/express/error-handler.d.cts +2 -2
- package/dist/types-cjs/integrations/express/index.d.cts +8 -8
- package/dist/types-cjs/integrations/express/request-context.d.cts +2 -2
- package/dist/types-cjs/integrations/express/status-observer.d.cts +2 -2
- package/dist/types-cjs/integrations/nest/capture.d.cts +2 -2
- package/dist/types-cjs/integrations/nest/exception-filter.d.cts +5 -5
- package/dist/types-cjs/integrations/nest/filter-wrapper.d.cts +2 -2
- package/dist/types-cjs/integrations/nest/index.d.cts +9 -9
- package/dist/types-cjs/integrations/nest/module.d.cts +1 -1
- package/dist/types-cjs/integrations/nest/register.d.cts +4 -4
- package/dist/types-cjs/integrations/nest/request-context.d.cts +2 -2
- package/dist/types-cjs/integrations/request-context.d.cts +2 -1
- package/dist/types-cjs/notifiers/alert-payload.d.cts +2 -2
- package/dist/types-cjs/notifiers/console.d.cts +1 -1
- package/dist/types-cjs/notifiers/telegram-format.d.cts +1 -1
- package/dist/types-cjs/notifiers/telegram.d.cts +2 -2
- package/dist/types-cjs/notifiers/webhook-http.d.cts +1 -1
- package/dist/types-cjs/notifiers/webhook.d.cts +2 -2
- 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:**
|
|
5
|
+
> **Status:** Stable (`1.0.0`).
|
|
6
6
|
|
|
7
7
|
[](https://github.com/FutureWindAI/Wotchi/actions/workflows/ci.yml)
|
|
8
8
|
[](https://github.com/FutureWindAI/Wotchi/actions/workflows/codeql.yml)
|
|
9
|
-
[](https://github.com/FutureWindAI/Wotchi/blob/
|
|
11
|
-
[](https://www.npmjs.com/package/@futurewindai/wotchi)
|
|
10
|
+
[](https://github.com/FutureWindAI/Wotchi/blob/v1.0.0/LICENSE)
|
|
11
|
+
[](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
|
|
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
|
|
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
|
|
85
|
-
`0.
|
|
86
|
-
`60
|
|
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
|
-
|
|
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
|
|
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 24—for 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/
|
|
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/
|
|
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/
|
|
288
|
-
- [Examples](https://github.com/FutureWindAI/Wotchi/blob/
|
|
289
|
-
- [API reference](https://github.com/FutureWindAI/Wotchi/blob/
|
|
290
|
-
- [Configuration](https://github.com/FutureWindAI/Wotchi/blob/
|
|
291
|
-
- [Compatibility](https://github.com/FutureWindAI/Wotchi/blob/
|
|
292
|
-
- [Performance](https://github.com/FutureWindAI/Wotchi/blob/
|
|
293
|
-
- [Architecture](https://github.com/FutureWindAI/Wotchi/blob/
|
|
294
|
-
- [Roadmap](https://github.com/FutureWindAI/Wotchi/blob/
|
|
295
|
-
- [Troubleshooting](https://github.com/FutureWindAI/Wotchi/blob/
|
|
296
|
-
- [FAQ](https://github.com/FutureWindAI/Wotchi/blob/
|
|
297
|
-
- [Security and privacy](https://github.com/FutureWindAI/Wotchi/blob/
|
|
298
|
-
- [Threat model](https://github.com/FutureWindAI/Wotchi/blob/
|
|
299
|
-
- [Contributing](https://github.com/FutureWindAI/Wotchi/blob/
|
|
300
|
-
- [Changelog](https://github.com/FutureWindAI/Wotchi/blob/
|
|
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/
|
|
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/
|
|
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 `>=
|
|
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("
|
|
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,
|
|
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
|
|
6
|
-
const
|
|
5
|
+
const capturedRequests = new WeakSet();
|
|
6
|
+
const asRequestObject = (request) => typeof request === "object" && request !== null ? request : undefined;
|
|
7
7
|
function markExpressErrorCaptured(request) {
|
|
8
|
-
const
|
|
9
|
-
if (
|
|
8
|
+
const requestObject = asRequestObject(request);
|
|
9
|
+
if (requestObject === undefined) {
|
|
10
10
|
return;
|
|
11
11
|
}
|
|
12
|
-
|
|
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
|
|
21
|
-
|
|
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
|
-
...
|
|
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,
|
|
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,
|
|
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
|
|
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(
|
|
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
|
|
112
|
-
const 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,
|
|
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
|
|
2
|
-
const
|
|
1
|
+
const capturedRequests = new WeakSet();
|
|
2
|
+
const asRequestObject = (request) => typeof request === "object" && request !== null ? request : undefined;
|
|
3
3
|
export function markExpressErrorCaptured(request) {
|
|
4
|
-
const
|
|
5
|
-
if (
|
|
4
|
+
const requestObject = asRequestObject(request);
|
|
5
|
+
if (requestObject === undefined) {
|
|
6
6
|
return;
|
|
7
7
|
}
|
|
8
|
-
|
|
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
|
|
17
|
-
|
|
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
|
-
...
|
|
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,
|
|
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,
|
|
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
|
|
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(
|
|
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
|
|
108
|
-
const 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
|
|
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
|
-
|
|
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.
|
|
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.
|
|
2
|
-
import { WotchiConfigurationError } from "./errors.
|
|
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,3 +1,3 @@
|
|
|
1
|
-
import type { IncidentAlert, IncidentGroup } from "./types.
|
|
2
|
-
import type { IncidentPolicyDecision } from "./incident-policy.
|
|
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.
|
|
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 { WotchiClient } from "./types.
|
|
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.
|
|
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,15 +1,15 @@
|
|
|
1
|
-
import { createWotchi } from "./core/client.
|
|
2
|
-
import { registerWotchiProcessMonitor } from "./core/process-monitor.
|
|
3
|
-
import { createWotchiPrometheusExporter, PROMETHEUS_CONTENT_TYPE } from "./core/prometheus.
|
|
4
|
-
import { registerWotchiRuntimeWatcher } from "./core/runtime-monitor.
|
|
5
|
-
import { consoleNotifier } from "./notifiers/console.
|
|
6
|
-
import { telegramNotifier } from "./notifiers/telegram.
|
|
7
|
-
import { webhookNotifier } from "./notifiers/webhook.
|
|
8
|
-
export { WotchiConfigurationError } from "./core/errors.
|
|
9
|
-
export type { ProcessMonitorHandle } from "./core/process-monitor.
|
|
10
|
-
export type { NormalizedWotchiConfig } from "./core/config.
|
|
11
|
-
export type { WotchiPrometheusExporter } from "./core/prometheus.
|
|
12
|
-
export type { WotchiRuntimeWatcherHandle, WotchiRuntimeWatcherOptions, } from "./core/runtime-monitor.
|
|
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.
|
|
14
|
-
export type { WebhookRequestFunction, WebhookRequestOptions, WebhookResponse, } from "./notifiers/webhook-http.
|
|
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.
|
|
3
|
-
import { type ExpressWotchiOptions } from "./request-context.
|
|
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.
|
|
2
|
-
import { createExpressErrorHandler } from "./error-handler.
|
|
3
|
-
import type { ExpressWotchiOptions } from "./request-context.
|
|
4
|
-
export { consoleNotifier, createWotchi, telegramNotifier, webhookNotifier } from "../../index.
|
|
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.
|
|
6
|
-
export type { ExpressWotchiOptions } from "./request-context.
|
|
7
|
-
export type { WotchiStatusClass, WotchiStatusObserverOptions } from "./status-observer.
|
|
8
|
-
export { wotchiStatusObserver } from "./status-observer.
|
|
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.
|
|
3
|
-
import type { WotchiRequestContext } from "../../core/types.
|
|
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.
|
|
3
|
-
import type
|
|
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.
|
|
3
|
-
import { type NestWotchiOptions } from "./request-context.
|
|
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.
|
|
4
|
-
import type { NestExceptionFilterLike } from "./filter-wrapper.
|
|
5
|
-
import type { NestWotchiOptions } from "./request-context.
|
|
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
|
-
|
|
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.
|
|
3
|
-
import type { NestWotchiOptions } from "./request-context.
|
|
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.
|
|
2
|
-
export { withWotchiNestFilter } from "./filter-wrapper.
|
|
3
|
-
export { WotchiModule, WOTCHI_CLIENT } from "./module.
|
|
4
|
-
export { registerWotchiNest, registerWotchiNestStatusObserver } from "./register.
|
|
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.
|
|
6
|
-
export type { NestWotchiApplication, NestWotchiOptions } from "./register.
|
|
7
|
-
export type { NestExceptionFilterLike } from "./filter-wrapper.
|
|
8
|
-
export type { WotchiNestModuleOptions } from "./module.
|
|
9
|
-
export type { WotchiStatusClass, WotchiStatusObserverOptions } from "../express/status-observer.
|
|
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.
|
|
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.
|
|
3
|
-
import type { NestWotchiOptions } from "./request-context.
|
|
4
|
-
import type { WotchiStatusObserverOptions } from "../express/status-observer.
|
|
5
|
-
export type { NestWotchiOptions } from "./request-context.
|
|
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.
|
|
3
|
-
import type { WotchiRequestContext } from "../../core/types.
|
|
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.
|
|
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.
|
|
2
|
-
import type { IncidentAlert } from "../core/types.
|
|
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.
|
|
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.
|
|
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.
|
|
2
|
-
import { type TelegramRequestFunction } from "./telegram-http.
|
|
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 { WebhookNotifierOptions, WotchiNotifier } from "../core/types.
|
|
2
|
-
import { type WebhookRequestFunction } from "./webhook-http.
|
|
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": "
|
|
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": ">=
|
|
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": "^
|
|
132
|
-
"@nestjs/core": "^
|
|
133
|
-
"@nestjs/platform-express": "^
|
|
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": "^
|
|
135
|
+
"@types/node": "^22.20.1",
|
|
136
136
|
"eslint": "^10.8.0",
|
|
137
137
|
"express": "^5.2.1",
|
|
138
138
|
"prettier": "^3.9.6",
|