tackbox 0.1.65 → 0.1.66

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/js/README.md CHANGED
@@ -151,7 +151,7 @@ mode.
151
151
  ## Report helper
152
152
 
153
153
  ```js
154
- import { init, reportError, reportWarn, setupGlobalHandlers, flush } from 'tackbox/report'
154
+ import { init, reportError, reportWarn, flush } from 'tackbox/report'
155
155
 
156
156
  init({
157
157
  dsn: import.meta.env.VITE_SENTRY_DSN || '',
@@ -159,7 +159,6 @@ init({
159
159
  verify: true, // confirm connectivity at startup
160
160
  debug: false,
161
161
  })
162
- setupGlobalHandlers()
163
162
  // ... on shutdown:
164
163
  await flush(2000)
165
164
 
@@ -176,6 +175,22 @@ and stays log-only. `init({ verify: true })` sends one healthcheck
176
175
  event with `fingerprint: ["report.startup"]` and flushes; glitchtip
177
176
  groups all startups under one issue, no spam.
178
177
 
178
+ ### Global handlers
179
+
180
+ The helper installs no process-global hook. To route uncaught errors and
181
+ promise rejections into the report lane, the app owns the listeners:
182
+
183
+ ```js
184
+ window.addEventListener('error', e => {
185
+ reportError('uncaught global error', e.error || e.message,
186
+ { source: 'window.error' }, 'global.uncaught')
187
+ })
188
+ window.addEventListener('unhandledrejection', e => {
189
+ reportError('unhandled promise rejection', e.reason,
190
+ { source: 'window.unhandledrejection' }, 'global.unhandled')
191
+ })
192
+ ```
193
+
179
194
  ## Bundled API
180
195
 
181
196
  - `init(opts)`, `flush(timeout)`, `verify(timeout)`, `isReady()`
@@ -188,22 +203,20 @@ groups all startups under one issue, no spam.
188
203
  the user lost connectivity)
189
204
  - `reportSynthError(msg, tags, dedupKey)`
190
205
  - `reportPanic(name, recovered)`
191
- - `setupGlobalHandlers()` wires `window.error` and
192
- `window.unhandledrejection` to `reportError`
193
206
 
194
- The `tackbox:error` custom event is the user lane: it is dispatched on the
195
- window before the init + rate-window gate (and is never rate-limited) after
196
- each `reportError` / `reportWarn` / `notify` / `reportPanic` call, so a
197
- single top-level component can render a toast. `reportQuiet` does not
198
- dispatch it. The event `detail` carries `{ msg, cause, tags, dedupKey,
199
- level }`; the listener coalesces on `dedupKey`. Capture is gated behind init
200
- and the per-`dedupKey` rate window; the user lane is not.
207
+ Sink ordering, the never-suppressed user lane, and the per-`dedupKey` capture
208
+ rate window are the cross-language runtime contract:
209
+ [`../docs/report-contracts.md`](../docs/report-contracts.md). JS-specific: the
210
+ user lane is the `tackbox:error` custom event, dispatched on the window after
211
+ each `reportError` / `reportWarn` / `notify` / `reportPanic` call (`reportQuiet`
212
+ does not dispatch it). The event `detail` carries `{ msg, cause, tags, dedupKey,
213
+ level }`; the listener coalesces on `dedupKey`.
201
214
 
202
215
  Platform limit: a `tackbox:error` listener that throws is not observable from
203
216
  `dispatchEvent` - the browser routes a listener failure to `window.onerror` by
204
217
  design. So the JS user lane cannot capture its own listener's failure the way
205
218
  Go, Python, and Java capture a throwing `report.notifier`. A module-level
206
219
  re-entrancy guard stops the one loop this opens (a throwing listener reaching
207
- `window.onerror`, which `setupGlobalHandlers` turns back into
220
+ `window.onerror`, which an app-owned global handler turns back into
208
221
  `reportError` -> dispatch): a dispatch already in progress on the stack skips
209
222
  the nested one and logs locally instead. Sequential dispatches are unaffected.
package/js/report.js CHANGED
@@ -128,16 +128,6 @@ function reportPanic(name, recovered) {
128
128
  })
129
129
  }
130
130
 
131
- function setupGlobalHandlers() {
132
- if (typeof window === 'undefined') return
133
- window.addEventListener('error', e => {
134
- reportError('uncaught global error from window', e.error || e.message, { source: 'window.error' }, 'global.uncaught')
135
- })
136
- window.addEventListener('unhandledrejection', e => {
137
- reportError('unhandled promise rejection from window', e.reason, { source: 'window.unhandledrejection' }, 'global.unhandled')
138
- })
139
- }
140
-
141
131
  function maskDSN(dsn) {
142
132
  // no-report: malformed user DSN, opaque marker is the recovery
143
133
  try {
@@ -154,9 +144,9 @@ function dispatchEventSafely(name, detail) {
154
144
  if (typeof window === 'undefined' || typeof CustomEvent === 'undefined') return
155
145
  // Re-entrancy guard: a throwing `tackbox:error` listener surfaces via
156
146
  // window.onerror (the DOM routes listener failures there, not to dispatchEvent),
157
- // which setupGlobalHandlers turns back into reportError -> dispatch on the same
158
- // stack. Skip the nested dispatch so that cannot loop; sequential dispatches
159
- // are unaffected (D005 deliver-always intact).
147
+ // which an app-owned global handler can turn back into reportError -> dispatch
148
+ // on the same stack. Skip the nested dispatch so that cannot loop; sequential
149
+ // dispatches are unaffected (D005 deliver-always intact).
160
150
  if (dispatching) {
161
151
  console.warn('[tackbox] report: nested tackbox:error dispatch skipped (listener-failure re-entry)')
162
152
  return
@@ -183,5 +173,4 @@ module.exports = {
183
173
  reportSynthError,
184
174
  notify,
185
175
  reportPanic,
186
- setupGlobalHandlers,
187
176
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tackbox",
3
- "version": "0.1.65",
3
+ "version": "0.1.66",
4
4
  "description": "ESLint and Markdown lint plugins plus direct error-reporting helpers for JavaScript and TypeScript.",
5
5
  "license": "MIT",
6
6
  "main": "./js/eslint-plugin.js",