{"_id":"@pushforge/builder","_rev":"11-0a78ed8ac745189a5b7bd06ff6726e4a","name":"@pushforge/builder","dist-tags":{"beta":"1.0.0-beta.1","latest":"2.0.5"},"versions":{"1.0.0":{"name":"@pushforge/builder","version":"1.0.0","keywords":["web-push","webcrypto","vapid","web-push-protocol","typescript","node","cloudflare","browser","ecdh","hkdf","push-service"],"author":{"name":"David Raphi"},"license":"MIT","_id":"@pushforge/builder@1.0.0","maintainers":[{"name":"draphy","email":"davidraphi@proton.me"}],"homepage":"https://github.com/draphy/pushforge","bugs":{"url":"https://github.com/draphy/pushforge/issues"},"bin":{"generate-vapid-keys":"dist/lib/commandLine/keys.js"},"dist":{"shasum":"90e02132869d1fb659296e8a28d276117bb08c86","tarball":"https://registry.npmjs.org/@pushforge/builder/-/builder-1.0.0.tgz","fileCount":25,"integrity":"sha512-4XLGls3CwgG+1XaHKOTiH02crsKhvrRPVBQyNmniZ0daygp72hIzYnpwVmrWBLaQV2dn8QhlHlfWH9q47EL1Rg==","signatures":[{"sig":"MEYCIQCyUxTBQONXD/pUGFzgN7/b0mRD3oebRswjq8TSM3imCQIhAOTsjsqA0k9Z/ul95PaS2Yof/9JmS/vJ1/BmPqkqqXDx","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":49454},"main":"dist/lib/main.js","type":"module","types":"dist/lib/main.d.ts","module":"dist/lib/main.js","exports":{".":{"types":"./dist/lib/main.d.ts","default":"./dist/lib/main.js"}},"gitHead":"5369fca06c8ffc55790b843f3aa2b0ba25f80156","private":false,"scripts":{"test":"vitest run","build":"tsc --build","test:watch":"vitest","semantic-release":"semantic-release"},"_npmUser":{"name":"draphy","email":"davidraphi@proton.me"},"repository":{"url":"git+https://github.com/draphy/pushforge.git","type":"git"},"_npmVersion":"10.9.2","description":"A robust, cross-platform Web Push notification library that handles VAPID authentication and payload encryption following the Web Push Protocol standard. Works in Node.js 16+, Browsers, Deno, Bun and Cloudflare Workers.","directories":{},"_nodeVersion":"22.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.1.2","@types/node":"^22.14.1","semantic-release":"^24.2.3","@semantic-release/npm":"^11.0.3","@semantic-release/github":"^9.2.6","@semantic-release/commit-analyzer":"^11.1.0","@semantic-release/release-notes-generator":"^12.1.0"},"_npmOperationalInternal":{"tmp":"tmp/builder_1.0.0_1745278185713_0.07364406621825781","host":"s3://npm-registry-packages-npm-production"}},"1.0.0-beta.1":{"name":"@pushforge/builder","version":"1.0.0-beta.1","keywords":["web-push","webcrypto","vapid","web-push-protocol","typescript","node","cloudflare","browser","ecdh","hkdf","push-service"],"author":{"name":"David Raphi"},"license":"MIT","_id":"@pushforge/builder@1.0.0-beta.1","maintainers":[{"name":"draphy","email":"davidraphi@proton.me"}],"homepage":"https://github.com/draphy/pushforge","bugs":{"url":"https://github.com/draphy/pushforge/issues"},"bin":{"generate-vapid-keys":"dist/lib/commandLine/keys.js"},"dist":{"shasum":"294e6d61fe9bc09059ead2c9b346d0de9f09a5a1","tarball":"https://registry.npmjs.org/@pushforge/builder/-/builder-1.0.0-beta.1.tgz","fileCount":25,"integrity":"sha512-IS1LSc3tFaYxOwOU3YU71IAgNGbp0prARtp2tTCs0q6bUxTsXBMQfGk+6+a7X1o3kHHSi3Z9puSAQ9vQAvutog==","signatures":[{"sig":"MEUCIEoJc0OztX5jmBPQ2JkZnV9Ojb60fPT9uabiHyUeESf1AiEAgbLnBS2JquFufQpleZiRzfd+JNTxi0ALOfAUCVmWTPU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":49461},"main":"dist/lib/main.js","type":"module","types":"dist/lib/main.d.ts","module":"dist/lib/main.js","exports":{".":{"types":"./dist/lib/main.d.ts","default":"./dist/lib/main.js"}},"gitHead":"cb708323889c338cf8cfdfaa66120095433804ed","private":false,"scripts":{"test":"vitest run","build":"tsc --build","test:watch":"vitest","semantic-release":"semantic-release"},"_npmUser":{"name":"draphy","email":"davidraphi@proton.me"},"repository":{"url":"git+https://github.com/draphy/pushforge.git","type":"git"},"_npmVersion":"10.9.2","description":"A robust, cross-platform Web Push notification library that handles VAPID authentication and payload encryption following the Web Push Protocol standard. Works in Node.js 16+, Browsers, Deno, Bun and Cloudflare Workers.","directories":{},"_nodeVersion":"22.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"vitest":"^3.1.2","@types/node":"^22.14.1","semantic-release":"^24.2.3","@semantic-release/npm":"^11.0.3","@semantic-release/github":"^9.2.6","@semantic-release/commit-analyzer":"^11.1.0","@semantic-release/release-notes-generator":"^12.1.0"},"_npmOperationalInternal":{"tmp":"tmp/builder_1.0.0-beta.1_1745286213188_0.9970120640879059","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@pushforge/builder","version":"1.1.0","keywords":["David Raphi","David","draphy","david","draphi","draphy.org","www.draphy.org","non profit organization","open source","web push","pushforge","@pushforge/builder","pushforge builder","draphy pushforge builder","draphy pushforge","web push builder","web push notification","web push protocol","pushforge npm","web push service","@pushforge/builder npm package","web push npm package","web push vapid","notification","cross-platform","cross platform","web-push","webcrypto","vapid","web-push-protocol","typescript","node","cloudflare","browser","ecdh","hkdf","push-service"],"author":{"name":"David Raphi"},"license":"MIT","_id":"@pushforge/builder@1.1.0","maintainers":[{"name":"draphy","email":"davidraphi@proton.me"}],"homepage":"https://github.com/draphy/pushforge","bugs":{"url":"https://github.com/draphy/pushforge/issues"},"bin":{"generate-vapid-keys":"dist/lib/commandLine/keys.js"},"dist":{"shasum":"2b2e15b7e86c7e54ad968b407268f17e5c262a66","tarball":"https://registry.npmjs.org/@pushforge/builder/-/builder-1.1.0.tgz","fileCount":25,"integrity":"sha512-WJ/ohefj69w8eUD0lhfysFlIDzyiRQP56PfT5hpf/8ap468/KyYkGpLdYtouraBrOkMucnOZpIz3wYAJK1lPbw==","signatures":[{"sig":"MEQCIHuMt69d+DQmQMdB3ACHbT9LLcmWG2nGSGTAeDbtxOzAAiAsPNzIvur9W/WoiQJpGqIz8b9L0IG7zfV7QjZm4URFpQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":50032},"main":"dist/lib/main.js","type":"module","types":"dist/lib/main.d.ts","module":"dist/lib/main.js","exports":{".":{"types":"./dist/lib/main.d.ts","default":"./dist/lib/main.js"}},"gitHead":"c29177130889181988109aa6617cb6605e8f78a6","private":false,"scripts":{"test":"vitest run","build":"tsc --build","test:watch":"vitest","semantic-release":"semantic-release"},"_npmUser":{"name":"draphy","email":"davidraphi@proton.me"},"repository":{"url":"git+https://github.com/draphy/pushforge.git","type":"git"},"_npmVersion":"10.9.2","description":"A robust, cross-platform Web Push notification library that handles VAPID authentication and payload encryption following the Web Push Protocol standard. Works in Node.js 16+, Browsers, Deno, Bun and Cloudflare Workers.","directories":{},"_nodeVersion":"22.17.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.1.2","@types/node":"^22.14.1","semantic-release":"^24.2.3","@semantic-release/npm":"^11.0.3","@semantic-release/github":"^9.2.6","@semantic-release/commit-analyzer":"^11.1.0","@semantic-release/release-notes-generator":"^12.1.0"},"_npmOperationalInternal":{"tmp":"tmp/builder_1.1.0_1753515323690_0.8044081507506178","host":"s3://npm-registry-packages-npm-production"}},"1.1.1":{"name":"@pushforge/builder","version":"1.1.1","keywords":["David Raphi","David","draphy","david","draphi","draphy.org","www.draphy.org","non profit organization","open source","web push","pushforge","@pushforge/builder","pushforge builder","draphy pushforge builder","draphy pushforge","web push builder","web push notification","web push protocol","pushforge npm","web push service","@pushforge/builder npm package","web push npm package","web push vapid","notification","cross-platform","cross platform","web-push","webcrypto","vapid","web-push-protocol","typescript","node","cloudflare","browser","ecdh","hkdf","push-service"],"author":{"name":"David Raphi","email":"david@draphy.org"},"license":"MIT","_id":"@pushforge/builder@1.1.1","maintainers":[{"name":"draphy","email":"david@draphy.org"}],"homepage":"https://github.com/draphy/pushforge","bugs":{"url":"https://github.com/draphy/pushforge/issues"},"bin":{"generate-vapid-keys":"dist/lib/commandLine/keys.js"},"dist":{"shasum":"e50e4383fd88f8c7b34e2a4ed08b3b71949afbe7","tarball":"https://registry.npmjs.org/@pushforge/builder/-/builder-1.1.1.tgz","fileCount":25,"integrity":"sha512-8DnL9NCHfsLj5y2ad1SFIp/5M+pnzWqXc5ywOhGlr/43VqT8n49vk25dY24a91akw1nPneLTTE+WHw5bByMerw==","signatures":[{"sig":"MEUCIQD2L72cpmr7uFfO8e4aKLLaPnFOzj7iLyDgCZrzzdWf5gIgZ6BSF7+PRpFuiTttVZeSxXQ+b38XhYBHVP+Cvyyg5wY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":50083},"main":"dist/lib/main.js","type":"module","types":"dist/lib/main.d.ts","module":"dist/lib/main.js","exports":{".":{"types":"./dist/lib/main.d.ts","default":"./dist/lib/main.js"}},"gitHead":"1ea86723861e3533e9e024bb96c2d6c0702b208f","private":false,"scripts":{"test":"vitest run","build":"tsc --build","test:watch":"vitest","semantic-release":"semantic-release"},"_npmUser":{"name":"draphy","email":"david@draphy.org"},"repository":{"url":"git+https://github.com/draphy/pushforge.git","type":"git"},"_npmVersion":"10.9.3","description":"A robust, cross-platform Web Push notification library that handles VAPID authentication and payload encryption following the Web Push Protocol standard. Works in Node.js 16+, Browsers, Deno, Bun and Cloudflare Workers.","directories":{},"_nodeVersion":"22.20.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.1.2","@types/node":"^22.14.1","semantic-release":"^24.2.3","@semantic-release/npm":"^11.0.3","@semantic-release/github":"^9.2.6","@semantic-release/commit-analyzer":"^11.1.0","@semantic-release/release-notes-generator":"^12.1.0"},"_npmOperationalInternal":{"tmp":"tmp/builder_1.1.1_1761622880159_0.6609827962591865","host":"s3://npm-registry-packages-npm-production"}},"1.1.2":{"name":"@pushforge/builder","version":"1.1.2","keywords":["David Raphi","David","draphy","david","draphi","draphy.org","www.draphy.org","non profit organization","open source","web push","pushforge","@pushforge/builder","pushforge builder","draphy pushforge builder","draphy pushforge","web push builder","web push notification","web push protocol","pushforge npm","web push service","@pushforge/builder npm package","web push npm package","web push vapid","notification","cross-platform","cross platform","web-push","webcrypto","vapid","web-push-protocol","typescript","node","cloudflare","browser","ecdh","hkdf","push-service"],"author":{"name":"David Raphi","email":"david@draphy.org"},"license":"MIT","_id":"@pushforge/builder@1.1.2","maintainers":[{"name":"draphy","email":"david@draphy.org"}],"homepage":"https://github.com/draphy/pushforge","bugs":{"url":"https://github.com/draphy/pushforge/issues"},"bin":{"generate-vapid-keys":"dist/lib/commandLine/keys.js"},"dist":{"shasum":"fa17d4caf39b80205c3109649161049f1d982683","tarball":"https://registry.npmjs.org/@pushforge/builder/-/builder-1.1.2.tgz","fileCount":25,"integrity":"sha512-9dhcm++aBKRAWo/1qLF1WSOqfn9ZHDNdAd0JAVSw1XjvQNxpe07ObXw0IiCng7Fa5q3rmam7DzLi0bs/W2Aumw==","signatures":[{"sig":"MEYCIQD1rIgy9pNmOahonIgYhSoYB8g944rElNqKNivqBb4ufQIhANXcTeDSTJm+uK6KGEq5xzLTjdpin5a3cGDAsIlwZClz","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":50126},"main":"dist/lib/main.js","type":"module","types":"dist/lib/main.d.ts","module":"dist/lib/main.js","engines":{"node":">=16.0.0"},"exports":{".":{"types":"./dist/lib/main.d.ts","default":"./dist/lib/main.js"}},"gitHead":"4aa88ef1ecfb880d9cbe559a423e49f17bf8c17a","private":false,"scripts":{"test":"vitest run","build":"tsc --build","test:watch":"vitest","semantic-release":"semantic-release"},"_npmUser":{"name":"draphy","email":"david@draphy.org"},"repository":{"url":"git+https://github.com/draphy/pushforge.git","type":"git"},"_npmVersion":"10.9.4","description":"A robust, cross-platform Web Push notification library that handles VAPID authentication and payload encryption following the Web Push Protocol standard. Works in Node.js 16+, Browsers, Deno, Bun and Cloudflare Workers.","directories":{},"_nodeVersion":"22.21.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.1.2","@types/node":"^22.14.1","semantic-release":"^24.2.3","@semantic-release/npm":"^11.0.3","@semantic-release/github":"^9.2.6","@semantic-release/commit-analyzer":"^11.1.0","@semantic-release/release-notes-generator":"^12.1.0"},"_npmOperationalInternal":{"tmp":"tmp/builder_1.1.2_1762688823100_0.1794194229078374","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"name":"@pushforge/builder","version":"2.0.0","keywords":["David Raphi","David","draphy","david","draphi","draphy.org","www.draphy.org","non profit organization","open source","web push","pushforge","@pushforge/builder","pushforge builder","draphy pushforge builder","draphy pushforge","web push builder","web push notification","web push protocol","pushforge npm","web push service","@pushforge/builder npm package","web push npm package","web push vapid","notification","cross-platform","cross platform","web-push","webcrypto","vapid","web-push-protocol","typescript","node","cloudflare","browser","ecdh","hkdf","push-service"],"author":{"name":"David Raphi","email":"david@draphy.org"},"license":"MIT","_id":"@pushforge/builder@2.0.0","maintainers":[{"name":"draphy","email":"david@draphy.org"}],"homepage":"https://github.com/draphy/pushforge","bugs":{"url":"https://github.com/draphy/pushforge/issues"},"bin":{"pushforge":"dist/lib/commandLine/keys.js"},"dist":{"shasum":"4991030bf94c93aea17fa8f4ffebba6ba22ace6e","tarball":"https://registry.npmjs.org/@pushforge/builder/-/builder-2.0.0.tgz","fileCount":25,"integrity":"sha512-PBJytyd9oB+nfrjxDDQffwySPXLiu7PF2EHgjUDvJcAsVveW9iUUP4afcGNjFFQo9v+wXtO8wXEMGFvLPi+prw==","signatures":[{"sig":"MEUCIQDf0VL6ykk4oa5ohcGfwWSGgnV0hlpJG7YkPleSs+OtGQIgaMpcNHe3vnwCemYYc8DH4rI16629QtUc12qNXHPB8n0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@pushforge%2fbuilder@2.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":56835},"main":"dist/lib/main.js","type":"module","types":"dist/lib/main.d.ts","module":"dist/lib/main.js","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/lib/main.d.ts","default":"./dist/lib/main.js"}},"gitHead":"b485655fd867338f305a94b389be00f7e4e1aac0","private":false,"scripts":{"test":"vitest run","build":"tsc --build","test:watch":"vitest","semantic-release":"semantic-release"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:834e65ad-b854-4d48-abda-a0045b476edf"}},"repository":{"url":"git+https://github.com/draphy/pushforge.git","type":"git"},"_npmVersion":"11.6.2","description":"A robust, cross-platform Web Push notification library that handles VAPID authentication and payload encryption following the Web Push Protocol standard. Works in Node.js 16+, Browsers, Deno, Bun and Cloudflare Workers.","directories":{},"_nodeVersion":"24.13.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.1.2","@types/node":"^22.14.1","semantic-release":"^24.2.3","@semantic-release/npm":"^11.0.3","@semantic-release/github":"^9.2.6","@semantic-release/commit-analyzer":"^11.1.0","@semantic-release/release-notes-generator":"^12.1.0"},"_npmOperationalInternal":{"tmp":"tmp/builder_2.0.0_1769660943431_0.29829028527472157","host":"s3://npm-registry-packages-npm-production"}},"2.0.1":{"name":"@pushforge/builder","version":"2.0.1","keywords":["David Raphi","David","draphy","david","draphi","draphy.org","www.draphy.org","non profit organization","open source","web push","pushforge","@pushforge/builder","pushforge builder","draphy pushforge builder","draphy pushforge","web push builder","web push notification","web push protocol","pushforge npm","web push service","@pushforge/builder npm package","web push npm package","web push vapid","notification","cross-platform","cross platform","web-push","webcrypto","vapid","web-push-protocol","typescript","node","cloudflare","browser","ecdh","hkdf","push-service"],"author":{"name":"David Raphi","email":"david@draphy.org"},"license":"MIT","_id":"@pushforge/builder@2.0.1","maintainers":[{"name":"draphy","email":"david@draphy.org"}],"homepage":"https://github.com/draphy/pushforge","bugs":{"url":"https://github.com/draphy/pushforge/issues"},"bin":{"pushforge":"dist/lib/commandLine/keys.js"},"dist":{"shasum":"4ef4bb4644994fe40633bfeb1e00fc0dd8a6f237","tarball":"https://registry.npmjs.org/@pushforge/builder/-/builder-2.0.1.tgz","fileCount":25,"integrity":"sha512-bH7GU4gzUlBhmpxZOfmYJ4KQrC+CVkV1IN2fav+W7sp0tqi2HfcAHFtvISSTstyjOfz+32Rvy9bmpfLgNSOHCQ==","signatures":[{"sig":"MEUCIQDlegXbOlF76LbNDqvp22Okac/Yutrh4k5Vos5B7TciaAIgUKGGzUamiSUfRBEkr19T2+Y+TVyCTXujJ4JzFo9qBYY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@pushforge%2fbuilder@2.0.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":56826},"main":"dist/lib/main.js","type":"module","types":"dist/lib/main.d.ts","module":"dist/lib/main.js","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/lib/main.d.ts","default":"./dist/lib/main.js"}},"gitHead":"aa7b711214616ba793050fb694094aba69e780a1","private":false,"scripts":{"test":"vitest run","build":"tsc --build","test:watch":"vitest","semantic-release":"semantic-release"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:834e65ad-b854-4d48-abda-a0045b476edf"}},"repository":{"url":"git+https://github.com/draphy/pushforge.git","type":"git"},"_npmVersion":"11.6.2","description":"A robust, cross-platform Web Push notification library that handles VAPID authentication and payload encryption following the Web Push Protocol standard. Works in Node.js 16+, Browsers, Deno, Bun and Cloudflare Workers.","directories":{},"_nodeVersion":"24.13.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.1.2","@types/node":"^22.14.1","semantic-release":"^24.2.3","@semantic-release/npm":"^11.0.3","@semantic-release/github":"^9.2.6","@semantic-release/commit-analyzer":"^11.1.0","@semantic-release/release-notes-generator":"^12.1.0"},"_npmOperationalInternal":{"tmp":"tmp/builder_2.0.1_1769663541631_0.49928539366382263","host":"s3://npm-registry-packages-npm-production"}},"2.0.2":{"name":"@pushforge/builder","version":"2.0.2","keywords":["David Raphi","David","draphy","david","draphi","draphy.org","www.draphy.org","non profit organization","open source","web push","pushforge","@pushforge/builder","pushforge builder","draphy pushforge builder","draphy pushforge","web push builder","web push notification","web push protocol","pushforge npm","web push service","@pushforge/builder npm package","web push npm package","web push vapid","notification","cross-platform","cross platform","web-push","webcrypto","vapid","web-push-protocol","typescript","node","cloudflare","browser","ecdh","hkdf","push-service"],"author":{"name":"David Raphi","email":"david@draphy.org"},"license":"MIT","_id":"@pushforge/builder@2.0.2","maintainers":[{"name":"draphy","email":"david@draphy.org"}],"homepage":"https://github.com/draphy/pushforge","bugs":{"url":"https://github.com/draphy/pushforge/issues"},"bin":{"pushforge":"dist/lib/commandLine/keys.js"},"dist":{"shasum":"ad2ec0342a6e2df4c7f179cb1e54cb52de7db172","tarball":"https://registry.npmjs.org/@pushforge/builder/-/builder-2.0.2.tgz","fileCount":25,"integrity":"sha512-41RYUH8f/1BBQaTcJB9U/0+TrErGaEPMlWQoWavp38/SlElCYZYHXOlSBdGfEFVyRfDntfmDhK93aoDFiwIPnQ==","signatures":[{"sig":"MEUCIHRtz/7I668Opfo/+8soeXsKCwogn/pwkNYMzyAylN5/AiEAmm7V3Vs5xwSp8LShxdovkbdx3uLNAR+Ws9O0nAd5a6s=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@pushforge%2fbuilder@2.0.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":57081},"main":"dist/lib/main.js","type":"module","types":"dist/lib/main.d.ts","module":"dist/lib/main.js","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/lib/main.d.ts","default":"./dist/lib/main.js"}},"gitHead":"2945a298f7d23227f5d78569cc34f5fc826cd741","private":false,"scripts":{"test":"vitest run","build":"tsc --build","test:watch":"vitest","semantic-release":"semantic-release"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:834e65ad-b854-4d48-abda-a0045b476edf"}},"repository":{"url":"git+https://github.com/draphy/pushforge.git","type":"git"},"_npmVersion":"11.9.0","description":"A robust, cross-platform Web Push notification library that handles VAPID authentication and payload encryption following the Web Push Protocol standard. Works in Node.js 16+, Browsers, Deno, Bun and Cloudflare Workers.","directories":{},"_nodeVersion":"24.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.1.2","@types/node":"^22.14.1","semantic-release":"^24.2.3","@semantic-release/npm":"^11.0.3","@semantic-release/github":"^9.2.6","@semantic-release/commit-analyzer":"^11.1.0","@semantic-release/release-notes-generator":"^12.1.0"},"_npmOperationalInternal":{"tmp":"tmp/builder_2.0.2_1774561437193_0.5891454931841995","host":"s3://npm-registry-packages-npm-production"}},"2.0.3":{"name":"@pushforge/builder","version":"2.0.3","keywords":["David Raphi","David","draphy","david","draphi","draphy.org","www.draphy.org","non profit organization","open source","web push","pushforge","@pushforge/builder","pushforge builder","draphy pushforge builder","draphy pushforge","web push builder","web push notification","web push protocol","pushforge npm","web push service","@pushforge/builder npm package","web push npm package","web push vapid","notification","cross-platform","cross platform","web-push","webcrypto","vapid","web-push-protocol","typescript","node","cloudflare","browser","ecdh","hkdf","push-service"],"author":{"name":"David Raphi","email":"david@draphy.org"},"license":"MIT","_id":"@pushforge/builder@2.0.3","maintainers":[{"name":"draphy","email":"david@draphy.org"}],"homepage":"https://github.com/draphy/pushforge","bugs":{"url":"https://github.com/draphy/pushforge/issues"},"bin":{"pushforge":"dist/lib/commandLine/keys.js"},"dist":{"shasum":"e69151db916d2f0aaaf87f75365de9831915aca4","tarball":"https://registry.npmjs.org/@pushforge/builder/-/builder-2.0.3.tgz","fileCount":25,"integrity":"sha512-lGKYtPg/RMHhNs/KNi4ZEJpjCY33xfldMVixeJDK7rsCDNeUU39DEme2+vKD8E/5brtvC6Wc/4ejBbj6SqnqAA==","signatures":[{"sig":"MEQCIBtw1IB+UiQNIgOy9hmYmYv5JaYYInn9RMetxfVHMNiKAiAwIUvhiFjVMx2m0YVYQNxzAhKbii43ptdja6CwGBfLOQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@pushforge%2fbuilder@2.0.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":57205},"main":"dist/lib/main.js","type":"module","types":"dist/lib/main.d.ts","module":"dist/lib/main.js","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/lib/main.d.ts","default":"./dist/lib/main.js"}},"gitHead":"f41951916dbf7d9b7ac32c5418afc8a0b5cea9c6","private":false,"scripts":{"test":"vitest run","build":"tsc --build","test:watch":"vitest","semantic-release":"semantic-release"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:834e65ad-b854-4d48-abda-a0045b476edf"}},"repository":{"url":"git+https://github.com/draphy/pushforge.git","type":"git"},"_npmVersion":"11.11.0","description":"A robust, cross-platform Web Push notification library that handles VAPID authentication and payload encryption following the Web Push Protocol standard. Works in Node.js 16+, Browsers, Deno, Bun and Cloudflare Workers.","directories":{},"_nodeVersion":"24.14.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.1.2","@types/node":"^22.14.1","semantic-release":"^24.2.3","@semantic-release/npm":"^11.0.3","@semantic-release/github":"^9.2.6","@semantic-release/commit-analyzer":"^11.1.0","@semantic-release/release-notes-generator":"^12.1.0"},"_npmOperationalInternal":{"tmp":"tmp/builder_2.0.3_1775434970050_0.025085400249877043","host":"s3://npm-registry-packages-npm-production"}},"2.0.4":{"name":"@pushforge/builder","version":"2.0.4","keywords":["David Raphi","David","draphy","david","draphi","draphy.org","www.draphy.org","non profit organization","open source","web push","pushforge","@pushforge/builder","pushforge builder","draphy pushforge builder","draphy pushforge","web push builder","web push notification","web push protocol","pushforge npm","web push service","@pushforge/builder npm package","web push npm package","web push vapid","notification","cross-platform","cross platform","web-push","webcrypto","vapid","web-push-protocol","typescript","node","cloudflare","browser","ecdh","hkdf","push-service"],"author":{"name":"David Raphi","email":"david@draphy.org"},"license":"MIT","_id":"@pushforge/builder@2.0.4","maintainers":[{"name":"draphy","email":"david@draphy.org"}],"homepage":"https://github.com/draphy/pushforge","bugs":{"url":"https://github.com/draphy/pushforge/issues"},"bin":{"pushforge":"dist/lib/commandLine/keys.js"},"dist":{"shasum":"6c7477330b6cae0eead6a96045097c50faf4382b","tarball":"https://registry.npmjs.org/@pushforge/builder/-/builder-2.0.4.tgz","fileCount":25,"integrity":"sha512-3uqkAEIpB9WGa+MF4/Q4LKjDkZ5GzbliLHw5yzlJ1tN80bbAw3SLW2C1XCstb6pgDCJV9lUgsDdLtIsArJ5LTw==","signatures":[{"sig":"MEYCIQCmlXwsQlG4AN0WWWKy35S+xuzZF1dkslwJHy5ccPpNXwIhALP6pbCRyAEWexPef4fCgnwsz/VR/oaJQa2ZmyCoihRI","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@pushforge%2fbuilder@2.0.4","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":57247},"main":"dist/lib/main.js","type":"module","types":"dist/lib/main.d.ts","module":"dist/lib/main.js","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/lib/main.d.ts","default":"./dist/lib/main.js"}},"gitHead":"08d34241859b1950059541f73d0c0accb539d72c","private":false,"scripts":{"test":"vitest run","build":"tsc --build","test:watch":"vitest","semantic-release":"semantic-release"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:834e65ad-b854-4d48-abda-a0045b476edf"}},"repository":{"url":"git+https://github.com/draphy/pushforge.git","type":"git"},"_npmVersion":"11.11.0","description":"A robust, cross-platform Web Push notification library that handles VAPID authentication and payload encryption following the Web Push Protocol standard. Works in Node.js 16+, Browsers, Deno, Bun and Cloudflare Workers.","directories":{},"_nodeVersion":"24.14.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.1.2","@types/node":"^22.14.1","semantic-release":"^24.2.3","@semantic-release/npm":"^11.0.3","@semantic-release/github":"^9.2.6","@semantic-release/commit-analyzer":"^11.1.0","@semantic-release/release-notes-generator":"^12.1.0"},"_npmOperationalInternal":{"tmp":"tmp/builder_2.0.4_1775682499618_0.4900885723486863","host":"s3://npm-registry-packages-npm-production"}},"2.0.5":{"name":"@pushforge/builder","version":"2.0.5","description":"Zero-dependency Web Push library for Cloudflare Workers, Vercel Edge, Convex, Deno, Bun, and Node.js 20+. A web-push alternative that uses Web Crypto API instead of Node.js crypto.","private":false,"main":"dist/lib/main.js","module":"dist/lib/main.js","types":"dist/lib/main.d.ts","author":{"name":"David Raphi","email":"david@draphy.org"},"bugs":{"url":"https://github.com/draphy/pushforge/issues"},"engines":{"node":">=20.0.0"},"repository":{"type":"git","url":"git+https://github.com/draphy/pushforge.git"},"homepage":"https://pushforge.draphy.org","license":"MIT","type":"module","exports":{".":{"types":"./dist/lib/main.d.ts","default":"./dist/lib/main.js"}},"publishConfig":{"access":"public"},"scripts":{"build":"tsc --build","test":"vitest run","test:watch":"vitest","semantic-release":"semantic-release"},"bin":{"pushforge":"dist/lib/commandLine/keys.js"},"devDependencies":{"@semantic-release/commit-analyzer":"^11.1.0","@semantic-release/github":"^9.2.6","@semantic-release/npm":"^11.0.3","@semantic-release/release-notes-generator":"^12.1.0","@types/node":"^22.14.1","semantic-release":"^24.2.3","vitest":"^3.1.2"},"keywords":["pushforge","web-push","web-push alternative","web push","push notifications","vapid","cloudflare workers","vercel edge","convex","deno","bun","nodejs","edge runtime","edge functions","zero dependencies","web crypto api","crypto.createECDH","typescript","fcm","apns","service worker","pwa"],"gitHead":"4acea6942d45cd58d5bbf5b1ad8275db1264fd36","_id":"@pushforge/builder@2.0.5","_nodeVersion":"24.15.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-9Q6lBFWUMKUL5vBknwAt+nlS7eWCrNuf1lCMAhC1Iypn8gtYJ0rdVHAnsGFMxPKhbtgkgpU3r6UVduDTQ8jgzw==","shasum":"3ee1d90d6fe0ef191505384d40b0bd94f323c278","tarball":"https://registry.npmjs.org/@pushforge/builder/-/builder-2.0.5.tgz","fileCount":25,"unpackedSize":62028,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@pushforge%2fbuilder@2.0.5","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIAmZAVMXDv1kyQ8xQDWvv3WnkfTZ+V7l95bg0iH6+da8AiBxDpHsKyzH52xN98Osb5TdTUkuVByzZU144MFM53DiNg=="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:834e65ad-b854-4d48-abda-a0045b476edf"}},"directories":{},"maintainers":[{"name":"draphy","email":"david@draphy.org"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/builder_2.0.5_1776960565686_0.2520600798882717"},"_hasShrinkwrap":false}},"time":{"created":"2025-04-21T23:29:45.648Z","modified":"2026-04-23T16:09:26.129Z","1.0.0":"2025-04-21T23:29:45.930Z","1.0.0-beta.1":"2025-04-22T01:43:33.517Z","1.1.0":"2025-07-26T07:35:23.900Z","1.1.1":"2025-10-28T03:41:20.369Z","1.1.2":"2025-11-09T11:47:03.284Z","2.0.0":"2026-01-29T04:29:03.571Z","2.0.1":"2026-01-29T05:12:21.760Z","2.0.2":"2026-03-26T21:43:57.347Z","2.0.3":"2026-04-06T00:22:50.340Z","2.0.4":"2026-04-08T21:08:19.751Z","2.0.5":"2026-04-23T16:09:25.836Z"},"bugs":{"url":"https://github.com/draphy/pushforge/issues"},"author":{"name":"David Raphi","email":"david@draphy.org"},"license":"MIT","homepage":"https://pushforge.draphy.org","keywords":["pushforge","web-push","web-push alternative","web push","push notifications","vapid","cloudflare workers","vercel edge","convex","deno","bun","nodejs","edge runtime","edge functions","zero dependencies","web crypto api","crypto.createECDH","typescript","fcm","apns","service worker","pwa"],"repository":{"type":"git","url":"git+https://github.com/draphy/pushforge.git"},"description":"Zero-dependency Web Push library for Cloudflare Workers, Vercel Edge, Convex, Deno, Bun, and Node.js 20+. A web-push alternative that uses Web Crypto API instead of Node.js crypto.","maintainers":[{"name":"draphy","email":"david@draphy.org"}],"readme":"<div align=\"center\">\r\n\r\n<img src=\"https://raw.githubusercontent.com/draphy/pushforge/master/images/logo.webp\" alt=\"PushForge Logo\" width=\"120\" />\r\n\r\n# PushForge Builder\r\n\r\n**A lightweight, dependency-free Web Push library built on the standard Web Crypto API.**\r\n\r\n[![npm version](https://img.shields.io/npm/v/@pushforge/builder.svg)](https://www.npmjs.com/package/@pushforge/builder)\r\n[![npm downloads](https://img.shields.io/npm/dm/@pushforge/builder?logo=npm&color=brightgreen&cacheSeconds=86400)](https://www.npmjs.com/package/@pushforge/builder)\r\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)\r\n[![TypeScript](https://img.shields.io/badge/TypeScript-first--class-blue.svg)](https://www.typescriptlang.org/)\r\n\r\nSend push notifications from any JavaScript runtime · Zero dependencies\r\n\r\n[GitHub](https://github.com/draphy/pushforge) · [npm](https://www.npmjs.com/package/@pushforge/builder) · [Report Bug](https://github.com/draphy/pushforge/issues)\r\n\r\n**[Try the Playground →](https://pushforge.draphy.org)**\r\n\r\n</div>\r\n\r\n---\r\n\r\n```bash\r\nnpm install @pushforge/builder\r\n```\r\n\r\n## Playground\r\n\r\nTest PushForge in your browser at **[pushforge.draphy.org](https://pushforge.draphy.org)** — an interactive playground for testing push notifications, powered by Cloudflare Workers.\r\n\r\n- **Quick Test** — enable notifications, send a test message, see it arrive in real time\r\n- **Topic Channels** — test targeted notifications by subscribing to specific channels\r\n- **Notification Customization** — experiment with title, body, icon, image, action buttons, vibration, click URL\r\n- **Push Options** — test urgency levels (battery hints) and TTL (message expiry)\r\n- **Cross-Browser** — test across Chrome, Firefox, Edge, Safari 16+\r\n- Subscriptions auto-expire (5 min for quick test, 1 hour for topics) — no permanent data stored\r\n- The backend is a single Cloudflare Worker using `buildPushHTTPRequest()` with zero dependencies\r\n\r\n## Why PushForge?\r\n\r\n| | PushForge | web-push |\r\n|---|:---:|:---:|\r\n| Dependencies | **0** | 5+ (with nested deps) |\r\n| Cloudflare Workers | Yes | [No](https://github.com/web-push-libs/web-push/issues/718) |\r\n| Vercel Edge | Yes | No |\r\n| Convex | Yes* | No |\r\n| Deno / Bun | Yes | Limited |\r\n| TypeScript | First-class | @types package |\r\n\r\n\\* Convex requires `\"use node\";` directive. [See example](#convex).\r\n\r\nTraditional web push libraries rely on Node.js-specific APIs (`crypto.createECDH`, `https.request`) that don't work in modern edge runtimes. PushForge uses the standard [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API), making it portable across all JavaScript environments.\r\n\r\n## Quick Start\r\n\r\n### 1. Generate VAPID Keys\r\n\r\n```bash\r\nnpx @pushforge/builder vapid\r\n```\r\n\r\nThis outputs a public key (for your frontend) and a private key in JWK format (for your server).\r\n\r\n### 2. Subscribe Users (Frontend)\r\n\r\nUse the VAPID public key to subscribe users to push notifications:\r\n\r\n```javascript\r\n// In your frontend application\r\nconst registration = await navigator.serviceWorker.ready;\r\n\r\nconst subscription = await registration.pushManager.subscribe({\r\n  userVisibleOnly: true,\r\n  applicationServerKey: 'YOUR_VAPID_PUBLIC_KEY' // From step 1\r\n});\r\n\r\n// Send this subscription to your server\r\n// subscription.toJSON() returns:\r\n// {\r\n//   endpoint: \"https://fcm.googleapis.com/fcm/send/...\",\r\n//   keys: {\r\n//     p256dh: \"BNcRd...\",\r\n//     auth: \"tBHI...\"\r\n//   }\r\n// }\r\nawait fetch('/api/subscribe', {\r\n  method: 'POST',\r\n  body: JSON.stringify(subscription)\r\n});\r\n```\r\n\r\n### 3. Send Notifications (Server)\r\n\r\n```typescript\r\nimport { buildPushHTTPRequest } from \"@pushforge/builder\";\r\n\r\n// Your VAPID private key (JWK format from step 1)\r\nconst privateJWK = {\r\n  kty: \"EC\",\r\n  crv: \"P-256\",\r\n  x: \"...\",\r\n  y: \"...\",\r\n  d: \"...\"\r\n};\r\n\r\n// The subscription object from the user's browser\r\nconst subscription = {\r\n  endpoint: \"https://fcm.googleapis.com/fcm/send/...\",\r\n  keys: {\r\n    p256dh: \"BNcRd...\",\r\n    auth: \"tBHI...\"\r\n  }\r\n};\r\n\r\n// Build and send the notification\r\nconst { endpoint, headers, body } = await buildPushHTTPRequest({\r\n  privateJWK,\r\n  subscription,\r\n  message: {\r\n    payload: {\r\n      title: \"New Message\",\r\n      body: \"You have a new notification!\",\r\n      icon: \"/icon.png\"\r\n    },\r\n    adminContact: \"mailto:admin@example.com\"\r\n  }\r\n});\r\n\r\nconst response = await fetch(endpoint, {\r\n  method: \"POST\",\r\n  headers,\r\n  body\r\n});\r\n\r\nif (response.status === 201) {\r\n  console.log(\"Notification sent\");\r\n}\r\n```\r\n\r\n## Understanding Push Subscriptions\r\n\r\nWhen a user subscribes to push notifications, the browser returns a `PushSubscription` object:\r\n\r\n```javascript\r\n{\r\n  // The unique URL for this user's browser push service\r\n  endpoint: \"https://fcm.googleapis.com/fcm/send/dAPT...\",\r\n\r\n  keys: {\r\n    // Public key for encrypting messages (base64url)\r\n    p256dh: \"BNcRdreALRFXTkOOUHK1EtK2wtaz5Ry4YfYCA...\",\r\n\r\n    // Authentication secret (base64url)\r\n    auth: \"tBHItJI5svbpez7KI4CCXg==\"\r\n  }\r\n}\r\n```\r\n\r\n| Field | Description |\r\n|-------|-------------|\r\n| `endpoint` | The push service URL. Each browser vendor has their own (Google FCM, Mozilla autopush, Apple APNs). |\r\n| `p256dh` | The user's public key for ECDH P-256 message encryption. |\r\n| `auth` | A shared 16-byte authentication secret. |\r\n\r\nStore these securely on your server. You'll need them to send notifications to this user.\r\n\r\n## API Reference\r\n\r\n### `buildPushHTTPRequest(options)`\r\n\r\nBuilds an HTTP request for sending a push notification.\r\n\r\n```typescript\r\nconst { endpoint, headers, body } = await buildPushHTTPRequest({\r\n  privateJWK,    // Your VAPID private key (JWK object or JSON string)\r\n  subscription,  // User's push subscription\r\n  message: {\r\n    payload,       // Any JSON-serializable data\r\n    adminContact,  // Contact email (mailto:...) or URL\r\n    options: {     // Optional\r\n      ttl,         // Time-to-live in seconds (default: 86400, max: 86400)\r\n      urgency,     // \"very-low\" | \"low\" | \"normal\" | \"high\"\r\n      topic        // Topic for notification replacement\r\n    }\r\n  }\r\n});\r\n```\r\n\r\n**Returns:** `{ endpoint: string, headers: Headers, body: ArrayBuffer }`\r\n\r\n#### Parameters\r\n\r\n| Parameter | Type | Required | Description |\r\n|-----------|------|----------|-------------|\r\n| `privateJWK` | JsonWebKey \\| string | Yes | Your VAPID private key (JWK object or JSON string) |\r\n| `subscription` | PushSubscription | Yes | User's push subscription with `endpoint` and `keys` |\r\n| `message.payload` | any | Yes | Any JSON-serializable data to send (see [Notification Payload](#notification-payload)) |\r\n| `message.adminContact` | string | Yes | Contact for push service (`mailto:you@example.com` or URL) |\r\n| `message.options` | object | No | Push delivery options (see below) |\r\n\r\n#### Push Options (Web Push Protocol Headers)\r\n\r\nThese options control how the push service handles message delivery:\r\n\r\n| Option | Type | Default | Description |\r\n|--------|------|---------|-------------|\r\n| `ttl` | number | 86400 | Time-to-live in seconds. How long the push service retains the message if user is offline. Max 24 hours. |\r\n| `urgency` | string | - | Battery hint: `\"very-low\"` (ads), `\"low\"` (topic updates), `\"normal\"` (chat), `\"high\"` (calls/time-sensitive). |\r\n| `topic` | string | - | Topic identifier. New message with same topic replaces pending one at push service level (before delivery). |\r\n\r\n#### TypeScript Types\r\n\r\nFor TypeScript users, these types are exported:\r\n\r\n```typescript\r\nimport type { \r\n  BuilderOptions,    // Parameter type for buildPushHTTPRequest\r\n  PushMessage,       // The message object type\r\n  PushSubscription   // The subscription object type\r\n} from \"@pushforge/builder\";\r\n```\r\n\r\n## Platform Examples\r\n\r\n### Cloudflare Workers\r\n\r\n```javascript\r\nexport default {\r\n  async fetch(request, env) {\r\n    const subscription = await request.json();\r\n\r\n    const { endpoint, headers, body } = await buildPushHTTPRequest({\r\n      privateJWK: JSON.parse(env.VAPID_PRIVATE_KEY),\r\n      subscription,\r\n      message: {\r\n        payload: { title: \"Hello from the Edge!\" },\r\n        adminContact: \"mailto:admin@example.com\"\r\n      }\r\n    });\r\n\r\n    return fetch(endpoint, { method: \"POST\", headers, body });\r\n  }\r\n};\r\n```\r\n\r\n### Vercel Edge Functions\r\n\r\n```typescript\r\nimport { buildPushHTTPRequest } from \"@pushforge/builder\";\r\n\r\nexport const config = { runtime: \"edge\" };\r\n\r\nexport default async function handler(request: Request) {\r\n  const subscription = await request.json();\r\n\r\n  const { endpoint, headers, body } = await buildPushHTTPRequest({\r\n    privateJWK: JSON.parse(process.env.VAPID_PRIVATE_KEY!),\r\n    subscription,\r\n    message: {\r\n      payload: { title: \"Edge Notification\" },\r\n      adminContact: \"mailto:admin@example.com\"\r\n    }\r\n  });\r\n\r\n  await fetch(endpoint, { method: \"POST\", headers, body });\r\n  return new Response(\"Sent\", { status: 200 });\r\n}\r\n```\r\n\r\n### Convex\r\n\r\n> **Note:** Convex's default runtime doesn't support ECDH operations required by Web Push. Add `\"use node\";` to use the Node.js runtime.\r\n\r\n```typescript\r\n\"use node\";\r\n\r\nimport { action } from \"./_generated/server\";\r\nimport { buildPushHTTPRequest } from \"@pushforge/builder\";\r\nimport { v } from \"convex/values\";\r\n\r\nexport const sendPush = action({\r\n  args: { subscription: v.any(), title: v.string(), body: v.string() },\r\n  handler: async (ctx, { subscription, title, body }) => {\r\n    const { endpoint, headers, body: reqBody } = await buildPushHTTPRequest({\r\n      privateJWK: JSON.parse(process.env.VAPID_PRIVATE_KEY!),\r\n      subscription,\r\n      message: {\r\n        payload: { title, body },\r\n        adminContact: \"mailto:admin@example.com\"\r\n      }\r\n    });\r\n\r\n    await fetch(endpoint, { method: \"POST\", headers, body: reqBody });\r\n  }\r\n});\r\n```\r\n\r\n### Deno\r\n\r\n```typescript\r\nimport { buildPushHTTPRequest } from \"npm:@pushforge/builder\";\r\n\r\nconst { endpoint, headers, body } = await buildPushHTTPRequest({\r\n  privateJWK: JSON.parse(Deno.env.get(\"VAPID_PRIVATE_KEY\")!),\r\n  subscription,\r\n  message: {\r\n    payload: { title: \"Hello from Deno!\" },\r\n    adminContact: \"mailto:admin@example.com\"\r\n  }\r\n});\r\n\r\nawait fetch(endpoint, { method: \"POST\", headers, body });\r\n```\r\n\r\n### Bun\r\n\r\n```typescript\r\nimport { buildPushHTTPRequest } from \"@pushforge/builder\";\r\n\r\nconst { endpoint, headers, body } = await buildPushHTTPRequest({\r\n  privateJWK: JSON.parse(Bun.env.VAPID_PRIVATE_KEY!),\r\n  subscription,\r\n  message: {\r\n    payload: { title: \"Hello from Bun!\" },\r\n    adminContact: \"mailto:admin@example.com\"\r\n  }\r\n});\r\n\r\nawait fetch(endpoint, { method: \"POST\", headers, body });\r\n```\r\n\r\n## How It Works\r\n\r\n```\r\nYour Server (PushForge) → Push Service (FCM/APNs) → Service Worker → User's Device\r\n```\r\n\r\n**PushForge handles:**\r\n- Encrypts payload\r\n- Signs with VAPID\r\n- Sets ttl/urgency/topic headers\r\n\r\n**Your service worker handles:**\r\n- Displays notification (title, body, icon, actions, etc.)\r\n- Handles clicks\r\n\r\n## Notification Payload\r\n\r\nThe `payload` field accepts any JSON-serializable data — PushForge encrypts and delivers it as-is. Your service worker receives this payload and passes it to the browser's `showNotification()` API.\r\n\r\n> **Note:** These are standard [Web Notifications API](https://developer.mozilla.org/en-US/docs/Web/API/Notification) options, not PushForge-specific. PushForge handles the transport; your service worker handles the display.\r\n\r\nCommon fields:\r\n\r\n| Field | Type | Description |\r\n|-------|------|-------------|\r\n| `title` | string | Notification title (required) |\r\n| `body` | string | Notification body text |\r\n| `icon` | string | URL for the notification icon |\r\n| `badge` | string | URL for the badge (small monochrome icon) |\r\n| `image` | string | URL for a large image |\r\n| `dir` | string | Text direction: `\"auto\"`, `\"ltr\"`, or `\"rtl\"` |\r\n| `lang` | string | Language tag (e.g., `\"en-US\"`, `\"es\"`) |\r\n| `tag` | string | Tag for notification replacement (same tag = replace, not stack) |\r\n| `renotify` | boolean | Vibrate/alert again when replacing a notification with same tag |\r\n| `requireInteraction` | boolean | Keep notification visible until user interacts |\r\n| `silent` | boolean | Suppress sound and vibration |\r\n| `timestamp` | number | Timestamp in milliseconds (e.g., `Date.now()`) |\r\n| `vibrate` | number[] | Vibration pattern `[vibrate, pause, vibrate, ...]` |\r\n| `actions` | array | Action buttons (max 2): `[{ action: \"id\", title: \"Label\", icon?: \"url\" }]` |\r\n| `data` | object | Custom data (e.g., `{ url: \"/page\" }` for click handling) |\r\n\r\nExample with full options:\r\n\r\n```typescript\r\nconst { endpoint, headers, body } = await buildPushHTTPRequest({\r\n  privateJWK,\r\n  subscription,\r\n  message: {\r\n    payload: {\r\n      title: \"New Message\",\r\n      body: \"John: Hey, are you free?\",\r\n      icon: \"/icons/chat.png\",\r\n      badge: \"/icons/badge.png\",\r\n      image: \"/images/preview.jpg\",\r\n      tag: \"chat-john\",\r\n      renotify: true,\r\n      actions: [\r\n        { action: \"reply\", title: \"Reply\" },\r\n        { action: \"dismiss\", title: \"Dismiss\" }\r\n      ],\r\n      data: { url: \"/chat/john\", messageId: \"123\" }\r\n    },\r\n    adminContact: \"mailto:admin@example.com\",\r\n    options: { urgency: \"high\", ttl: 3600 }\r\n  }\r\n});\r\n```\r\n\r\n## Service Worker Setup\r\n\r\nHandle incoming push notifications in your service worker:\r\n\r\n```javascript\r\n// sw.js\r\nself.addEventListener('push', (event) => {\r\n  const data = event.data?.json() ?? {};\r\n\r\n  event.waitUntil(\r\n    self.registration.showNotification(data.title, {\r\n      body: data.body,\r\n      icon: data.icon,\r\n      badge: data.badge,\r\n      image: data.image,\r\n      dir: data.dir,\r\n      lang: data.lang,\r\n      tag: data.tag,\r\n      renotify: data.renotify,\r\n      requireInteraction: data.requireInteraction,\r\n      silent: data.silent,\r\n      timestamp: data.timestamp,\r\n      vibrate: data.vibrate,\r\n      actions: data.actions,\r\n      data: data.data\r\n    })\r\n  );\r\n});\r\n\r\nself.addEventListener('notificationclick', (event) => {\r\n  event.notification.close();\r\n\r\n  // Handle action button clicks\r\n  if (event.action === 'reply') {\r\n    clients.openWindow('/chat?action=reply');\r\n    return;\r\n  }\r\n\r\n  // Handle main notification click\r\n  const url = event.notification.data?.url || '/';\r\n  event.waitUntil(clients.openWindow(url));\r\n});\r\n```\r\n\r\n## Requirements\r\n\r\n**Node.js 20+** or any runtime with Web Crypto API support.\r\n\r\n| Environment | Status |\r\n|-------------|--------|\r\n| Node.js 20+ | Fully supported |\r\n| Cloudflare Workers | Fully supported |\r\n| Vercel Edge | Fully supported |\r\n| Deno | Fully supported |\r\n| Bun | Fully supported |\r\n| Convex | Requires `\"use node\";` ([example](#convex)) |\r\n| Modern Browsers | Fully supported |\r\n\r\n<details>\r\n<summary>Node.js 18 (requires polyfill)</summary>\r\n\r\n```javascript\r\nimport { webcrypto } from \"node:crypto\";\r\nglobalThis.crypto = webcrypto;\r\n\r\nimport { buildPushHTTPRequest } from \"@pushforge/builder\";\r\n```\r\n\r\nOr: `node --experimental-global-webcrypto your-script.js`\r\n\r\n</details>\r\n\r\n## Security\r\n\r\nPushForge validates all inputs before processing:\r\n\r\n- VAPID key structure (EC P-256 curve with required x, y, d parameters)\r\n- Subscription endpoint (must be valid HTTPS URL)\r\n- p256dh key format (65-byte uncompressed P-256 point)\r\n- Auth secret length (exactly 16 bytes)\r\n- Payload size (max 4KB per Web Push spec)\r\n- TTL bounds (max 24 hours per VAPID spec)\r\n\r\n## Contributing\r\n\r\nContributions welcome! See [CONTRIBUTING.md](https://github.com/draphy/pushforge/blob/master/CONTRIBUTING.md) for guidelines.\r\n\r\n## License\r\n\r\nMIT © [David Raphi](https://github.com/draphy)\r\n","readmeFilename":"README.md"}