{"_id":"@agentwares/notify","_rev":"2-33e78f47237637676da182c14a0800d6","name":"@agentwares/notify","dist-tags":{"latest":"0.1.1"},"versions":{"0.1.0":{"name":"@agentwares/notify","version":"0.1.0","keywords":["resend","transactional-email","slack-webhook","discord-webhook","webhooks","hmac","email-templates","cloudflare-workers","notifications"],"license":"MIT","_id":"@agentwares/notify@0.1.0","maintainers":[{"name":"umerbukhari","email":"umer.bukhari@gmail.com"}],"homepage":"https://github.com/agentwares/libs/tree/main/packages/notify#readme","bugs":{"url":"https://github.com/agentwares/libs/issues"},"dist":{"shasum":"0f7c4925324592d347632e71ff63552a3c45f845","tarball":"https://registry.npmjs.org/@agentwares/notify/-/notify-0.1.0.tgz","fileCount":6,"integrity":"sha512-Z8A4n93zAVsORSa1vsc+AXt2VeSFN8i0itoWFo8l/iY7fZQnPh5QbR2SphjNVNWOhs8SBQJtpk1eB2NABE6Wag==","signatures":[{"sig":"MEYCIQDhmczLqlkDtUxhDRhwdLMXK80L/pEnu7JbtnUZ6H9MLgIhAMhCwU7mCS89zS13Qaw74HjH4mUP6sG101EvrRL1lpY5","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":121189},"main":"./dist/index.js","type":"module","_from":"file:agentwares-notify-0.1.0.tgz","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"lint":"eslint src","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"umerbukhari","email":"umer.bukhari@gmail.com"},"_resolved":"/private/var/folders/2p/2j2wcvfs4wz6v0nfr4h455k40000gn/T/35f888e703de546f815c6bd824b107e7/agentwares-notify-0.1.0.tgz","_integrity":"sha512-Z8A4n93zAVsORSa1vsc+AXt2VeSFN8i0itoWFo8l/iY7fZQnPh5QbR2SphjNVNWOhs8SBQJtpk1eB2NABE6Wag==","repository":{"url":"git+https://github.com/agentwares/libs.git","type":"git","directory":"packages/notify"},"_npmVersion":"10.9.8","description":"Transactional email (Resend), Slack and Discord webhooks and HMAC-signed generic webhooks from one fan-out call, with plain HTML alert/digest/drift/receipt templates. Web-standard only, so the same code runs on Vercel Functions and Cloudflare Workers.","directories":{},"sideEffects":false,"_nodeVersion":"22.23.2","dependencies":{"zod":"^4.5.4","resend":"^6.25.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/notify_0.1.0_1788805235323_0.35360135828968087","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"_id":"@agentwares/notify@0.1.1","bugs":{"url":"https://github.com/agentwares/libs/issues"},"dist":{"shasum":"4b944207048af33b31dc28b7fbd2c3adcf2cf806","tarball":"https://registry.npmjs.org/@agentwares/notify/-/notify-0.1.1.tgz","fileCount":6,"integrity":"sha512-wv0zrrUClywhwp9BGwfF0wl/kSwOW+HzBVXJRLqpZA8GawqQn9UCuPz2ohnj1TmajUsry4R6jm/YefxecHkZ5Q==","signatures":[{"sig":"MEUCIBnubIB0Xvvh2GhLWFo/SubytqCWVdxXveJ67Cv8iaZuAiEA0Tc2PmA5f4zu2qFUq2aNftpQFwwyiz6KIThzuYk4wTE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIAOsGuRNjhPqgs55yoWVW9dmHgm28swvDXRuIDLtoTJjAiB2RFW+u9SaXTA2QoyS+X/x5mJQk6n+bO26EUa3XgK3dQ=="}],"unpackedSize":121162},"main":"./dist/index.js","name":"@agentwares/notify","type":"module","_from":"file:agentwares-notify-0.1.1.tgz","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"license":"MIT","scripts":{"lint":"eslint src","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit"},"version":"0.1.1","_npmUser":{"name":"umerbukhari","email":"umer.bukhari@gmail.com"},"homepage":"https://github.com/agentwares/libs/tree/main/packages/notify#readme","keywords":["resend","transactional-email","slack-webhook","discord-webhook","webhooks","hmac","email-templates","cloudflare-workers","notifications"],"_resolved":"/private/var/folders/2p/2j2wcvfs4wz6v0nfr4h455k40000gn/T/b4ce650bcdabd5e4a7491f2185fbc18b/agentwares-notify-0.1.1.tgz","_integrity":"sha512-wv0zrrUClywhwp9BGwfF0wl/kSwOW+HzBVXJRLqpZA8GawqQn9UCuPz2ohnj1TmajUsry4R6jm/YefxecHkZ5Q==","repository":{"url":"git+https://github.com/agentwares/libs.git","type":"git","directory":"packages/notify"},"_npmVersion":"10.9.8","description":"Transactional email (Resend), Slack and Discord webhooks and HMAC-signed generic webhooks from one fan-out call, with plain HTML alert/digest/drift/receipt templates. Web-standard only, so the same code runs on Vercel Functions and Cloudflare Workers.","directories":{},"maintainers":[{"name":"umerbukhari","email":"umer.bukhari@gmail.com"}],"sideEffects":false,"_nodeVersion":"22.23.2","dependencies":{"zod":"^4.5.4","resend":"^6.25.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/notify_0.1.1_1788820558284_0.41814947172007755"}}},"time":{"created":"2026-09-07T18:20:35.210Z","modified":"2026-09-07T22:35:58.549Z","0.1.0":"2026-09-07T18:20:35.469Z","0.1.1":"2026-09-07T22:35:58.386Z"},"bugs":{"url":"https://github.com/agentwares/libs/issues"},"license":"MIT","homepage":"https://github.com/agentwares/libs/tree/main/packages/notify#readme","keywords":["resend","transactional-email","slack-webhook","discord-webhook","webhooks","hmac","email-templates","cloudflare-workers","notifications"],"repository":{"url":"git+https://github.com/agentwares/libs.git","type":"git","directory":"packages/notify"},"description":"Transactional email (Resend), Slack and Discord webhooks and HMAC-signed generic webhooks from one fan-out call, with plain HTML alert/digest/drift/receipt templates. Web-standard only, so the same code runs on Vercel Functions and Cloudflare Workers.","maintainers":[{"name":"umerbukhari","email":"umer.bukhari@gmail.com"}],"readme":"# @agentwares/notify\n\nTransactional email (Resend free tier), Slack/Discord webhooks and HMAC-signed generic webhooks\nbehind one fan-out call. Plain HTML templates, no images, no tracking. Web-standard only (`fetch`,\n`crypto.subtle`): the same code runs on Vercel Functions, Cloudflare Workers and Node 20+.\n\n```sh\nnpm install @agentwares/notify\n```\n\n```ts\nimport { createNotify } from \"@agentwares/notify\";\n\nconst notify = createNotify({ resendApiKey: env.RESEND_API_KEY, from: env.RESEND_FROM });\n\nawait notify.alert(\n  { email: [\"ops@acme.dev\"], slack: user.slackWebhookUrl },\n  {\n    productName: \"acme\",\n    targetName: \"support-bot\",\n    checkName: \"refund-policy\",\n    statusUrl: \"https://status.example.com/acme/support-bot\",\n    openedAt: incident.openedAt,\n    error: { code: \"RUBRIC_FAIL\", cause: \"Score 0.41 < 0.7\", fix: \"Compare the diff below.\" },\n    transcriptExcerpt: result.transcriptExcerpt,\n    diff: { before: lastPass.answer, after: result.answer },\n    consecutiveFailures: 2,\n  },\n);\n// -> { email: { ok: true, id: \"...\" }, slack: { ok: true, status: 200 } }\n```\n\n## Exports\n\n| Area      | Export                                                                                                                                                                                                         |\n| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| HTML      | `escapeHtml`, `safeUrl`, `layout({ title, preheader, bodyHtml, footerHtml })`, `toText(html)`, block helpers (`h1`, `p`, `link`, `button`, `pre`, `table`, `pill`, `callout`, `ul`), `formatDate`, `formatUsd` |\n| Templates | `alertTemplate`, `recoveredTemplate`, `digestTemplate`, `driftTemplate`, `receiptTemplate` (each returns `{ subject, html, text }`)                                                                            |\n| Channels  | `sendEmail`, `sendSlack`, `sendDiscord`, `sendWebhook`, `hmacSha256Hex`, `verifyWebhookSignature`, `truncate`                                                                                                  |\n| Fan-out   | `createNotify({ resendApiKey?, from, replyTo?, fetch?, resend? })` returning `{ email, slack, discord, webhook, alert, recovered, drift, digest }`                                                             |\n\nEvery channel resolves a `SendResult = { ok, id?, status?, error? }` and never throws on remote failure. With no `resendApiKey` and no injected client, `email` resolves `{ ok: false, error: \"RESEND_API_KEY not configured\" }`.\n\n## Templates\n\nAll inputs are escaped; output has no `<img>`; `text` is derived from the HTML (links kept as `text (url)`, `<pre>` preserved).\n\n| Template            | Subject                                                 | Inputs                                                                                                                                             |\n| ------------------- | ------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `alertTemplate`     | `[product] FAILING: target · check`                     | `productName, targetName, checkName, statusUrl, openedAt, error {code, cause?, fix?}, transcriptExcerpt?, diff?, consecutiveFailures, silenceUrl?` |\n| `recoveredTemplate` | `[product] RECOVERED: target · check`                   | `productName, targetName, checkName, statusUrl, downtimeMinutes`                                                                                   |\n| `digestTemplate`    | `[product] Nightly report <date>[: N decisions needed]` | `productName, date, sections [{title, items}], costUsd?, decisionsNeeded?, reportUrl`                                                              |\n| `driftTemplate`     | `[product] <model> shipped: N regressions in <target>`  | `productName, provider, model, releasedAt, targetName, summary, changes [{check, before, after, verdict}], runUrl, shareUrl`                       |\n| `receiptTemplate`   | `[product] Receipt [ref]: $total`                       | `productName, lines [{description, amountUsd}], totalUsd, portalUrl, reference?`                                                                   |\n\n`alertTemplate` leads with what failed, then the transcript excerpt, then the diff vs the last pass (unified string or `{ before, after }`), then the status link and a \"silence for 24h\" link (`silenceUrl`, defaulting to `statusUrl?silence=24h` until the app wires a route). `driftTemplate` is the forwardable one: regressed rows first, a \"Forward this\" line and the public `shareUrl`.\n\n## Channels\n\n- `sendEmail({ from, to, subject, html, text?, replyTo?, apiKey?, resend?, fetch? })`: Resend SDK (`new Resend(apiKey)`, `emails.send`). Inject `resend` for tests; injecting `fetch` posts to the same REST endpoint through it (the SDK only uses the global fetch).\n- `sendSlack(webhookUrl, { text, blocks? })`\n- `sendDiscord(webhookUrl, { content, embeds? })`: `content` truncated to 2000 chars without splitting surrogate pairs; returns the message id.\n- `sendWebhook(url, payload, { secret?, headers? })`: JSON POST; with `secret`, adds `X-Agentwares-Signature: sha256=<HMAC-SHA256 hex of the raw body>`. Receivers call `verifyWebhookSignature(rawBody, secret, header)`.\n\nFan-out helpers send the full HTML by email and a one-line summary with the link to Slack/Discord; webhooks receive `{ event, product, subject, summary, url, occurredAt, data }`.\n\n## Environment\n\nNothing is read from `process.env`: pass `resendApiKey` and `from` to `createNotify` yourself. In the agentwares portfolio those come from `RESEND_API_KEY` and `RESEND_FROM` (use `onboarding@resend.dev` until a domain is verified).\n\n## Tests\n\n`pnpm test` is fully offline (fake fetch / fake Resend). `LIVE_TESTS=1 pnpm test` additionally sends one alert to `delivered@resend.dev` through the real SDK, using `RESEND_API_KEY` from the environment or a repo-root `.env.local`.\n\nMIT © agentwares contributors — part of the [agentwares](https://agentwares.vercel.app) portfolio.\n","readmeFilename":"README.md"}