{"_id":"@cancjs/unhandled-rejection","_rev":"2-80a036518c62ee389535d8a69eba3b34","name":"@cancjs/unhandled-rejection","dist-tags":{"beta":"1.0.0-beta.18","latest":"1.0.0"},"versions":{"1.0.0-beta.18":{"name":"@cancjs/unhandled-rejection","version":"1.0.0-beta.18","keywords":["unhandledrejection","unhandled","rejection","promise","cancelable","cancellation","cancel","canc"],"author":{"name":"bisubus"},"license":"MIT","_id":"@cancjs/unhandled-rejection@1.0.0-beta.18","maintainers":[{"name":"busybus","email":"busybus8@proton.me"}],"homepage":"https://github.com/cancjs/canc/tree/master/packages/canc-unhandled-rejection#readme","bugs":{"url":"https://github.com/cancjs/canc/issues"},"dist":{"shasum":"c25903a8e09ff0ce0047f8154e81b2e7fc8f97e3","tarball":"https://registry.npmjs.org/@cancjs/unhandled-rejection/-/unhandled-rejection-1.0.0-beta.18.tgz","fileCount":43,"integrity":"sha512-tYkZPTWQ68blgCod94x+pUcnPlMNP6kg7szXubLmGDoJHYMDGlJXFmE50MT5nZKDi0i9mzh3jiKvUE5lKm8adw==","signatures":[{"sig":"MEUCIQCw0CJ9jKo9+nJgNxRekoWTAJgo9YiWV750lXG6VHfidwIga3c01W6osXpddtEsVXAu9RhskgWhYN7Li2Ca/YP5XDM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":309054},"main":"dist/index.cjs","type":"commonjs","types":"dist/types/index.d.ts","unpkg":"dist/index.umd.min.js","module":"dist/index.mjs","engines":{"npm":">=9","node":">=18"},"exports":{".":{"import":{"types":"./dist/types/index.d.mts","default":"./dist/index.mjs","types@<4.7":"./dist/types-ts4.2/index.d.mts"},"require":{"types":"./dist/types/index.d.ts","default":"./dist/index.cjs","types@<4.7":"./dist/types-ts4.2/index.d.ts"}},"./register":{"import":{"types":"./dist/types/register.d.mts","default":"./dist/register.mjs","types@<4.7":"./dist/types-ts4.2/register.d.mts"},"require":{"types":"./dist/types/register.d.ts","default":"./dist/register.cjs","types@<4.7":"./dist/types-ts4.2/register.d.ts"}}},"gitHead":"17debd83ea144e803b99804d93e63d83291e505c","scripts":{"lint":"eslint .","test":"jest --colors","build":"rimraf dist && rollup --config ./rollup.config.mjs","check":"tsc --noEmit --project ./tsconfig.prod.json","format":"eslint --fix .","lint:src":"eslint ./src","format:src":"eslint --fix ./src","test:watch":"jest --colors --watch"},"_npmUser":{"name":"busybus","email":"busybus8@proton.me"},"jsdelivr":"dist/index.umd.min.js","repository":{"url":"git+https://github.com/cancjs/canc.git","type":"git","directory":"packages/canc-unhandled-rejection"},"_npmVersion":"11.6.3","description":"Global handler that silences CancelError rejections from @cancjs/promise.","directories":{},"sideEffects":["./dist/register.mjs","./dist/register.cjs"],"_nodeVersion":"24.18.1","publishConfig":{"access":"public"},"typesVersions":{"<4.7":{"dist/types/*":["dist/types-ts4.2/*"]}},"_hasShrinkwrap":false,"devDependencies":{"@cancjs/promise":"*"},"peerDependencies":{"@cancjs/promise":">=1.0.0"},"_npmOperationalInternal":{"tmp":"tmp/unhandled-rejection_1.0.0-beta.18_1785833272306_0.6004615940099518","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"name":"@cancjs/unhandled-rejection","version":"1.0.0","description":"Global handler that silences CancelError rejections from @cancjs/promise.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/cancjs/canc.git","directory":"packages/canc-unhandled-rejection"},"homepage":"https://github.com/cancjs/canc/tree/master/packages/canc-unhandled-rejection#readme","bugs":{"url":"https://github.com/cancjs/canc/issues"},"author":{"name":"bisubus"},"type":"commonjs","exports":{".":{"import":{"types@<4.7":"./dist/types-ts4.2/index.d.mts","types":"./dist/types/index.d.mts","default":"./dist/index.mjs"},"require":{"types@<4.7":"./dist/types-ts4.2/index.d.ts","types":"./dist/types/index.d.ts","default":"./dist/index.cjs"}},"./register":{"import":{"types@<4.7":"./dist/types-ts4.2/register.d.mts","types":"./dist/types/register.d.mts","default":"./dist/register.mjs"},"require":{"types@<4.7":"./dist/types-ts4.2/register.d.ts","types":"./dist/types/register.d.ts","default":"./dist/register.cjs"}}},"main":"dist/index.cjs","module":"dist/index.mjs","types":"dist/types/index.d.ts","sideEffects":["./dist/register.mjs","./dist/register.cjs"],"engines":{"node":">=18","npm":">=9"},"publishConfig":{"access":"public"},"scripts":{"build":"rimraf dist && rollup --config ./rollup.config.mjs","check":"tsc --noEmit --project ./tsconfig.prod.json","format:src":"eslint --fix ./src","format":"eslint --fix .","lint:src":"eslint ./src","lint":"eslint .","test":"jest --colors","test:watch":"jest --colors --watch"},"keywords":["unhandledrejection","unhandled","rejection","promise","cancelable","cancellation","cancel","canc"],"devDependencies":{"@cancjs/promise":"*"},"peerDependencies":{"@cancjs/promise":">=1.0.0"},"unpkg":"dist/index.umd.min.js","jsdelivr":"dist/index.umd.min.js","typesVersions":{"<4.7":{"dist/types/*":["dist/types-ts4.2/*"]},"*":{"register":["dist/types/register.d.ts"]}},"gitHead":"3fcdda94f6abbb8e1eb0cb8c27b14e2f4386faed","_id":"@cancjs/unhandled-rejection@1.0.0","_nodeVersion":"22.23.1","_npmVersion":"12.0.2","dist":{"integrity":"sha512-mKhqS10s6vr5n0bU9qnyPxDvxIyGY12N5Ku0WrhpHmbh/+ssMN1AzLFTTVeRC7J04LPJSgVXWxyrInPfxA6m1w==","shasum":"a124cb94d52ee1a5b6dd81469352e8f4ebef6fed","tarball":"https://registry.npmjs.org/@cancjs/unhandled-rejection/-/unhandled-rejection-1.0.0.tgz","fileCount":47,"unpackedSize":309611,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@cancjs%2funhandled-rejection@1.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDXrFtXSdBAdFfojm7XvCBsIkKsq8owRSOgbDMd5bSHSQIhANReQvFiDFekLc3Geq7QI1qxPsMQfdq1eeYwAjuFtG7r"}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:4240742f-4886-440e-9466-a6e5d3de1a19"}},"directories":{},"maintainers":[{"name":"busybus","email":"busybus8@proton.me"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/unhandled-rejection_1.0.0_1785883285665_0.7132219394083414"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-04T08:47:52.090Z","modified":"2026-08-04T22:41:26.313Z","1.0.0-beta.18":"2026-08-04T08:47:52.471Z","1.0.0":"2026-08-04T22:41:25.836Z"},"bugs":{"url":"https://github.com/cancjs/canc/issues"},"author":{"name":"bisubus"},"license":"MIT","homepage":"https://github.com/cancjs/canc/tree/master/packages/canc-unhandled-rejection#readme","keywords":["unhandledrejection","unhandled","rejection","promise","cancelable","cancellation","cancel","canc"],"repository":{"type":"git","url":"git+https://github.com/cancjs/canc.git","directory":"packages/canc-unhandled-rejection"},"description":"Global handler that silences CancelError rejections from @cancjs/promise.","maintainers":[{"name":"busybus","email":"busybus8@proton.me"}],"readme":"<div align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/cancjs/canc/master/assets/canc-logo.svg\" style=\"width: 400px; max-width: 100%; height: auto;\" title=\"canc &#x2BBF; A crafty foundation for cancelable promises\" alt=\"canc &#x2BBF; A crafty foundation for cancelable promises\">\n  <div>&nbsp;</div>\n</div>\n\n<h1 align=\"center\">@cancjs/unhandled-rejection</h1>\n\n<p align=\"center\">\nGlobal handler that silences CancelError rejections from @cancjs/promise.\n</p>\n\n---\n\n## Introduction\n\nA canceled promise rejects with a `CancelError` by design. When a canceled promise is left without a rejection handler, JavaScript runtimes trigger an `unhandledRejection` event. In Node.js 15 and later, unhandled rejections terminate the process with a non-zero exit code.\n\nThis package registers a global rejection handler that filters out `CancelError` rejections while letting real errors pass through to default environment error handling or custom application callbacks. Adding a single import at your entry point prevents expected cancellation flow from crashing your application.\n\n## Features\n\n- Automatic environment detection supporting Node.js, Deno, Bun, web browsers, Web Workers, Service Workers, Cloudflare Workers, and Electron\n- One-line side-effect import for zero-config global registration\n- Explicit registration API with process-specific and environment-specific methods\n- Optional suppression scope widening for `AbortError` and `TimeoutError`\n- Synchronous custom callback support for custom logging or telemetry forwarding\n- Per-call and global warning controls for diagnostic feedback\n- Environment-safe guards that never throw in non-supported or custom JavaScript environments\n\n## Getting Started\n\n### Installation\n\n```sh\nnpm install @cancjs/unhandled-rejection @cancjs/promise\n```\n\n`@cancjs/promise` is a peer dependency. This package is ecosystem tier: a minor release can carry a breaking change, so pin it with a tilde, `~1.4` (pin the minor, not `~1.x`, which npm expands to the same range as `^1`), rather than the default caret. See [Versioning](https://github.com/cancjs/canc/blob/master/docs/versioning.md) for the full policy.\n\n### Usage\n\nSide-effect registration (automatic environment detection):\n\n```js\nimport '@cancjs/unhandled-rejection/register';\n```\n\nExplicit programmatic registration:\n\n```js\nimport { register } from '@cancjs/unhandled-rejection';\n\nregister();\n```\n\nCustom callback for unhandled non-cancellation errors:\n\n```js\nimport { register } from '@cancjs/unhandled-rejection';\n\nregister({\n  onUnhandledRejection(reason, promise) {\n    logger.error('Unhandled promise rejection:', reason);\n  },\n});\n```\n\n## How It Works\n\n`@cancjs/unhandled-rejection` selects an environment-appropriate strategy during registration. In Node.js, Bun, and Electron main processes, it hooks `process.on('unhandledRejection')`. If the rejection reason is a `CancelError`, the handler returns silently. If the rejection is a real application error, the handler either delegates to your `onUnhandledRejection` callback or re-throws the error to preserve the runtime's default crash behavior.\n\nThe re-throw is not identical to the Node.js default. Without an `onUnhandledRejection` callback the handler throws the rejection reason from inside the listener, so the process still crashes, but it crashes with an uncaught exception rather than an `ERR_UNHANDLED_REJECTION` error. It also overrides `--unhandled-rejections=warn`: a process started with that flag would normally log and continue, and with this handler installed it exits. Pass `onUnhandledRejection` if you need to keep the process alive.\n\nIn web browsers, Deno, Web Workers, Service Workers, and Cloudflare Workers, it attaches an `unhandledrejection` event listener to `globalThis`. When a `CancelError` is encountered, the listener calls `event.preventDefault()` to prevent default browser console reporting or script termination. Non-cancellation rejections are passed to your custom callback (with `preventDefault()`) or left alone to let default runtime behavior proceed.\n\n## Description\n\n### Cancellation and unhandled rejection\n\nIn `@cancjs/promise`, cancellation is represented as a promise rejection using `CancelError`. When an operation is canceled, any pending promise chain rejects. If a consumer drops a promise reference or cancels an operation without attaching a `.catch()` or `catchCancel()` handler, the runtime receives an unhandled rejection event.\n\nTreating cancellation as rejection preserves standard `try`/`catch` control flow and async error propagation semantics. However, uncaught cancellation should not crash Node.js applications or pollute browser telemetry logs. `@cancjs/unhandled-rejection` provides global filtering to distinguish expected cancellation from actual runtime defects.\n\n### Environment detection\n\nThe `register()` function automatically detects your runtime environment in the following order:\n\n| Environment        | Detection Rule                | Registration Strategy                                 |\n| ------------------ | ----------------------------- | ----------------------------------------------------- |\n| Bun                | `globalThis.Bun`              | `process.on('unhandledRejection')`                    |\n| Deno               | `globalThis.Deno`             | `globalThis.addEventListener('unhandledrejection')`   |\n| Electron           | `process.versions.electron`   | `process.on()` plus the event listener when it exists |\n| Node.js            | `process.versions.node`       | `process.on('unhandledRejection')`                    |\n| Browsers / Workers | `globalThis.addEventListener` | `globalThis.addEventListener('unhandledrejection')`   |\n\nBun, Deno, and Electron are all checked before `process.versions.node`, because all three define it. Deno 2 runs Node.js compatibility by default, so a `process.versions.node` check alone would take Deno down the Node.js path, and which path it took would depend on the Deno version. Bun uses the Node.js process hook, and the registration is labeled bun so duplicate registration warnings name the real environment. Deno uses the event listener, which it supports in both 1.x and 2.x. An Electron renderer has a Node.js process and a DOM, and renderer rejections land on the DOM event, so both targets are hooked there. An Electron main process has no `addEventListener` and gets the process hook only. Specific registration functions (`registerNode()`, `registerBrowser()`, `registerDeno()`, `registerBun()`, `registerWorker()`, `registerElectron()`) are also exported for explicit control.\n\n### Widening the suppression scope\n\nBy default, only `CancelError` instances are suppressed. You can widen the suppression scope to include `AbortError` or `TimeoutError` by passing optional boolean flags:\n\n```ts\nimport { register } from '@cancjs/unhandled-rejection';\n\nregister({\n  abort: true,\n  timeout: true,\n});\n```\n\nWhen `abort: true` is set, the handler silences raw `AbortError` instances as well as `CancelError` instances wrapping an abort (where `error.aborted === true`). When `timeout: true` is set, the handler silences raw `TimeoutError` instances as well as `CancelError` instances wrapping a timeout (where `error.timedOut === true`). This matches the predicate matching behavior of `catchCancel` and `suppressCancel` in `@cancjs/promise`. Enable these options if your application frequently aborts `fetch` requests or uses operation timeouts.\n\n### Custom rejection handling\n\nWhen passing `onUnhandledRejection`, your custom function receives `(reason, promise)` for all non-suppressed rejections. In Node.js, specifying a custom callback suppresses default process termination and forwards non-cancellation errors to your function. In browser environments, specifying a custom callback calls `preventDefault()` on the event and routes the error to your callback.\n\nYour `onUnhandledRejection` callback must execute synchronously. Runtimes fire rejection events synchronously and ignore returned promises. If an asynchronous callback rejects, it creates a secondary unhandled rejection. Start async operations inside your callback and handle their errors explicitly:\n\n```js\nregister({\n  onUnhandledRejection(reason) {\n    sendTelemetry(reason).catch(console.error);\n  },\n});\n```\n\n### Integration with error reporting services\n\nWhen using error tracking SDKs like Sentry, Datadog, or Bugsnag, register `@cancjs/unhandled-rejection` before initializing your SDK. For example, with Sentry:\n\n```js\nimport { register } from '@cancjs/unhandled-rejection';\nimport * as Sentry from '@sentry/node';\n\nregister();\n\nSentry.init({\n  dsn: 'https://example@sentry.io/123',\n  beforeSend(event, hint) {\n    if (hint && hint.originalException && hint.originalException.name === 'CancelError') {\n      return null;\n    }\n    return event;\n  },\n});\n```\n\nRegistering `@cancjs/unhandled-rejection` first prevents Node.js process termination, while adding a `beforeSend` filter prevents `CancelError` events from generating unnecessary telemetry alerts.\n\n### Integration with existing handlers\n\nEvent listeners for `unhandledrejection` and `process.on('unhandledRejection')` are additive. Attaching `@cancjs/unhandled-rejection` does not remove existing process listeners. If your application or a third-party framework registers a custom rejection listener, that listener will still receive `CancelError` objects unless it includes an explicit `isCancelError` check:\n\n```js\nimport { isCancelError } from '@cancjs/promise';\n\nprocess.on('unhandledRejection', (reason) => {\n  if (isCancelError(reason)) {\n    return;\n  }\n  customLogger.error(reason);\n});\n```\n\n### Manual recipes\n\nFor library authors, custom test harnesses, or exotic runtimes where global package registration is not desired, use these manual recipes:\n\nNode.js and Bun:\n\n```js\nimport { isCancelError } from '@cancjs/promise';\n\nprocess.on('unhandledRejection', (reason) => {\n  if (!isCancelError(reason)) {\n    throw reason;\n  }\n});\n```\n\nNode.js and Bun with abort and timeout widening:\n\n```js\nimport { isCancelError } from '@cancjs/promise';\nimport { isAbortError, isTimeoutError } from '@cancjs/toolbox';\n\nprocess.on('unhandledRejection', (reason) => {\n  const isSuppressed =\n    isCancelError(reason) ||\n    isAbortError(reason) ||\n    isTimeoutError(reason) ||\n    (isCancelError(reason) && (reason.aborted || reason.timedOut));\n\n  if (!isSuppressed) {\n    throw reason;\n  }\n});\n```\n\nBrowsers, Deno, and Web Workers:\n\n```js\nimport { isCancelError } from '@cancjs/promise';\n\nglobalThis.addEventListener('unhandledrejection', (event) => {\n  if (isCancelError(event.reason)) {\n    event.preventDefault();\n  }\n});\n```\n\nBrowsers, Deno, and Web Workers with abort and timeout widening:\n\n```js\nimport { isCancelError } from '@cancjs/promise';\nimport { isAbortError, isTimeoutError } from '@cancjs/toolbox';\n\nglobalThis.addEventListener('unhandledrejection', (event) => {\n  const reason = event.reason;\n  const isSuppressed =\n    isCancelError(reason) ||\n    isAbortError(reason) ||\n    isTimeoutError(reason) ||\n    (isCancelError(reason) && (reason.aborted || reason.timedOut));\n\n  if (isSuppressed) {\n    event.preventDefault();\n  }\n});\n```\n\nCustom logging pattern (Node.js):\n\n```js\nimport { isCancelError } from '@cancjs/promise';\n\nprocess.on('unhandledRejection', (reason) => {\n  if (isCancelError(reason)) {\n    return;\n  }\n  logger.error('Unhandled rejection:', reason);\n});\n```\n\nElectron applications:\n\n```js\n// Call register() in main.js and renderer.js entry points\nimport { register } from '@cancjs/unhandled-rejection';\n\nregister();\n```\n\n### Warnings and diagnostics\n\n`@cancjs/unhandled-rejection` logs diagnostic warnings to `console.warn` when duplicate registrations occur or when a process-specific handler is invoked in an incompatible environment.\n\nDisable warnings globally at runtime:\n\n```js\nimport { setWarn } from '@cancjs/unhandled-rejection';\n\nsetWarn(false);\n```\n\nDisable warnings per registration call:\n\n```js\nimport { register } from '@cancjs/unhandled-rejection';\n\nregister({ warn: false });\n```\n\nDisable warnings using environment variables (Node.js/Bun):\n\n```sh\nCANC_UNHANDLED_WARN=0 node app.js\n```\n\n### Library authors\n\nLibraries should not invoke `register()` or `@cancjs/unhandled-rejection/register`. Global rejection handling is an application-level concern. Library code should handle expected cancellation using `catchCancel` or `suppressCancel` from `@cancjs/promise` at internal flow boundaries.\n\n## API\n\n### Registration functions\n\n- `register(options?: RegisterOptions): void`: Autodetects the host environment and installs the corresponding rejection handler.\n- `registerNode(options?: RegisterOptions): void`: Installs a Node.js process listener. Logs a warning if `process.on` is unavailable.\n- `registerBrowser(options?: RegisterOptions): void`: Installs a browser `globalThis` event listener. Logs a warning if `addEventListener` is unavailable.\n- `registerDeno(options?: RegisterOptions): void`: Installs a Deno global event listener.\n- `registerBun(options?: RegisterOptions): void`: Installs a Bun rejection handler through the Node.js process hook.\n- `registerWorker(options?: RegisterOptions): void`: Installs a Web Worker / Service Worker event listener.\n- `registerElectron(options?: RegisterOptions): void`: Installs the process listener and, in a renderer, the `globalThis` event listener as well. Outside Electron it falls back to `register()`.\n\n### Lifecycle & Configuration\n\n- `unregister(): void`: Removes all handlers registered by this package and resets registration tracking.\n- `setWarn(enabled: boolean): void`: Globally enables or disables diagnostic warnings.\n\n### Types\n\n```ts\ninterface RegisterOptions {\n  warn?: boolean;\n  abort?: boolean;\n  timeout?: boolean;\n  onUnhandledRejection?: (reason: unknown, promise?: Promise<unknown>) => void;\n}\n```\n\n## Runtime support\n\n| Host Environment   | Default Behavior     | Unhandled Crash? | Supported Strategy                       |\n| ------------------ | -------------------- | ---------------- | ---------------------------------------- |\n| Node.js 15+        | Terminates process   | Yes              | `process.on('unhandledRejection')`       |\n| Node.js 18+        | Terminates process   | Yes              | `process.on('unhandledRejection')`       |\n| Bun                | Terminates process   | Yes              | `process.on('unhandledRejection')`       |\n| Deno               | Terminates process   | Yes              | `addEventListener('unhandledrejection')` |\n| Web Browsers       | Console error output | No               | `addEventListener('unhandledrejection')` |\n| Web Workers        | Worker error event   | No               | `addEventListener('unhandledrejection')` |\n| Service Workers    | Worker error event   | No               | `addEventListener('unhandledrejection')` |\n| Cloudflare Workers | Request fail         | Yes              | `addEventListener('unhandledrejection')` |\n| Electron Main      | Terminates process   | Yes              | `process.on('unhandledRejection')`       |\n| Electron Renderer  | Console error output | No               | `addEventListener('unhandledrejection')` |\n\nAll listed environments are supported. Standard web runtimes are autodetected automatically when calling `register()`.\n\n## Compatibility\n\nNode.js 18 and later, Deno 1.0 and later, Bun 1.0 and later, modern browsers, TypeScript 4.2 and later. Requires `@cancjs/promise >=1.0.0` as a peer dependency. Everything else follows [`@cancjs/promise`](https://github.com/cancjs/canc/tree/master/packages/canc-promise#compatibility).\n\n## Documentation\n\n- [`@cancjs/promise`](https://github.com/cancjs/canc/tree/master/packages/canc-promise) for core cancellation semantics and `CancelError`\n- [`docs/unhandled-rejection.md`](https://github.com/cancjs/canc/blob/master/docs/unhandled-rejection.md) for full integration patterns and edge-case handling\n- [Root README](https://github.com/cancjs/canc/blob/master/README.md) for monorepo overview\n- [Examples](https://github.com/cancjs/canc/tree/master/examples) for application integration samples\n\n## Contributing\n\nYou are welcome to participate through issues and pull requests!\n\n## License\n\n[MIT](./LICENSE)\n","readmeFilename":"README.md"}