{"_id":"@apifyhub/sentinel","_rev":"2-4413f7ed9ff2945372a43f28b44cedda","name":"@apifyhub/sentinel","dist-tags":{"latest":"0.3.1"},"versions":{"0.3.0":{"name":"@apifyhub/sentinel","version":"0.3.0","_id":"@apifyhub/sentinel@0.3.0","maintainers":[{"name":"strike90","email":"strike90@gmail.com"}],"dist":{"shasum":"29e0582cf3d2e3f37396183301302a649a91df4f","tarball":"https://registry.npmjs.org/@apifyhub/sentinel/-/sentinel-0.3.0.tgz","fileCount":8,"integrity":"sha512-bcpOTk+GOaDFAEH4I+I3Pi1bcsTgHbTtABsNFPfP8yGkUF8LAI5r4eYv6tbQz3ZrkYaWjBfpTpMiZuii/M3Nlw==","signatures":[{"sig":"MEYCIQDgGelyLtSfvYvUjqexsLonl14z63Jl51W0V6CzGKfe4AIhAKCw8on2+7pt1uwFEZeA/+IbeQuL2V+48FQnFKZBFjVu","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":119421},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"gitHead":"8ad3f8948f2a1dc9817167203aca3bce055c71eb","scripts":{"dev":"tsup --watch","build":"tsup","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"strike90","email":"strike90@gmail.com"},"_npmVersion":"10.9.2","description":"Drop-in error tracking for Apify actors. Captures, groups, and reports exceptions with full Apify run context.","directories":{},"_nodeVersion":"22.13.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","typescript":"^5.4.0"},"_npmOperationalInternal":{"tmp":"tmp/sentinel_0.3.0_1786488582200_0.6766281971143848","host":"s3://npm-registry-packages-npm-production"}},"0.3.1":{"name":"@apifyhub/sentinel","version":"0.3.1","description":"Drop-in error tracking for Apify actors. Captures, groups, and reports exceptions with full Apify run context.","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"engines":{"node":">=18"},"scripts":{"build":"tsup","dev":"tsup --watch","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"devDependencies":{"tsup":"^8.0.0","typescript":"^5.4.0"},"_id":"@apifyhub/sentinel@0.3.1","gitHead":"b2166d5363c8607d4df237747045ca916920a3fc","_nodeVersion":"22.13.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-uo1i+4lnbrfIaViPpfINU9E9JsaIpwZ09S36BWGgJkK1QqhM4k4Bu8lX4HSo51R/E42zdhKBQhGaTBhI8eY2gw==","shasum":"cc6c767e170a164c7169e8fdac4634881d4516ce","tarball":"https://registry.npmjs.org/@apifyhub/sentinel/-/sentinel-0.3.1.tgz","fileCount":8,"unpackedSize":119034,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIDmiarsbRz57vEs224KvvWoRMQaHW1H8j1Oy13Wti1a9AiEAy6Tc6Lo7CDPszgBMsrW/rj/MpUOALkBXeag3g0SfN2M="}]},"_npmUser":{"name":"strike90","email":"strike90@gmail.com"},"directories":{},"maintainers":[{"name":"strike90","email":"strike90@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sentinel_0.3.1_1786492940002_0.5171541925358638"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-11T22:49:41.986Z","modified":"2026-08-12T00:02:20.394Z","0.3.0":"2026-08-11T22:49:42.397Z","0.3.1":"2026-08-12T00:02:20.156Z"},"description":"Drop-in error tracking for Apify actors. Captures, groups, and reports exceptions with full Apify run context.","maintainers":[{"name":"strike90","email":"strike90@gmail.com"}],"readme":"# @apifyhub/sentinel\n\nDrop-in **error tracking** built for [Apify](https://apify.com) actors. It\ncaptures exceptions, **groups them into issues** by\nfingerprint, carries breadcrumbs and the full Apify run context (run/actor/build\nids, consumer, origin), and reports each to your apifyHub dashboard.\n\n## Install\n\n```bash\nnpm install @apifyhub/sentinel\n```\n\nRequires Node 18+ (built-in `fetch`). Zero runtime dependencies.\n\n## Usage\n\nWrap your actor body — any throw is captured, grouped, flushed, and re-thrown\nunchanged:\n\n```js\nimport { withSentinel } from '@apifyhub/sentinel';\n\nawait withSentinel({ apifyHubKey: process.env.APIFYHUB_KEY }, async () => {\n  // ...the actor's normal code.\n});\n```\n\nOr wire it manually for finer control:\n\n```js\nimport { init, captureException, addBreadcrumb, flush } from '@apifyhub/sentinel';\n\ninit({ apifyHubKey: process.env.APIFYHUB_KEY });\n\naddBreadcrumb({ category: 'http', message: 'GET /search', level: 'info' });\n\ntry {\n  await doWork();\n} catch (err) {\n  captureException(err);   // grouped into an issue by fingerprint\n  await flush();           // ensure delivery before the container exits\n  throw err;\n}\n```\n\nCommonJS works too (`const { init } = require('@apifyhub/sentinel')`).\n\n### Setup\n\n1. Generate an **apifyHub key** on your [apifyhub.com](https://apifyhub.com) profile.\n2. Add it as a **secret env var** named `APIFYHUB_KEY` (Apify Console → your actor →\n   Settings → Environment variables).\n3. Deploy. Crashes and captured errors now show up as issues on your dashboard.\n\n## API\n\n| Function | Purpose |\n| --- | --- |\n| `init(options)` | Initialise; installs global crash handlers (unless `captureUnhandled: false`) — a crash is captured, flushed, then the process still exits 1 as normal. Idempotent. |\n| `withSentinel(options, fn)` | Init + run `fn`, capturing/flushing/re-throwing any error. |\n| `captureException(err, opts?)` | Report an exception. Returns the event id. |\n| `captureMessage(msg, opts?)` | Report a plain message at a given `level`. |\n| `addBreadcrumb(crumb)` | Append a timeline entry shown with the next error. |\n| `setTag(k, v)` / `setContext(name, obj)` / `setUser(u)` | Attach metadata to subsequent events. |\n| `flush(timeoutMs?)` | Await in-flight sends before teardown. Returns whether it drained in time. |\n\n### Options\n\n```ts\ninit({\n  apifyHubKey: string;        // required — your apifyHub key (Bearer token)\n  endpoint?: string;          // override ingest URL (defaults to apifyHub prod)\n  debug?: boolean;            // log internal failures (default false)\n  forceOutsideApify?: boolean // send even when not on Apify (default false)\n  captureUnhandled?: boolean; // install uncaught/unhandledRejection handlers (default true)\n  sampleRate?: number;        // 0..1, deterministic per-event drop (default 1)\n  maxBreadcrumbs?: number;    // ring-buffer size (default 50)\n  release?: string;           // e.g. actor build number\n  environment?: string;       // e.g. \"production\"\n  beforeSend?: (event) => event | null;  // scrub or drop; null = drop\n});\n```\n\nSampling happens before transport, so dropped events use no network or server\nquota. It defaults to 100%; set `sampleRate: 0.25` to retain a deterministic\n25% sample. Values are clamped to the `0..1` range.\n\nRaw event payloads are retained for 90 days on every plan. Grouped issue and\ndaily aggregate history remains available after the raw stack trace expires.\n\n## How grouping works\n\nEach event gets a **fingerprint** computed in-process: exception type +\n*normalized* message + the top in-app stack frame. Normalizing replaces volatile\ntokens (numbers, hex, uuids, urls, quoted strings) with placeholders, so\n`Timeout after 314ms` and `Timeout after 2200ms` land in the same issue. The\nserver buckets events by fingerprint into issues you can triage.\n\n## Guarantees\n\n- **Never throws.** A bad key, network outage, or non-Apify environment can never\n  crash your actor or swallow its error — every entry point is wrapped.\n- **Key stays in the header.** Sent as `Authorization: Bearer`, never in the body.\n- **Idempotent init.** A second `init()` in the same process is a silent no-op.\n- **Outside Apify it no-ops** (unless `forceOutsideApify: true`), so local runs\n  and tests stay quiet.\n","readmeFilename":"README.md"}