@forge-ops/tracker 0.1.0 → 0.1.1

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 (2) hide show
  1. package/README.md +26 -28
  2. package/package.json +7 -3
package/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # @forge-ops/tracker
2
2
 
3
3
  Node.js error reporting client for a private, self-hosted [ForgeOps](../../) tracker instance.
4
- Requires Node 18+ (for global `fetch`). A from-scratch port of
5
- [`gems/forge_ops_tracker`](../../gems/forge_ops_tracker) (the Rails client) -- see that gem's
6
- README for the shared design rationale; this document only covers what's Node-specific.
4
+ Requires Node 18+ (for global `fetch`). It captures uncaught exceptions, unhandled promise
5
+ rejections, and explicitly reported errors, builds a backtrace, scrubs likely PII, and delivers
6
+ events to ForgeOps over HTTP without blocking the request or process that raised them.
7
7
 
8
8
  ## Installation
9
9
 
@@ -76,8 +76,8 @@ try {
76
76
  }
77
77
  ```
78
78
 
79
- There's no Express/Fastify-wide equivalent to Rails' `Rails.error.handle` here -- report it
80
- explicitly instead, right at the catch site:
79
+ There's no application-wide hook that reports an exception while still letting your own catch
80
+ block handle it -- report it explicitly instead, right at the catch site:
81
81
 
82
82
  ```js
83
83
  } catch (err) {
@@ -108,35 +108,33 @@ dropped rather than thrown, so a broken or unreachable tracker can never take do
108
108
 
109
109
  ## Delivery: an async loop, not a thread
110
110
 
111
- `DeliveryQueue` here isn't a background *thread* the way the Ruby/.NET/Python clients' equivalent
112
- class is -- Node is single-threaded. But (unlike PHP-FPM) a Node process is long-running across many
113
- requests, so an async processing loop on the event loop is the natural substitute: `push()` returns
114
- immediately, and delivery happens via non-blocking `fetch()` calls without ever blocking the
115
- request that pushed it. The loop starts lazily, on first push, not at import time -- Node's
116
- `cluster` module can fork worker processes *after* the application has already loaded, the same
117
- hazard Puma/Gunicorn have for the Ruby/Python clients; starting fresh on first push means each
118
- forked worker gets its own live loop regardless of when it was forked relative to import.
111
+ `DeliveryQueue` here isn't a background *thread* -- Node is single-threaded. But a Node process is
112
+ long-running across many requests, so an async processing loop on the event loop is the natural
113
+ substitute: `push()` returns immediately, and delivery happens via non-blocking `fetch()` calls
114
+ without ever blocking the request that pushed it. The loop starts lazily, on first push, not at
115
+ import time -- Node's `cluster` module can fork worker processes *after* the application has
116
+ already loaded, and an eagerly-started loop would be left dead in every forked child; starting
117
+ fresh on first push means each forked worker gets its own live loop regardless of when it was
118
+ forked relative to import.
119
119
 
120
120
  ## `in_app` backtrace frames
121
121
 
122
- Like the Ruby gem (and unlike the .NET SDK, where a compiled assembly's file path never matches its
123
- original source location), Node runs interpreted directly from real `.js` files on disk, so
124
- file-path matching against `Configuration#appRoot` works the same way it does in the Ruby gem's
125
- `Rails.root` comparison. Defaults to the current working directory; set it explicitly if that
126
- doesn't match your app's actual layout. `node_modules` frames are never marked `in_app`, regardless
127
- of `appRoot`; Node's own internal modules (the `node:` scheme) never match a real `appRoot` prefix
128
- either, so they don't need special-casing.
129
-
130
- Backtrace parsing is a regex over `Error#stack` (a plain string in V8, unlike Python's structured
131
- `traceback` module or PHP's `Throwable::getTrace()`) -- verified directly against real captured
132
- stack traces, both synchronous and `async`, named and anonymous frames, before relying on it. A
133
- related quirk, shared with PHP but not Ruby/.NET/Python: V8 captures an Error's stack at
134
- *construction* time, not at `throw` time, so there's no "empty backtrace" case for an exception
135
- that's constructed but never thrown.
122
+ Node runs interpreted directly from real `.js` files on disk, so file-path matching against
123
+ `Configuration#appRoot` is a straightforward prefix comparison against those on-disk paths.
124
+ Defaults to the current working directory; set it explicitly if that doesn't match your app's
125
+ actual layout. `node_modules` frames are never marked `in_app`, regardless of `appRoot`; Node's own
126
+ internal modules (the `node:` scheme) never match a real `appRoot` prefix either, so they don't
127
+ need special-casing.
128
+
129
+ Backtrace parsing is a regex over `Error#stack`, a plain string in V8 rather than a structured
130
+ object -- verified directly against real captured stack traces, both synchronous and `async`,
131
+ named and anonymous frames, before relying on it. One quirk worth knowing: V8 captures an Error's
132
+ stack at *construction* time, not at `throw` time, so there's no "empty backtrace" case for an
133
+ exception that's constructed but never thrown.
136
134
 
137
135
  ## PII scrubbing
138
136
 
139
- Same behavior as the Ruby gem: the message, backtrace, and any context/tags you attach are scanned
137
+ By default, the message, backtrace, and any context/tags you attach are scanned
140
138
  for likely personal data -- email addresses, formatted SSNs/credit cards, known API key/token
141
139
  formats, and anything under a suspiciously-named key (`password`, `apiKey`, `ssn`, and similar) --
142
140
  and redacted before the payload ever leaves this process. ForgeOps itself scrubs again on arrival
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forge-ops/tracker",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "ForgeOps error tracking client: captures unhandled exceptions (Express/Fastify integration, plus explicit capture anywhere else) and delivers them to a ForgeOps instance over HTTP.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -31,8 +31,12 @@
31
31
  "fastify": ">=4"
32
32
  },
33
33
  "peerDependenciesMeta": {
34
- "express": { "optional": true },
35
- "fastify": { "optional": true }
34
+ "express": {
35
+ "optional": true
36
+ },
37
+ "fastify": {
38
+ "optional": true
39
+ }
36
40
  },
37
41
  "devDependencies": {
38
42
  "@eslint/js": "^10.0.0",