node-webpush 1.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +3 -2
- package/dist/index.es.js.map +1 -1
- package/package.json +9 -8
package/dist/index.cjs.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const m=require("crypto");var w=(e=>(e.AES_GCM="aesgcm",e.AES_128_GCM="aes128gcm",e))(w||{}),A=(e=>(e.VERY_LOW="very-low",e.LOW="low",e.NORMAL="normal",e.HIGH="high",e))(A||{});const T=2419200,D=4096,P=18,f={validate(e){return/^[A-Za-z0-9\-_]+$/.test(e)},toBuffer(e){return Buffer.from(e,"base64url")},toString(e){return(typeof e=="string"?Buffer.from(e,"utf8"):Buffer.from(e)).toString("base64url")},toJSON(e){return this.toString(Buffer.from(JSON.stringify(e),"utf8"))}},N=720*60,$=1440*60;function L(e){const t=new Date;return t.setSeconds(t.getSeconds()+e),Math.floor(t.getTime()/1e3)}const E={subject(e){if(!e)throw new Error("No subject set in VAPID details.");const t=typeof e=="string"?new URL(e):e;if(!["https:","mailto:"].includes(t.protocol))throw new Error(`VAPID subject must be https: or mailto:. Got: ${t.toString()}`);t.hostname==="localhost"&&console.warn("VAPID subject points to localhost; some push services may reject this.")},publicKey(e){if(!e)throw new Error("No VAPID public key set.");if(typeof e!="string"||!f.validate(e))throw new Error('VAPID public key must be base64url without "=" padding.');const t=f.toBuffer(e);if(t.length!==65||t[0]!==4)throw new Error("VAPID public key must decode to 65-byte uncompressed P-256.")},privateKey(e){if(!e)throw new Error("No VAPID private key set.");if(typeof e!="string"||!f.validate(e))throw new Error('VAPID private key must be base64url without "=" padding.');if(f.toBuffer(e).length!==32)throw new Error("VAPID private key must decode to 32 bytes.")},expiration(e){if(!Number.isInteger(e))throw new Error("VAPID expiration must be an integer.");if(e<0)throw new Error("VAPID expiration must be positive.");const t=L($);if(e>=t)throw new Error("VAPID expiration exceeds 24 hours.")},audience(e){if(!e)throw new Error("No audience set for VAPID.");const t=typeof e=="string"?new URL(e):e;if(!t.origin||t.origin==="null")throw new Error(`VAPID audience must be an origin. Got: ${t.toString()}`)}};function G(){const e=m.createECDH("prime256v1");e.generateKeys();let t=e.getPublicKey(),r=e.getPrivateKey();return r.length<32&&(r=Buffer.concat([Buffer.alloc(32-r.length,0),r])),t.length<65&&(t=Buffer.concat([Buffer.alloc(65-t.length,0),t])),{publicKey:f.toString(t),privateKey:f.toString(r)}}function U(e){const t=f.toBuffer(e);if(t.length!==65||t[0]!==4)throw new Error("Invalid uncompressed P-256 public key.");return{x:f.toString(t.subarray(1,33)),y:f.toString(t.subarray(33,65))}}function j(e){const t=f.toBuffer(e);if(t.length!==32)throw new Error("Invalid P-256 private key scalar.");return f.toString(t)}function O(e,t=32){let r=0;if(e[r++]!==48)throw new Error("Invalid DER signature (expected SEQUENCE).");let n=e[r++];if(n&128){const l=n&127;r+=l}if(e[r++]!==2)throw new Error("Invalid DER signature (expected INTEGER r).");const c=e[r++];let o=e.subarray(r,r+c);if(r+=c,e[r++]!==2)throw new Error("Invalid DER signature (expected INTEGER s).");const i=e[r++];let s=e.subarray(r,r+i);if(o[0]===0&&(o=o.subarray(1)),s[0]===0&&(s=s.subarray(1)),o.length>t||s.length>t)throw new Error("Invalid DER signature size.");const u=Buffer.concat([Buffer.alloc(t-o.length,0),o]),d=Buffer.concat([Buffer.alloc(t-s.length,0),s]);return Buffer.concat([u,d])}function V(e,t,r){const n={typ:"JWT",alg:"ES256"},c=f.toJSON(n),o=f.toJSON(e),i=`${c}.${o}`,{x:s,y:u}=U(r),d=j(t),l=m.createPrivateKey({key:{kty:"EC",crv:"P-256",x:s,y:u,d},format:"jwk"}),y=m.sign("sha256",Buffer.from(i,"utf8"),{key:l,dsaEncoding:"der"}),h=O(y,32),g=f.toString(h);return`${i}.${g}`}function _(e){E.audience(e.audience),E.subject(e.subject),E.publicKey(e.publicKey),E.privateKey(e.privateKey);const t=(typeof e.audience=="string"?new URL(e.audience):e.audience).origin,r=(typeof e.subject=="string"?new URL(e.subject):e.subject).toString(),n=e.expiration??L(N);E.expiration(n);const c=V({aud:t,exp:n,sub:r},e.privateKey,e.publicKey),o=new Headers;if(e.contentEncoding===w.AES_128_GCM)o.set("Authorization",`vapid t=${c}, k=${e.publicKey}`);else if(e.contentEncoding===w.AES_GCM)o.set("Authorization",`WebPush ${c}`),o.set("Crypto-Key",`p256ecdsa=${e.publicKey}`);else throw new Error(`Unsupported content encoding: ${e.contentEncoding}`);return o}const F=Object.freeze(Object.defineProperty({__proto__:null,GenerateHeaders:_,GenerateKeys:G,Validate:E},Symbol.toStringTag,{value:"Module"}));function W(e){const t=Buffer.alloc(2);return t.writeUInt16BE(e,0),t}function H(e){const t=Buffer.alloc(4);return t.writeUInt32BE(e>>>0,0),t}function J(e,t){if(e.length!==t.length)throw new Error("xorBytes: length mismatch");const r=Buffer.alloc(e.length);for(let n=0;n<e.length;n++)r[n]=e[n]^t[n];return r}function z(e){if(!Number.isSafeInteger(e)||e<0)throw new Error("SEQ must be a non-negative safe integer");const t=Buffer.alloc(12,0),r=Math.floor(e/4294967296),n=e>>>0;return t.writeUInt32BE(r>>>0,4),t.writeUInt32BE(n,8),t}function X(e){const{message:t,rs:r,allowMultipleRecords:n,finalRecordPadding:c}=e;if(!Number.isInteger(r)||r<P)throw new Error(`Invalid rs; must be an integer >= ${P}.`);const o=r-16;if(o<=1)throw new Error("Invalid rs (too small)");const i=o-1;if(!n&&t.length>i)throw new Error(`Payload too large for a single RFC8188 record: ${t.length} bytes. Max is ${i} bytes for rs=${r}.`);if(!Number.isInteger(c)||c<0)throw new Error("finalRecordPadding must be a non-negative integer");const s=[];let u=0;for(;u<t.length;){const d=t.length-u,l=d<=i,y=l?d:i,h=t.subarray(u,u+y);u+=y;const g=Buffer.from([l?2:1]);if(l)s.push(Buffer.concat([h,g,Buffer.alloc(c,0)]));else{const a=o-(h.length+1);s.push(Buffer.concat([h,g,Buffer.alloc(a,0)]))}}return t.length===0&&s.push(Buffer.from([2])),s}function q(e){const{cek:t,baseNonce:r,plaintextRecords:n}=e;if(t.length!==16)throw new Error("CEK must be 16 bytes");if(r.length!==12)throw new Error("baseNonce must be 12 bytes");const c=[];for(let o=0;o<n.length;o++){const i=J(r,z(o)),s=m.createCipheriv("aes-128-gcm",t,i),u=Buffer.concat([s.update(n[o]),s.final()]);c.push(Buffer.concat([u,s.getAuthTag()]))}return Buffer.concat(c)}function b(e,t){return m.createHmac("sha256",e).update(t).digest()}function x(e,t){if(!e||typeof e!="string")throw new Error("subscription.keys.p256dh must be a string");if(!t||typeof t!="string")throw new Error("subscription.keys.auth must be a string");const r=f.toBuffer(e);if(r.length!==65||r[0]!==4)throw new Error("subscription.keys.p256dh must decode to a 65-byte uncompressed P-256 public key");if(f.toBuffer(t).length<16)throw new Error("subscription.keys.auth must decode to at least 16 bytes")}function Q(e){x(e.p256dh,e.auth);const t=f.toBuffer(e.p256dh),r=f.toBuffer(e.auth),n=m.createECDH("prime256v1");n.generateKeys();const c=n.getPublicKey(),o=n.computeSecret(t),i=m.randomBytes(16),s=b(r,o),u=Buffer.concat([Buffer.from("WebPush: info","utf8"),Buffer.from([0]),t,c]),d=b(s,Buffer.concat([u,Buffer.from([1])])),l=b(i,d),y=Buffer.concat([Buffer.from("Content-Encoding: aes128gcm","utf8"),Buffer.from([0])]),h=Buffer.concat([Buffer.from("Content-Encoding: nonce","utf8"),Buffer.from([0])]),g=b(l,Buffer.concat([y,Buffer.from([1])])).subarray(0,16),a=b(l,Buffer.concat([h,Buffer.from([1])])).subarray(0,12),B=X({message:e.payload,rs:e.rs,allowMultipleRecords:e.allowMultipleRecords,finalRecordPadding:e.finalRecordPadding}),I=q({cek:g,baseNonce:a,plaintextRecords:B});if(c.length!==65)throw new Error("Sender public key must be 65 bytes");return Buffer.concat([i,H(e.rs),Buffer.from([c.length]),c,I])}function Y(e){x(e.p256dh,e.auth);const t=f.toBuffer(e.p256dh),r=f.toBuffer(e.auth),n=m.createECDH("prime256v1");n.generateKeys();const c=n.getPublicKey(),o=n.computeSecret(t),i=m.randomBytes(16),s=b(r,o),u=Buffer.concat([Buffer.from("WebPush: info","utf8"),Buffer.from([0]),t,c]),d=b(s,Buffer.concat([u,Buffer.from([1])])),l=b(i,d),y=Buffer.concat([Buffer.from("Content-Encoding: aesgcm","utf8"),Buffer.from([0])]),h=Buffer.concat([Buffer.from("Content-Encoding: nonce","utf8"),Buffer.from([0])]),g=b(l,Buffer.concat([y,Buffer.from([1])])).subarray(0,16),a=b(l,Buffer.concat([h,Buffer.from([1])])).subarray(0,12),B=0,I=Buffer.concat([W(B),Buffer.alloc(B,0),e.payload]),v=m.createCipheriv("aes-128-gcm",g,a),k=Buffer.concat([v.update(I),v.final()]),R=v.getAuthTag();return{saltB64Url:f.toString(i),localPublicKey:c,ciphertext:Buffer.concat([k,R])}}class M extends Error{constructor(t,r){super(t),this.response=r}}function Z(e){if(!f.validate(e))throw new Error("Topic contains invalid characters; must be URL-safe base64 chars.");if(e.length>32)throw new Error("Topic must be <= 32 characters.")}function ee(e){return e.startsWith("https://android.googleapis.com/gcm/send")}function te(e){return e.startsWith("https://fcm.googleapis.com/fcm/send")||e.includes("fcm.googleapis.com")}function re(e){return new URL(e).origin}function ne(e){if(!Object.values(w).includes(e))throw new Error(`Unsupported content encoding: ${e}`)}function oe(e){if(!Object.values(A).includes(e))throw new Error(`Unsupported urgency: ${e}`)}class ce{constructor(t){if(this.config=t,E.subject(t.vapid.subject),E.privateKey(t.vapid.privateKey),E.publicKey(t.vapid.publicKey),t.gcm?.apiKey!=null&&t.gcm.apiKey.length===0)throw new Error("The GCM/FCM API Key should be a non-empty string, null, or undefined.")}generateRequest(t,r,n){if(!t?.endpoint||typeof t.endpoint!="string"||t.endpoint.length===0)throw new Error("You must pass a subscription with a valid endpoint URL.");const c=n?.TTL??T;if(!Number.isFinite(c)||c<0)throw new Error("TTL must be a number >= 0.");const o=n?.contentEncoding??w.AES_128_GCM;ne(o);const i=n?.urgency??A.NORMAL;oe(i);const s=n?.topic;s&&Z(s);const u=n?.allowMultipleRecords??!1,d=n?.rs??D;if(!Number.isInteger(d)||d<P)throw new Error(`rs must be an integer >= ${P}.`);const l=n?.finalRecordPadding??0,y=n?.headers??{},h=n?.gcmAPIKey??this.config.gcm?.apiKey??null,g=n?.vapidDetails===void 0?this.config.vapid:n.vapidDetails,a={TTL:String(c),Urgency:i,...y};s&&(a.Topic=s);let B;if(r!=null&&r!==""){if(!t.keys?.p256dh||!t.keys?.auth)throw new Error("To send a payload, the subscription must include 'keys.p256dh' and 'keys.auth'.");const S=Buffer.isBuffer(r)?r:r instanceof Uint8Array?Buffer.from(r):Buffer.from(r,"utf8");if(a["Content-Type"]="application/octet-stream",o===w.AES_128_GCM){const p=Q({p256dh:t.keys.p256dh,auth:t.keys.auth,payload:S,rs:d,allowMultipleRecords:u,finalRecordPadding:l});B=p,a["Content-Encoding"]=w.AES_128_GCM,a["Content-Length"]=String(p.length)}else{const p=Y({p256dh:t.keys.p256dh,auth:t.keys.auth,payload:S});B=p.ciphertext,a["Content-Encoding"]=w.AES_GCM,a["Content-Length"]=String(p.ciphertext.length),a.Encryption=`salt=${p.saltB64Url}`,a["Crypto-Key"]=`dh=${f.toString(p.localPublicKey)}`}}else a["Content-Length"]="0";const v=t.endpoint,k=ee(v),R=te(v);if(k)h?a.Authorization=`key=${h}`:console.warn("GCM endpoint detected but no GCM API key provided.");else if(g){const S=re(v),p=_({audience:S,subject:g.subject,publicKey:g.publicKey,privateKey:g.privateKey,contentEncoding:o}),C=p.get("Authorization");C&&(a.Authorization=C);const K=p.get("Crypto-Key");K&&o===w.AES_GCM&&(a["Crypto-Key"]=a["Crypto-Key"]?`${a["Crypto-Key"]}; ${K}`:K)}else R&&h&&(a.Authorization=`key=${h}`);return{endpoint:v,init:{method:"POST",headers:a,body:B}}}async notify(t,r,n){const{endpoint:c,init:o}=this.generateRequest(t,r,n),i=await fetch(c,o);if(!i.ok&&n?.throwOnInvalidResponse)throw new M("Received unexpected response code",i);return i}}exports.DEFAULT_RS=D;exports.DEFAULT_TTL=T;exports.MIN_RS=P;exports.SupportedContentEncoding=w;exports.SupportedUrgency=A;exports.VAPID=F;exports.WebPush=ce;exports.WebPushError=M;
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const m=require("crypto");var w=(e=>(e.AES_GCM="aesgcm",e.AES_128_GCM="aes128gcm",e))(w||{}),A=(e=>(e.VERY_LOW="very-low",e.LOW="low",e.NORMAL="normal",e.HIGH="high",e))(A||{});const T=2419200,D=4096,P=18,f={validate(e){return/^[A-Za-z0-9\-_]+$/.test(e)},toBuffer(e){return Buffer.from(e,"base64url")},toString(e){return(typeof e=="string"?Buffer.from(e,"utf8"):Buffer.from(e)).toString("base64url")},toJSON(e){return this.toString(Buffer.from(JSON.stringify(e),"utf8"))}},N=720*60,$=1440*60;function L(e){const t=new Date;return t.setSeconds(t.getSeconds()+e),Math.floor(t.getTime()/1e3)}const E={subject(e){if(!e)throw new Error("No subject set in VAPID details.");const t=typeof e=="string"?new URL(e):e;if(!["https:","mailto:"].includes(t.protocol))throw new Error(`VAPID subject must be https: or mailto:. Got: ${t.toString()}`);t.hostname==="localhost"&&console.warn("VAPID subject points to localhost; some push services may reject this.")},publicKey(e){if(!e)throw new Error("No VAPID public key set.");if(typeof e!="string"||!f.validate(e))throw new Error('VAPID public key must be base64url without "=" padding.');const t=f.toBuffer(e);if(t.length!==65||t[0]!==4)throw new Error("VAPID public key must decode to 65-byte uncompressed P-256.")},privateKey(e){if(!e)throw new Error("No VAPID private key set.");if(typeof e!="string"||!f.validate(e))throw new Error('VAPID private key must be base64url without "=" padding.');if(f.toBuffer(e).length!==32)throw new Error("VAPID private key must decode to 32 bytes.")},expiration(e){if(!Number.isInteger(e))throw new Error("VAPID expiration must be an integer.");if(e<0)throw new Error("VAPID expiration must be positive.");const t=L($);if(e>=t)throw new Error("VAPID expiration exceeds 24 hours.")},audience(e){if(!e)throw new Error("No audience set for VAPID.");const t=typeof e=="string"?new URL(e):e;if(!t.origin||t.origin==="null")throw new Error(`VAPID audience must be an origin. Got: ${t.toString()}`)}};function G(){const e=m.createECDH("prime256v1");e.generateKeys();let t=e.getPublicKey(),r=e.getPrivateKey();return r.length<32&&(r=Buffer.concat([Buffer.alloc(32-r.length,0),r])),t.length<65&&(t=Buffer.concat([Buffer.alloc(65-t.length,0),t])),{publicKey:f.toString(t),privateKey:f.toString(r)}}function U(e){const t=f.toBuffer(e);if(t.length!==65||t[0]!==4)throw new Error("Invalid uncompressed P-256 public key.");return{x:f.toString(t.subarray(1,33)),y:f.toString(t.subarray(33,65))}}function j(e){const t=f.toBuffer(e);if(t.length!==32)throw new Error("Invalid P-256 private key scalar.");return f.toString(t)}function O(e,t=32){let r=0;if(e[r++]!==48)throw new Error("Invalid DER signature (expected SEQUENCE).");let n=e[r++];if(n&128){const l=n&127;r+=l}if(e[r++]!==2)throw new Error("Invalid DER signature (expected INTEGER r).");const c=e[r++];let o=e.subarray(r,r+c);if(r+=c,e[r++]!==2)throw new Error("Invalid DER signature (expected INTEGER s).");const i=e[r++];let s=e.subarray(r,r+i);if(o[0]===0&&(o=o.subarray(1)),s[0]===0&&(s=s.subarray(1)),o.length>t||s.length>t)throw new Error("Invalid DER signature size.");const u=Buffer.concat([Buffer.alloc(t-o.length,0),o]),d=Buffer.concat([Buffer.alloc(t-s.length,0),s]);return Buffer.concat([u,d])}function V(e,t,r){const n={typ:"JWT",alg:"ES256"},c=f.toJSON(n),o=f.toJSON(e),i=`${c}.${o}`,{x:s,y:u}=U(r),d=j(t),l=m.createPrivateKey({key:{kty:"EC",crv:"P-256",x:s,y:u,d},format:"jwk"}),y=m.sign("sha256",Buffer.from(i,"utf8"),{key:l,dsaEncoding:"der"}),h=O(y,32),g=f.toString(h);return`${i}.${g}`}function _(e){E.audience(e.audience),E.subject(e.subject),E.publicKey(e.publicKey),E.privateKey(e.privateKey);const t=(typeof e.audience=="string"?new URL(e.audience):e.audience).origin,r=(typeof e.subject=="string"?new URL(e.subject):e.subject).toString(),n=e.expiration??L(N);E.expiration(n);const c=V({aud:t,exp:n,sub:r},e.privateKey,e.publicKey),o=new Headers;if(e.contentEncoding===w.AES_128_GCM)o.set("Authorization",`vapid t=${c}, k=${e.publicKey}`);else if(e.contentEncoding===w.AES_GCM)o.set("Authorization",`WebPush ${c}`),o.set("Crypto-Key",`p256ecdsa=${e.publicKey}`);else throw new Error(`Unsupported content encoding: ${e.contentEncoding}`);return o}const F=Object.freeze(Object.defineProperty({__proto__:null,GenerateHeaders:_,GenerateKeys:G,Validate:E},Symbol.toStringTag,{value:"Module"}));function W(e){const t=Buffer.alloc(2);return t.writeUInt16BE(e,0),t}function H(e){const t=Buffer.alloc(4);return t.writeUInt32BE(e>>>0,0),t}function J(e,t){if(e.length!==t.length)throw new Error("xorBytes: length mismatch");const r=Buffer.alloc(e.length);for(let n=0;n<e.length;n++)r[n]=e[n]^t[n];return r}function z(e){if(!Number.isSafeInteger(e)||e<0)throw new Error("SEQ must be a non-negative safe integer");const t=Buffer.alloc(12,0),r=Math.floor(e/4294967296),n=e>>>0;return t.writeUInt32BE(r>>>0,4),t.writeUInt32BE(n,8),t}function X(e){const{message:t,rs:r,allowMultipleRecords:n,finalRecordPadding:c}=e;if(!Number.isInteger(r)||r<P)throw new Error(`Invalid rs; must be an integer >= ${P}.`);const o=r-16;if(o<=1)throw new Error("Invalid rs (too small)");const i=o-1;if(!n&&t.length>i)throw new Error(`Payload too large for a single RFC8188 record: ${t.length} bytes. Max is ${i} bytes for rs=${r}.`);if(!Number.isInteger(c)||c<0)throw new Error("finalRecordPadding must be a non-negative integer");const s=[];let u=0;for(;u<t.length;){const d=t.length-u,l=d<=i,y=l?d:i,h=t.subarray(u,u+y);u+=y;const g=Buffer.from([l?2:1]);if(l)s.push(Buffer.concat([h,g,Buffer.alloc(c,0)]));else{const a=o-(h.length+1);s.push(Buffer.concat([h,g,Buffer.alloc(a,0)]))}}return t.length===0&&s.push(Buffer.from([2])),s}function q(e){const{cek:t,baseNonce:r,plaintextRecords:n}=e;if(t.length!==16)throw new Error("CEK must be 16 bytes");if(r.length!==12)throw new Error("baseNonce must be 12 bytes");const c=[];for(let o=0;o<n.length;o++){const i=J(r,z(o)),s=m.createCipheriv("aes-128-gcm",t,i),u=Buffer.concat([s.update(n[o]),s.final()]);c.push(Buffer.concat([u,s.getAuthTag()]))}return Buffer.concat(c)}function b(e,t){return m.createHmac("sha256",e).update(t).digest()}function x(e,t){if(!e||typeof e!="string")throw new Error("subscription.keys.p256dh must be a string");if(!t||typeof t!="string")throw new Error("subscription.keys.auth must be a string");const r=f.toBuffer(e);if(r.length!==65||r[0]!==4)throw new Error("subscription.keys.p256dh must decode to a 65-byte uncompressed P-256 public key");if(f.toBuffer(t).length<16)throw new Error("subscription.keys.auth must decode to at least 16 bytes")}function Q(e){x(e.p256dh,e.auth);const t=f.toBuffer(e.p256dh),r=f.toBuffer(e.auth),n=m.createECDH("prime256v1");n.generateKeys();const c=n.getPublicKey(),o=n.computeSecret(t),i=m.randomBytes(16),s=b(r,o),u=Buffer.concat([Buffer.from("WebPush: info","utf8"),Buffer.from([0]),t,c]),d=b(s,Buffer.concat([u,Buffer.from([1])])),l=b(i,d),y=Buffer.concat([Buffer.from("Content-Encoding: aes128gcm","utf8"),Buffer.from([0])]),h=Buffer.concat([Buffer.from("Content-Encoding: nonce","utf8"),Buffer.from([0])]),g=b(l,Buffer.concat([y,Buffer.from([1])])).subarray(0,16),a=b(l,Buffer.concat([h,Buffer.from([1])])).subarray(0,12),B=X({message:e.payload,rs:e.rs,allowMultipleRecords:e.allowMultipleRecords,finalRecordPadding:e.finalRecordPadding}),I=q({cek:g,baseNonce:a,plaintextRecords:B});if(c.length!==65)throw new Error("Sender public key must be 65 bytes");return Buffer.concat([i,H(e.rs),Buffer.from([c.length]),c,I])}function Y(e){x(e.p256dh,e.auth);const t=f.toBuffer(e.p256dh),r=f.toBuffer(e.auth),n=m.createECDH("prime256v1");n.generateKeys();const c=n.getPublicKey(),o=n.computeSecret(t),i=m.randomBytes(16),s=b(r,o),u=Buffer.concat([Buffer.from("WebPush: info","utf8"),Buffer.from([0]),t,c]),d=b(s,Buffer.concat([u,Buffer.from([1])])),l=b(i,d),y=Buffer.concat([Buffer.from("Content-Encoding: aesgcm","utf8"),Buffer.from([0])]),h=Buffer.concat([Buffer.from("Content-Encoding: nonce","utf8"),Buffer.from([0])]),g=b(l,Buffer.concat([y,Buffer.from([1])])).subarray(0,16),a=b(l,Buffer.concat([h,Buffer.from([1])])).subarray(0,12),B=0,I=Buffer.concat([W(B),Buffer.alloc(B,0),e.payload]),v=m.createCipheriv("aes-128-gcm",g,a),k=Buffer.concat([v.update(I),v.final()]),R=v.getAuthTag();return{saltB64Url:f.toString(i),localPublicKey:c,ciphertext:Buffer.concat([k,R])}}class M extends Error{constructor(t,r){super(t),this.response=r}}function Z(e){if(!f.validate(e))throw new Error("Topic contains invalid characters; must be URL-safe base64 chars.");if(e.length>32)throw new Error("Topic must be <= 32 characters.")}function ee(e){return e.startsWith("https://android.googleapis.com/gcm/send")}function te(e){return e.startsWith("https://fcm.googleapis.com/fcm/send")||e.includes("fcm.googleapis.com")}function re(e){return new URL(e).origin}function ne(e){if(!Object.values(w).includes(e))throw new Error(`Unsupported content encoding: ${e}`)}function oe(e){if(!Object.values(A).includes(e))throw new Error(`Unsupported urgency: ${e}`)}class ce{constructor(t){if(this.config=t,E.subject(t.vapid.subject),E.privateKey(t.vapid.privateKey),E.publicKey(t.vapid.publicKey),t.gcm?.apiKey!=null&&t.gcm.apiKey.length===0)throw new Error("The GCM/FCM API Key should be a non-empty string, null, or undefined.")}generateRequest(t,r,n){if(!t?.endpoint||typeof t.endpoint!="string"||t.endpoint.length===0)throw new Error("You must pass a subscription with a valid endpoint URL.");const c=n?.TTL??T;if(!Number.isFinite(c)||c<0)throw new Error("TTL must be a number >= 0.");const o=n?.contentEncoding??w.AES_128_GCM;ne(o);const i=n?.urgency??A.NORMAL;oe(i);const s=n?.topic;s&&Z(s);const u=n?.allowMultipleRecords??!1,d=n?.rs??D;if(!Number.isInteger(d)||d<P)throw new Error(`rs must be an integer >= ${P}.`);const l=n?.finalRecordPadding??0,y=n?.headers??{},h=n?.gcmAPIKey??this.config.gcm?.apiKey??null,g=n?.vapidDetails===void 0?this.config.vapid:n.vapidDetails,a={...y,TTL:String(c),Urgency:i};s&&(a.Topic=s);let B;if(r!=null&&r!==""){if(!t.keys?.p256dh||!t.keys?.auth)throw new Error("To send a payload, the subscription must include 'keys.p256dh' and 'keys.auth'.");const S=Buffer.isBuffer(r)?r:r instanceof Uint8Array?Buffer.from(r):Buffer.from(r,"utf8");if(a["Content-Type"]="application/octet-stream",o===w.AES_128_GCM){const p=Q({p256dh:t.keys.p256dh,auth:t.keys.auth,payload:S,rs:d,allowMultipleRecords:u,finalRecordPadding:l});B=p,a["Content-Encoding"]=w.AES_128_GCM,a["Content-Length"]=String(p.length)}else{const p=Y({p256dh:t.keys.p256dh,auth:t.keys.auth,payload:S});B=p.ciphertext,a["Content-Encoding"]=w.AES_GCM,a["Content-Length"]=String(p.ciphertext.length),a.Encryption=`salt=${p.saltB64Url}`,a["Crypto-Key"]=`dh=${f.toString(p.localPublicKey)}`}}else a["Content-Length"]="0";const v=t.endpoint,k=ee(v),R=te(v);if(k)h?a.Authorization=`key=${h}`:console.warn("GCM endpoint detected but no GCM API key provided.");else if(g){const S=re(v),p=_({audience:S,subject:g.subject,publicKey:g.publicKey,privateKey:g.privateKey,contentEncoding:o}),C=p.get("Authorization");C&&(a.Authorization=C);const K=p.get("Crypto-Key");K&&o===w.AES_GCM&&(a["Crypto-Key"]=a["Crypto-Key"]?`${a["Crypto-Key"]}; ${K}`:K)}else R&&h&&(a.Authorization=`key=${h}`);return{endpoint:v,init:{method:"POST",headers:a,body:B}}}async notify(t,r,n){const{endpoint:c,init:o}=this.generateRequest(t,r,n),i=await fetch(c,o);if(!i.ok&&n?.throwOnInvalidResponse)throw new M("Received unexpected response code",i);return i}}exports.DEFAULT_RS=D;exports.DEFAULT_TTL=T;exports.MIN_RS=P;exports.SupportedContentEncoding=w;exports.SupportedUrgency=A;exports.VAPID=F;exports.WebPush=ce;exports.WebPushError=M;
|
|
2
2
|
//# sourceMappingURL=index.cjs.js.map
|
package/dist/index.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":["../src/constants.ts","../src/utils/base64url.ts","../src/vapid.ts","../src/utils/binary.ts","../src/crypto/rfc8188.ts","../src/crypto/webpush-encryption.ts","../src/webpush.ts"],"sourcesContent":["'use strict';\n\nexport enum SupportedContentEncoding {\n AES_GCM = 'aesgcm',\n AES_128_GCM = 'aes128gcm',\n}\n\nexport enum SupportedUrgency {\n VERY_LOW = 'very-low',\n LOW = 'low',\n NORMAL = 'normal',\n HIGH = 'high',\n}\n\n/** Default TTL is 4 weeks (seconds). */\nexport const DEFAULT_TTL = 2419200;\n\n/**\n * Default record size (RFC8188 `rs`) for aes128gcm.\n * For Web Push (RFC8291), a single record is required, but `rs` still appears in the body header.\n */\nexport const DEFAULT_RS = 4096;\n\n/** Minimum valid `rs` for aes128gcm per RFC8188 (must be >= 18). */\nexport const MIN_RS = 18;\n","'use strict';\n\nexport const base64url = {\n validate(input: string): boolean {\n // URL-safe base64 without \"=\" padding\n return /^[A-Za-z0-9\\-_]+$/.test(input);\n },\n toBuffer(input: string): Buffer {\n return Buffer.from(input, 'base64url');\n },\n toString(input: Buffer | Uint8Array | string): string {\n const buf = typeof input === 'string' ? Buffer.from(input, 'utf8') : Buffer.from(input);\n return buf.toString('base64url');\n },\n toJSON(obj: any) {\n return this.toString(Buffer.from(JSON.stringify(obj), 'utf8'));\n }\n};\n","'use strict';\n\nimport crypto from 'crypto';\nimport {base64url} from './utils/base64url';\nimport {SupportedContentEncoding} from './constants';\n\nconst DEFAULT_EXPIRATION_SECONDS = 12 * 60 * 60;\nconst MAX_EXPIRATION_SECONDS = 24 * 60 * 60;\n\nfunction getFutureExpirationTimestamp(numSeconds: number): number {\n const future = new Date();\n future.setSeconds(future.getSeconds() + numSeconds);\n return Math.floor(future.getTime() / 1000);\n}\n\nexport const Validate = {\n subject(subject: string | URL): void {\n if (!subject) throw new Error('No subject set in VAPID details.');\n const u = typeof subject === 'string' ? new URL(subject) : subject;\n if (!['https:', 'mailto:'].includes(u.protocol)) {\n throw new Error(`VAPID subject must be https: or mailto:. Got: ${u.toString()}`);\n }\n if (u.hostname === 'localhost') {\n console.warn('VAPID subject points to localhost; some push services may reject this.');\n }\n },\n\n publicKey(publicKey: string): void {\n if (!publicKey) throw new Error('No VAPID public key set.');\n if (typeof publicKey !== 'string' || !base64url.validate(publicKey)) {\n throw new Error('VAPID public key must be base64url without \"=\" padding.');\n }\n const pk = base64url.toBuffer(publicKey);\n if (pk.length !== 65 || pk[0] !== 0x04) {\n throw new Error('VAPID public key must decode to 65-byte uncompressed P-256.');\n }\n },\n\n privateKey(privateKey: string): void {\n if (!privateKey) throw new Error('No VAPID private key set.');\n if (typeof privateKey !== 'string' || !base64url.validate(privateKey)) {\n throw new Error('VAPID private key must be base64url without \"=\" padding.');\n }\n const sk = base64url.toBuffer(privateKey);\n if (sk.length !== 32) throw new Error('VAPID private key must decode to 32 bytes.');\n },\n\n expiration(expiration: number): void {\n if (!Number.isInteger(expiration)) throw new Error('VAPID expiration must be an integer.');\n if (expiration < 0) throw new Error('VAPID expiration must be positive.');\n const maxExp = getFutureExpirationTimestamp(MAX_EXPIRATION_SECONDS);\n if (expiration >= maxExp) throw new Error('VAPID expiration exceeds 24 hours.');\n },\n\n audience(audience: string | URL): void {\n if (!audience) throw new Error('No audience set for VAPID.');\n const a = typeof audience === 'string' ? new URL(audience) : audience;\n if (!a.origin || a.origin === 'null') {\n throw new Error(`VAPID audience must be an origin. Got: ${a.toString()}`);\n }\n },\n};\n\nexport function GenerateKeys(): { publicKey: string; privateKey: string } {\n const ecdh = crypto.createECDH('prime256v1');\n ecdh.generateKeys();\n\n let pub = ecdh.getPublicKey();\n let priv = ecdh.getPrivateKey();\n\n if (priv.length < 32) priv = Buffer.concat([Buffer.alloc(32 - priv.length, 0x00), priv]);\n if (pub.length < 65) pub = Buffer.concat([Buffer.alloc(65 - pub.length, 0x00), pub]);\n\n return {publicKey: base64url.toString(pub), privateKey: base64url.toString(priv)};\n}\n\nfunction publicKeyToJwk(publicKeyB64Url: string): { x: string; y: string } {\n const pub = base64url.toBuffer(publicKeyB64Url);\n if (pub.length !== 65 || pub[0] !== 0x04) throw new Error('Invalid uncompressed P-256 public key.');\n return {x: base64url.toString(pub.subarray(1, 33)), y: base64url.toString(pub.subarray(33, 65))};\n}\n\nfunction privateKeyToJwk(privateKeyB64Url: string): string {\n const d = base64url.toBuffer(privateKeyB64Url);\n if (d.length !== 32) throw new Error('Invalid P-256 private key scalar.');\n return base64url.toString(d);\n}\n\nfunction derToJose(derSig: Buffer, keySizeBytes = 32): Buffer {\n let off = 0;\n if (derSig[off++] !== 0x30) throw new Error('Invalid DER signature (expected SEQUENCE).');\n\n let seqLen = derSig[off++];\n if (seqLen & 0x80) {\n const n = seqLen & 0x7f;\n off += n;\n }\n\n if (derSig[off++] !== 0x02) throw new Error('Invalid DER signature (expected INTEGER r).');\n const rLen = derSig[off++];\n let r = derSig.subarray(off, off + rLen);\n off += rLen;\n\n if (derSig[off++] !== 0x02) throw new Error('Invalid DER signature (expected INTEGER s).');\n const sLen = derSig[off++];\n let s = derSig.subarray(off, off + sLen);\n\n if (r[0] === 0x00) r = r.subarray(1);\n if (s[0] === 0x00) s = s.subarray(1);\n\n if (r.length > keySizeBytes || s.length > keySizeBytes) throw new Error('Invalid DER signature size.');\n const rPad = Buffer.concat([Buffer.alloc(keySizeBytes - r.length, 0x00), r]);\n const sPad = Buffer.concat([Buffer.alloc(keySizeBytes - s.length, 0x00), s]);\n return Buffer.concat([rPad, sPad]);\n}\n\nfunction signJwtES256(payload: Record<string, unknown>, privateKeyB64Url: string, publicKeyB64Url: string): string {\n const header = {typ: 'JWT', alg: 'ES256'};\n const encodedHeader = base64url.toJSON(header);\n const encodedPayload = base64url.toJSON(payload);\n const signingInput = `${encodedHeader}.${encodedPayload}`;\n\n const {x, y} = publicKeyToJwk(publicKeyB64Url);\n const d = privateKeyToJwk(privateKeyB64Url);\n\n const keyObject = crypto.createPrivateKey({\n key: {kty: 'EC', crv: 'P-256', x, y, d},\n format: 'jwk',\n });\n\n const derSig = crypto.sign('sha256', Buffer.from(signingInput, 'utf8'), {\n key: keyObject,\n dsaEncoding: 'der',\n });\n\n const joseSig = derToJose(derSig, 32);\n const encodedSig = base64url.toString(joseSig);\n return `${signingInput}.${encodedSig}`;\n}\n\nexport function GenerateHeaders(input: {\n audience: string | URL;\n subject: string | URL;\n publicKey: string;\n privateKey: string;\n contentEncoding: SupportedContentEncoding;\n expiration?: number;\n}): Headers {\n Validate.audience(input.audience);\n Validate.subject(input.subject);\n Validate.publicKey(input.publicKey);\n Validate.privateKey(input.privateKey);\n\n const audience = (typeof input.audience === 'string' ? new URL(input.audience) : input.audience).origin;\n const subject = (typeof input.subject === 'string' ? new URL(input.subject) : input.subject).toString();\n\n const exp = input.expiration ?? getFutureExpirationTimestamp(DEFAULT_EXPIRATION_SECONDS);\n Validate.expiration(exp);\n\n const jwt = signJwtES256({aud: audience, exp, sub: subject}, input.privateKey, input.publicKey);\n\n const headers = new Headers();\n if (input.contentEncoding === SupportedContentEncoding.AES_128_GCM) {\n headers.set('Authorization', `vapid t=${jwt}, k=${input.publicKey}`);\n } else if (input.contentEncoding === SupportedContentEncoding.AES_GCM) {\n headers.set('Authorization', `WebPush ${jwt}`);\n headers.set('Crypto-Key', `p256ecdsa=${input.publicKey}`);\n } else {\n throw new Error(`Unsupported content encoding: ${input.contentEncoding}`);\n }\n return headers;\n}\n\n","'use strict';\n\n/** Write UInt16BE. */\nexport function u16be(n: number): Buffer {\n const b = Buffer.alloc(2);\n b.writeUInt16BE(n, 0);\n return b;\n}\n\n/** Write UInt32BE. */\nexport function u32be(n: number): Buffer {\n const b = Buffer.alloc(4);\n b.writeUInt32BE(n >>> 0, 0);\n return b;\n}\n\nexport function xorBytes(a: Buffer, b: Buffer): Buffer {\n if (a.length !== b.length) throw new Error('xorBytes: length mismatch');\n const out = Buffer.alloc(a.length);\n for (let i = 0; i < a.length; i++) out[i] = a[i] ^ b[i];\n return out;\n}\n\n/**\n * Convert a record sequence number into a 96-bit (12-octet) big-endian buffer.\n * RFC8188 defines SEQ as a 96-bit unsigned integer starting at 0.\n */\nexport function seq96be(seq: number): Buffer {\n if (!Number.isSafeInteger(seq) || seq < 0) throw new Error('SEQ must be a non-negative safe integer');\n const out = Buffer.alloc(12, 0x00);\n\n // Write into low 64 bits; high 32 bits remain zero.\n const hi = Math.floor(seq / 0x100000000);\n const lo = seq >>> 0;\n out.writeUInt32BE(hi >>> 0, 4);\n out.writeUInt32BE(lo, 8);\n return out;\n}\n","'use strict';\n\nimport crypto from 'crypto';\nimport { MIN_RS } from '../constants';\nimport { xorBytes, seq96be } from '../utils/binary';\n\n/**\n * Build RFC8188 plaintext records from message bytes.\n *\n * Record plaintext length for full records is `rs - 16` because AES-GCM adds a 16-byte tag.\n * The padding delimiter must be present in every record:\n * - 0x01 for all non-last records\n * - 0x02 for the last record\n */\nexport function buildPlaintextRecords(params: {\n message: Buffer;\n rs: number;\n allowMultipleRecords: boolean;\n finalRecordPadding: number;\n}): Buffer[] {\n const { message, rs, allowMultipleRecords, finalRecordPadding } = params;\n\n if (!Number.isInteger(rs) || rs < MIN_RS) {\n throw new Error(`Invalid rs; must be an integer >= ${MIN_RS}.`);\n }\n\n const maxPlain = rs - 16; // AES-GCM tag length is 16\n if (maxPlain <= 1) throw new Error('Invalid rs (too small)');\n\n const maxDataPerFullRecord = maxPlain - 1; // reserve delimiter\n\n if (!allowMultipleRecords && message.length > maxDataPerFullRecord) {\n throw new Error(\n `Payload too large for a single RFC8188 record: ${message.length} bytes. ` +\n `Max is ${maxDataPerFullRecord} bytes for rs=${rs}.`\n );\n }\n\n if (!Number.isInteger(finalRecordPadding) || finalRecordPadding < 0) {\n throw new Error('finalRecordPadding must be a non-negative integer');\n }\n\n const records: Buffer[] = [];\n let offset = 0;\n\n while (offset < message.length) {\n const remaining = message.length - offset;\n const isLast = remaining <= maxDataPerFullRecord;\n\n const dataLen = isLast ? remaining : maxDataPerFullRecord;\n const data = message.subarray(offset, offset + dataLen);\n offset += dataLen;\n\n const delimiter = Buffer.from([isLast ? 0x02 : 0x01]);\n\n if (!isLast) {\n // Full-sized non-last record.\n const paddingLen = maxPlain - (data.length + 1);\n records.push(Buffer.concat([data, delimiter, Buffer.alloc(paddingLen, 0x00)]));\n } else {\n // Last record can be partial and may include padding after delimiter.\n records.push(Buffer.concat([data, delimiter, Buffer.alloc(finalRecordPadding, 0x00)]));\n }\n }\n\n // Empty message -> final record with only delimiter (0x02)\n if (message.length === 0) records.push(Buffer.from([0x02]));\n\n return records;\n}\n\n/**\n * Encrypt RFC8188 plaintext records using AES-128-GCM.\n *\n * Nonce per record: nonce = baseNonce XOR SEQ (96-bit big-endian SEQ starting at 0).\n * AAD is empty.\n */\nexport function encryptRecords(params: {\n cek: Buffer; // 16 bytes\n baseNonce: Buffer; // 12 bytes\n plaintextRecords: Buffer[];\n}): Buffer {\n const { cek, baseNonce, plaintextRecords } = params;\n if (cek.length !== 16) throw new Error('CEK must be 16 bytes');\n if (baseNonce.length !== 12) throw new Error('baseNonce must be 12 bytes');\n\n const out: Buffer[] = [];\n for (let i = 0; i < plaintextRecords.length; i++) {\n const nonce = xorBytes(baseNonce, seq96be(i));\n const cipher = crypto.createCipheriv('aes-128-gcm', cek, nonce);\n\n const enc = Buffer.concat([cipher.update(plaintextRecords[i]), cipher.final()]);\n out.push(Buffer.concat([enc, cipher.getAuthTag()]));\n }\n return Buffer.concat(out);\n}\n","'use strict';\n\nimport crypto from 'crypto';\nimport { base64url } from '../utils/base64url';\nimport { u16be, u32be } from '../utils/binary';\nimport { buildPlaintextRecords, encryptRecords } from './rfc8188';\n\nfunction hmacSha256(key: Buffer, data: Buffer): Buffer {\n return crypto.createHmac('sha256', key).update(data).digest();\n}\n\nfunction assertSubscriptionKeys(p256dh: string, auth: string): void {\n if (!p256dh || typeof p256dh !== 'string') throw new Error('subscription.keys.p256dh must be a string');\n if (!auth || typeof auth !== 'string') throw new Error('subscription.keys.auth must be a string');\n\n const p256dhBytes = base64url.toBuffer(p256dh);\n if (p256dhBytes.length !== 65 || p256dhBytes[0] !== 0x04) {\n throw new Error('subscription.keys.p256dh must decode to a 65-byte uncompressed P-256 public key');\n }\n\n const authBytes = base64url.toBuffer(auth);\n if (authBytes.length < 16) throw new Error('subscription.keys.auth must decode to at least 16 bytes');\n}\n\n/**\n * Encrypt a push payload using RFC8291 + RFC8188 with aes128gcm.\n *\n * Returns the full RFC8188 body:\n * salt(16) || rs(4) || idlen(1) || keyid(=sender pubkey) || encryptedRecords\n */\nexport function encryptAes128GcmBody(params: {\n p256dh: string;\n auth: string;\n payload: Buffer;\n rs: number;\n allowMultipleRecords: boolean;\n finalRecordPadding: number;\n}): Buffer {\n assertSubscriptionKeys(params.p256dh, params.auth);\n\n const uaPublic = base64url.toBuffer(params.p256dh);\n const authSecret = base64url.toBuffer(params.auth);\n\n // Sender ephemeral key\n const ecdh = crypto.createECDH('prime256v1');\n ecdh.generateKeys();\n const asPublic = ecdh.getPublicKey(); // 65 bytes\n const ecdhSecret = ecdh.computeSecret(uaPublic);\n\n // RFC8291: random salt\n const salt = crypto.randomBytes(16);\n\n // RFC8291: PRK_key = HMAC(auth_secret, ecdh_secret)\n const prkKey = hmacSha256(authSecret, ecdhSecret);\n\n // key_info = \"WebPush: info\" || 0x00 || ua_public || as_public\n const keyInfo = Buffer.concat([Buffer.from('WebPush: info', 'utf8'), Buffer.from([0x00]), uaPublic, asPublic]);\n\n // IKM = HMAC(PRK_key, key_info || 0x01)\n const ikm = hmacSha256(prkKey, Buffer.concat([keyInfo, Buffer.from([0x01])]));\n\n // RFC8188: PRK = HMAC(salt, IKM)\n const prk = hmacSha256(salt, ikm);\n\n // RFC8188: CEK / nonce derivation\n const cekInfo = Buffer.concat([Buffer.from('Content-Encoding: aes128gcm', 'utf8'), Buffer.from([0x00])]);\n const nonceInfo = Buffer.concat([Buffer.from('Content-Encoding: nonce', 'utf8'), Buffer.from([0x00])]);\n\n const cek = hmacSha256(prk, Buffer.concat([cekInfo, Buffer.from([0x01])])).subarray(0, 16);\n const baseNonce = hmacSha256(prk, Buffer.concat([nonceInfo, Buffer.from([0x01])])).subarray(0, 12);\n\n const plaintextRecords = buildPlaintextRecords({\n message: params.payload,\n rs: params.rs,\n allowMultipleRecords: params.allowMultipleRecords,\n finalRecordPadding: params.finalRecordPadding,\n });\n\n const encryptedRecords = encryptRecords({ cek, baseNonce, plaintextRecords });\n\n // RFC8188 header: salt || rs || idlen || keyid\n if (asPublic.length !== 65) throw new Error('Sender public key must be 65 bytes');\n\n return Buffer.concat([salt, u32be(params.rs), Buffer.from([asPublic.length]), asPublic, encryptedRecords]);\n}\n\n/**\n * Legacy aesgcm encryption for interop.\n * Returns ciphertext plus values needed for headers: Encryption (salt) and Crypto-Key (dh).\n */\nexport function encryptAesGcmLegacy(params: {\n p256dh: string;\n auth: string;\n payload: Buffer;\n}): { saltB64Url: string; localPublicKey: Buffer; ciphertext: Buffer } {\n assertSubscriptionKeys(params.p256dh, params.auth);\n\n const receiverPub = base64url.toBuffer(params.p256dh);\n const authSecret = base64url.toBuffer(params.auth);\n\n const ecdh = crypto.createECDH('prime256v1');\n ecdh.generateKeys();\n const senderPub = ecdh.getPublicKey();\n const ecdhSecret = ecdh.computeSecret(receiverPub);\n\n const salt = crypto.randomBytes(16);\n\n const prkKey = hmacSha256(authSecret, ecdhSecret);\n const keyInfo = Buffer.concat([Buffer.from('WebPush: info', 'utf8'), Buffer.from([0x00]), receiverPub, senderPub]);\n const ikm = hmacSha256(prkKey, Buffer.concat([keyInfo, Buffer.from([0x01])]));\n const prk = hmacSha256(salt, ikm);\n\n const cekInfo = Buffer.concat([Buffer.from('Content-Encoding: aesgcm', 'utf8'), Buffer.from([0x00])]);\n const nonceInfo = Buffer.concat([Buffer.from('Content-Encoding: nonce', 'utf8'), Buffer.from([0x00])]);\n\n const cek = hmacSha256(prk, Buffer.concat([cekInfo, Buffer.from([0x01])])).subarray(0, 16);\n const nonce = hmacSha256(prk, Buffer.concat([nonceInfo, Buffer.from([0x01])])).subarray(0, 12);\n\n const padLen = 0;\n const plaintext = Buffer.concat([u16be(padLen), Buffer.alloc(padLen, 0x00), params.payload]);\n\n const cipher = crypto.createCipheriv('aes-128-gcm', cek, nonce);\n const enc = Buffer.concat([cipher.update(plaintext), cipher.final()]);\n const tag = cipher.getAuthTag();\n\n return { saltB64Url: base64url.toString(salt), localPublicKey: senderPub, ciphertext: Buffer.concat([enc, tag]) };\n}\n","'use strict';\n\nimport {\n DEFAULT_RS,\n DEFAULT_TTL,\n MIN_RS,\n SupportedContentEncoding,\n SupportedUrgency,\n} from './constants';\nimport type {\n GenerateRequestOptions,\n PushSubscription,\n WebPushConfig,\n WebPushRequestDetails,\n} from './types';\nimport {base64url} from './utils/base64url';\nimport {encryptAes128GcmBody, encryptAesGcmLegacy} from './crypto/webpush-encryption';\nimport {GenerateHeaders, Validate} from \"./vapid\";\n\nexport class WebPushError extends Error {\n constructor(message: string, public readonly response: Response) {\n super(message);\n }\n}\n\nfunction validateTopic(topic: string): void {\n if (!base64url.validate(topic)) throw new Error('Topic contains invalid characters; must be URL-safe base64 chars.');\n if (topic.length > 32) throw new Error('Topic must be <= 32 characters.');\n}\n\nfunction isGcmEndpoint(endpoint: string): boolean {\n return endpoint.startsWith('https://android.googleapis.com/gcm/send');\n}\n\nfunction isFcmEndpoint(endpoint: string): boolean {\n return endpoint.startsWith('https://fcm.googleapis.com/fcm/send') || endpoint.includes('fcm.googleapis.com');\n}\n\nfunction getAudienceFromEndpoint(endpoint: string): string {\n return new URL(endpoint).origin;\n}\n\nfunction assertSupportedEncoding(e: SupportedContentEncoding): void {\n if (!Object.values(SupportedContentEncoding).includes(e)) throw new Error(`Unsupported content encoding: ${e}`);\n}\n\nfunction assertSupportedUrgency(u: SupportedUrgency): void {\n if (!Object.values(SupportedUrgency).includes(u)) throw new Error(`Unsupported urgency: ${u}`);\n}\n\nexport class WebPush {\n constructor(public readonly config: WebPushConfig) {\n Validate.subject(config.vapid.subject);\n Validate.privateKey(config.vapid.privateKey);\n Validate.publicKey(config.vapid.publicKey);\n\n if (config.gcm?.apiKey != null && config.gcm.apiKey.length === 0) {\n throw new Error('The GCM/FCM API Key should be a non-empty string, null, or undefined.');\n }\n }\n\n /**\n * Generate the request (endpoint + fetch init) to send a push message.\n *\n * - For `aes128gcm`, this produces an RFC8188 payload body (header block + encrypted records).\n * - For Web Push, defaults to a single record per RFC8291; multi-record requires `allowMultipleRecords: true`.\n */\n generateRequest(\n subscription: PushSubscription,\n payload?: string | Buffer | Uint8Array | null,\n options?: GenerateRequestOptions\n ): WebPushRequestDetails {\n if (!subscription?.endpoint || typeof subscription.endpoint !== 'string' || subscription.endpoint.length === 0) {\n throw new Error('You must pass a subscription with a valid endpoint URL.');\n }\n\n const timeToLive = options?.TTL ?? DEFAULT_TTL;\n if (!Number.isFinite(timeToLive) || timeToLive < 0) throw new Error('TTL must be a number >= 0.');\n\n const contentEncoding = options?.contentEncoding ?? SupportedContentEncoding.AES_128_GCM;\n assertSupportedEncoding(contentEncoding);\n\n const urgency = options?.urgency ?? SupportedUrgency.NORMAL;\n assertSupportedUrgency(urgency);\n\n const topic = options?.topic;\n if (topic) validateTopic(topic);\n\n const allowMultipleRecords = options?.allowMultipleRecords ?? false;\n const rs = options?.rs ?? DEFAULT_RS;\n if (!Number.isInteger(rs) || rs < MIN_RS) throw new Error(`rs must be an integer >= ${MIN_RS}.`);\n\n const finalRecordPadding = options?.finalRecordPadding ?? 0;\n\n const extraHeaders = options?.headers ?? {};\n const currentGcmKey = options?.gcmAPIKey ?? this.config.gcm?.apiKey ?? null;\n\n const vapidDetails = options?.vapidDetails === undefined ? this.config.vapid : options.vapidDetails;\n\n const headers: Record<string, string> = {\n TTL: String(timeToLive),\n Urgency: urgency,\n ...extraHeaders,\n };\n\n if (topic) headers.Topic = topic;\n\n let body: Buffer | undefined;\n\n const hasPayload = payload != null && payload !== '';\n if (hasPayload) {\n if (!subscription.keys?.p256dh || !subscription.keys?.auth) {\n throw new Error(\"To send a payload, the subscription must include 'keys.p256dh' and 'keys.auth'.\");\n }\n\n const payloadBuf =\n Buffer.isBuffer(payload) ? payload :\n payload instanceof Uint8Array ? Buffer.from(payload) :\n Buffer.from(payload, 'utf8');\n\n headers['Content-Type'] = 'application/octet-stream';\n\n if (contentEncoding === SupportedContentEncoding.AES_128_GCM) {\n const encBody = encryptAes128GcmBody({\n p256dh: subscription.keys.p256dh,\n auth: subscription.keys.auth,\n payload: payloadBuf,\n rs,\n allowMultipleRecords,\n finalRecordPadding,\n });\n\n body = encBody;\n headers['Content-Encoding'] = SupportedContentEncoding.AES_128_GCM;\n headers['Content-Length'] = String(encBody.length);\n } else {\n const enc = encryptAesGcmLegacy({\n p256dh: subscription.keys.p256dh,\n auth: subscription.keys.auth,\n payload: payloadBuf,\n });\n\n body = enc.ciphertext;\n headers['Content-Encoding'] = SupportedContentEncoding.AES_GCM;\n headers['Content-Length'] = String(enc.ciphertext.length);\n headers['Encryption'] = `salt=${enc.saltB64Url}`;\n headers['Crypto-Key'] = `dh=${base64url.toString(enc.localPublicKey)}`;\n }\n } else {\n headers['Content-Length'] = '0';\n }\n\n // Auth: GCM/FCM key OR VAPID\n const endpoint = subscription.endpoint;\n const gcm = isGcmEndpoint(endpoint);\n const fcm = isFcmEndpoint(endpoint);\n\n if (gcm) {\n if (currentGcmKey) headers.Authorization = `key=${currentGcmKey}`;\n else console.warn('GCM endpoint detected but no GCM API key provided.');\n } else if (vapidDetails) {\n const audience = getAudienceFromEndpoint(endpoint);\n const vapidHeaders = GenerateHeaders({\n audience,\n subject: vapidDetails.subject,\n publicKey: vapidDetails.publicKey,\n privateKey: vapidDetails.privateKey,\n contentEncoding,\n });\n\n const auth = vapidHeaders.get('Authorization');\n if (auth) headers.Authorization = auth;\n\n const ck = vapidHeaders.get('Crypto-Key');\n if (ck && contentEncoding === SupportedContentEncoding.AES_GCM) {\n headers['Crypto-Key'] = headers['Crypto-Key'] ? `${headers['Crypto-Key']}; ${ck}` : ck;\n }\n } else if (fcm && currentGcmKey) {\n headers.Authorization = `key=${currentGcmKey}`;\n }\n\n const init: RequestInit = {\n method: 'POST',\n headers,\n body: body as any,\n };\n\n return {endpoint, init};\n }\n\n /**\n * Method to send notification to subscribed device\n * @param subscription\n * @param payload\n * @param options\n */\n async notify(\n subscription: PushSubscription,\n payload?: string | Buffer | Uint8Array | null,\n options?: GenerateRequestOptions & { throwOnInvalidResponse?: boolean },\n ): Promise<Response> {\n const {endpoint, init} = this.generateRequest(subscription, payload, options);\n const res = await fetch(endpoint, init);\n if (!res.ok && options?.throwOnInvalidResponse) throw new WebPushError('Received unexpected response code', res);\n return res;\n }\n}\n"],"names":["SupportedContentEncoding","SupportedUrgency","DEFAULT_TTL","DEFAULT_RS","MIN_RS","base64url","input","obj","DEFAULT_EXPIRATION_SECONDS","MAX_EXPIRATION_SECONDS","getFutureExpirationTimestamp","numSeconds","future","Validate","subject","u","publicKey","pk","privateKey","expiration","maxExp","audience","a","GenerateKeys","ecdh","crypto","pub","priv","publicKeyToJwk","publicKeyB64Url","privateKeyToJwk","privateKeyB64Url","d","derToJose","derSig","keySizeBytes","off","seqLen","n","rLen","r","sLen","rPad","sPad","signJwtES256","payload","header","encodedHeader","encodedPayload","signingInput","x","y","keyObject","joseSig","encodedSig","GenerateHeaders","exp","jwt","headers","u16be","b","u32be","xorBytes","out","i","seq96be","seq","hi","lo","buildPlaintextRecords","params","message","rs","allowMultipleRecords","finalRecordPadding","maxPlain","maxDataPerFullRecord","records","offset","remaining","isLast","dataLen","data","delimiter","paddingLen","encryptRecords","cek","baseNonce","plaintextRecords","nonce","cipher","enc","hmacSha256","key","assertSubscriptionKeys","p256dh","auth","p256dhBytes","encryptAes128GcmBody","uaPublic","authSecret","asPublic","ecdhSecret","salt","prkKey","keyInfo","ikm","prk","cekInfo","nonceInfo","encryptedRecords","encryptAesGcmLegacy","receiverPub","senderPub","padLen","plaintext","tag","WebPushError","response","validateTopic","topic","isGcmEndpoint","endpoint","isFcmEndpoint","getAudienceFromEndpoint","assertSupportedEncoding","assertSupportedUrgency","WebPush","config","subscription","options","timeToLive","contentEncoding","urgency","extraHeaders","currentGcmKey","vapidDetails","body","payloadBuf","encBody","gcm","fcm","vapidHeaders","ck","init","res"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../src/constants.ts","../src/utils/base64url.ts","../src/vapid.ts","../src/utils/binary.ts","../src/crypto/rfc8188.ts","../src/crypto/webpush-encryption.ts","../src/webpush.ts"],"sourcesContent":["'use strict';\n\nexport enum SupportedContentEncoding {\n AES_GCM = 'aesgcm',\n AES_128_GCM = 'aes128gcm',\n}\n\nexport enum SupportedUrgency {\n VERY_LOW = 'very-low',\n LOW = 'low',\n NORMAL = 'normal',\n HIGH = 'high',\n}\n\n/** Default TTL is 4 weeks (seconds). */\nexport const DEFAULT_TTL = 2419200;\n\n/**\n * Default record size (RFC8188 `rs`) for aes128gcm.\n * For Web Push (RFC8291), a single record is required, but `rs` still appears in the body header.\n */\nexport const DEFAULT_RS = 4096;\n\n/** Minimum valid `rs` for aes128gcm per RFC8188 (must be >= 18). */\nexport const MIN_RS = 18;\n","'use strict';\n\nexport const base64url = {\n validate(input: string): boolean {\n // URL-safe base64 without \"=\" padding\n return /^[A-Za-z0-9\\-_]+$/.test(input);\n },\n toBuffer(input: string): Buffer {\n return Buffer.from(input, 'base64url');\n },\n toString(input: Buffer | Uint8Array | string): string {\n const buf = typeof input === 'string' ? Buffer.from(input, 'utf8') : Buffer.from(input);\n return buf.toString('base64url');\n },\n toJSON(obj: any) {\n return this.toString(Buffer.from(JSON.stringify(obj), 'utf8'));\n }\n};\n","'use strict';\n\nimport crypto from 'crypto';\nimport {base64url} from './utils/base64url';\nimport {SupportedContentEncoding} from './constants';\n\nconst DEFAULT_EXPIRATION_SECONDS = 12 * 60 * 60;\nconst MAX_EXPIRATION_SECONDS = 24 * 60 * 60;\n\nfunction getFutureExpirationTimestamp(numSeconds: number): number {\n const future = new Date();\n future.setSeconds(future.getSeconds() + numSeconds);\n return Math.floor(future.getTime() / 1000);\n}\n\nexport const Validate = {\n subject(subject: string | URL): void {\n if (!subject) throw new Error('No subject set in VAPID details.');\n const u = typeof subject === 'string' ? new URL(subject) : subject;\n if (!['https:', 'mailto:'].includes(u.protocol)) {\n throw new Error(`VAPID subject must be https: or mailto:. Got: ${u.toString()}`);\n }\n if (u.hostname === 'localhost') {\n console.warn('VAPID subject points to localhost; some push services may reject this.');\n }\n },\n\n publicKey(publicKey: string): void {\n if (!publicKey) throw new Error('No VAPID public key set.');\n if (typeof publicKey !== 'string' || !base64url.validate(publicKey)) {\n throw new Error('VAPID public key must be base64url without \"=\" padding.');\n }\n const pk = base64url.toBuffer(publicKey);\n if (pk.length !== 65 || pk[0] !== 0x04) {\n throw new Error('VAPID public key must decode to 65-byte uncompressed P-256.');\n }\n },\n\n privateKey(privateKey: string): void {\n if (!privateKey) throw new Error('No VAPID private key set.');\n if (typeof privateKey !== 'string' || !base64url.validate(privateKey)) {\n throw new Error('VAPID private key must be base64url without \"=\" padding.');\n }\n const sk = base64url.toBuffer(privateKey);\n if (sk.length !== 32) throw new Error('VAPID private key must decode to 32 bytes.');\n },\n\n expiration(expiration: number): void {\n if (!Number.isInteger(expiration)) throw new Error('VAPID expiration must be an integer.');\n if (expiration < 0) throw new Error('VAPID expiration must be positive.');\n const maxExp = getFutureExpirationTimestamp(MAX_EXPIRATION_SECONDS);\n if (expiration >= maxExp) throw new Error('VAPID expiration exceeds 24 hours.');\n },\n\n audience(audience: string | URL): void {\n if (!audience) throw new Error('No audience set for VAPID.');\n const a = typeof audience === 'string' ? new URL(audience) : audience;\n if (!a.origin || a.origin === 'null') {\n throw new Error(`VAPID audience must be an origin. Got: ${a.toString()}`);\n }\n },\n};\n\nexport function GenerateKeys(): { publicKey: string; privateKey: string } {\n const ecdh = crypto.createECDH('prime256v1');\n ecdh.generateKeys();\n\n let pub = ecdh.getPublicKey();\n let priv = ecdh.getPrivateKey();\n\n if (priv.length < 32) priv = Buffer.concat([Buffer.alloc(32 - priv.length, 0x00), priv]);\n if (pub.length < 65) pub = Buffer.concat([Buffer.alloc(65 - pub.length, 0x00), pub]);\n\n return {publicKey: base64url.toString(pub), privateKey: base64url.toString(priv)};\n}\n\nfunction publicKeyToJwk(publicKeyB64Url: string): { x: string; y: string } {\n const pub = base64url.toBuffer(publicKeyB64Url);\n if (pub.length !== 65 || pub[0] !== 0x04) throw new Error('Invalid uncompressed P-256 public key.');\n return {x: base64url.toString(pub.subarray(1, 33)), y: base64url.toString(pub.subarray(33, 65))};\n}\n\nfunction privateKeyToJwk(privateKeyB64Url: string): string {\n const d = base64url.toBuffer(privateKeyB64Url);\n if (d.length !== 32) throw new Error('Invalid P-256 private key scalar.');\n return base64url.toString(d);\n}\n\nfunction derToJose(derSig: Buffer, keySizeBytes = 32): Buffer {\n let off = 0;\n if (derSig[off++] !== 0x30) throw new Error('Invalid DER signature (expected SEQUENCE).');\n\n let seqLen = derSig[off++];\n if (seqLen & 0x80) {\n const n = seqLen & 0x7f;\n off += n;\n }\n\n if (derSig[off++] !== 0x02) throw new Error('Invalid DER signature (expected INTEGER r).');\n const rLen = derSig[off++];\n let r = derSig.subarray(off, off + rLen);\n off += rLen;\n\n if (derSig[off++] !== 0x02) throw new Error('Invalid DER signature (expected INTEGER s).');\n const sLen = derSig[off++];\n let s = derSig.subarray(off, off + sLen);\n\n if (r[0] === 0x00) r = r.subarray(1);\n if (s[0] === 0x00) s = s.subarray(1);\n\n if (r.length > keySizeBytes || s.length > keySizeBytes) throw new Error('Invalid DER signature size.');\n const rPad = Buffer.concat([Buffer.alloc(keySizeBytes - r.length, 0x00), r]);\n const sPad = Buffer.concat([Buffer.alloc(keySizeBytes - s.length, 0x00), s]);\n return Buffer.concat([rPad, sPad]);\n}\n\nfunction signJwtES256(payload: Record<string, unknown>, privateKeyB64Url: string, publicKeyB64Url: string): string {\n const header = {typ: 'JWT', alg: 'ES256'};\n const encodedHeader = base64url.toJSON(header);\n const encodedPayload = base64url.toJSON(payload);\n const signingInput = `${encodedHeader}.${encodedPayload}`;\n\n const {x, y} = publicKeyToJwk(publicKeyB64Url);\n const d = privateKeyToJwk(privateKeyB64Url);\n\n const keyObject = crypto.createPrivateKey({\n key: {kty: 'EC', crv: 'P-256', x, y, d},\n format: 'jwk',\n });\n\n const derSig = crypto.sign('sha256', Buffer.from(signingInput, 'utf8'), {\n key: keyObject,\n dsaEncoding: 'der',\n });\n\n const joseSig = derToJose(derSig, 32);\n const encodedSig = base64url.toString(joseSig);\n return `${signingInput}.${encodedSig}`;\n}\n\nexport function GenerateHeaders(input: {\n audience: string | URL;\n subject: string | URL;\n publicKey: string;\n privateKey: string;\n contentEncoding: SupportedContentEncoding;\n expiration?: number;\n}): Headers {\n Validate.audience(input.audience);\n Validate.subject(input.subject);\n Validate.publicKey(input.publicKey);\n Validate.privateKey(input.privateKey);\n\n const audience = (typeof input.audience === 'string' ? new URL(input.audience) : input.audience).origin;\n const subject = (typeof input.subject === 'string' ? new URL(input.subject) : input.subject).toString();\n\n const exp = input.expiration ?? getFutureExpirationTimestamp(DEFAULT_EXPIRATION_SECONDS);\n Validate.expiration(exp);\n\n const jwt = signJwtES256({aud: audience, exp, sub: subject}, input.privateKey, input.publicKey);\n\n const headers = new Headers();\n if (input.contentEncoding === SupportedContentEncoding.AES_128_GCM) {\n headers.set('Authorization', `vapid t=${jwt}, k=${input.publicKey}`);\n } else if (input.contentEncoding === SupportedContentEncoding.AES_GCM) {\n headers.set('Authorization', `WebPush ${jwt}`);\n headers.set('Crypto-Key', `p256ecdsa=${input.publicKey}`);\n } else {\n throw new Error(`Unsupported content encoding: ${input.contentEncoding}`);\n }\n return headers;\n}\n\n","'use strict';\n\n/** Write UInt16BE. */\nexport function u16be(n: number): Buffer {\n const b = Buffer.alloc(2);\n b.writeUInt16BE(n, 0);\n return b;\n}\n\n/** Write UInt32BE. */\nexport function u32be(n: number): Buffer {\n const b = Buffer.alloc(4);\n b.writeUInt32BE(n >>> 0, 0);\n return b;\n}\n\nexport function xorBytes(a: Buffer, b: Buffer): Buffer {\n if (a.length !== b.length) throw new Error('xorBytes: length mismatch');\n const out = Buffer.alloc(a.length);\n for (let i = 0; i < a.length; i++) out[i] = a[i] ^ b[i];\n return out;\n}\n\n/**\n * Convert a record sequence number into a 96-bit (12-octet) big-endian buffer.\n * RFC8188 defines SEQ as a 96-bit unsigned integer starting at 0.\n */\nexport function seq96be(seq: number): Buffer {\n if (!Number.isSafeInteger(seq) || seq < 0) throw new Error('SEQ must be a non-negative safe integer');\n const out = Buffer.alloc(12, 0x00);\n\n // Write into low 64 bits; high 32 bits remain zero.\n const hi = Math.floor(seq / 0x100000000);\n const lo = seq >>> 0;\n out.writeUInt32BE(hi >>> 0, 4);\n out.writeUInt32BE(lo, 8);\n return out;\n}\n","'use strict';\n\nimport crypto from 'crypto';\nimport { MIN_RS } from '../constants';\nimport { xorBytes, seq96be } from '../utils/binary';\n\n/**\n * Build RFC8188 plaintext records from message bytes.\n *\n * Record plaintext length for full records is `rs - 16` because AES-GCM adds a 16-byte tag.\n * The padding delimiter must be present in every record:\n * - 0x01 for all non-last records\n * - 0x02 for the last record\n */\nexport function buildPlaintextRecords(params: {\n message: Buffer;\n rs: number;\n allowMultipleRecords: boolean;\n finalRecordPadding: number;\n}): Buffer[] {\n const { message, rs, allowMultipleRecords, finalRecordPadding } = params;\n\n if (!Number.isInteger(rs) || rs < MIN_RS) {\n throw new Error(`Invalid rs; must be an integer >= ${MIN_RS}.`);\n }\n\n const maxPlain = rs - 16; // AES-GCM tag length is 16\n if (maxPlain <= 1) throw new Error('Invalid rs (too small)');\n\n const maxDataPerFullRecord = maxPlain - 1; // reserve delimiter\n\n if (!allowMultipleRecords && message.length > maxDataPerFullRecord) {\n throw new Error(\n `Payload too large for a single RFC8188 record: ${message.length} bytes. ` +\n `Max is ${maxDataPerFullRecord} bytes for rs=${rs}.`\n );\n }\n\n if (!Number.isInteger(finalRecordPadding) || finalRecordPadding < 0) {\n throw new Error('finalRecordPadding must be a non-negative integer');\n }\n\n const records: Buffer[] = [];\n let offset = 0;\n\n while (offset < message.length) {\n const remaining = message.length - offset;\n const isLast = remaining <= maxDataPerFullRecord;\n\n const dataLen = isLast ? remaining : maxDataPerFullRecord;\n const data = message.subarray(offset, offset + dataLen);\n offset += dataLen;\n\n const delimiter = Buffer.from([isLast ? 0x02 : 0x01]);\n\n if (!isLast) {\n // Full-sized non-last record.\n const paddingLen = maxPlain - (data.length + 1);\n records.push(Buffer.concat([data, delimiter, Buffer.alloc(paddingLen, 0x00)]));\n } else {\n // Last record can be partial and may include padding after delimiter.\n records.push(Buffer.concat([data, delimiter, Buffer.alloc(finalRecordPadding, 0x00)]));\n }\n }\n\n // Empty message -> final record with only delimiter (0x02)\n if (message.length === 0) records.push(Buffer.from([0x02]));\n\n return records;\n}\n\n/**\n * Encrypt RFC8188 plaintext records using AES-128-GCM.\n *\n * Nonce per record: nonce = baseNonce XOR SEQ (96-bit big-endian SEQ starting at 0).\n * AAD is empty.\n */\nexport function encryptRecords(params: {\n cek: Buffer; // 16 bytes\n baseNonce: Buffer; // 12 bytes\n plaintextRecords: Buffer[];\n}): Buffer {\n const { cek, baseNonce, plaintextRecords } = params;\n if (cek.length !== 16) throw new Error('CEK must be 16 bytes');\n if (baseNonce.length !== 12) throw new Error('baseNonce must be 12 bytes');\n\n const out: Buffer[] = [];\n for (let i = 0; i < plaintextRecords.length; i++) {\n const nonce = xorBytes(baseNonce, seq96be(i));\n const cipher = crypto.createCipheriv('aes-128-gcm', cek, nonce);\n\n const enc = Buffer.concat([cipher.update(plaintextRecords[i]), cipher.final()]);\n out.push(Buffer.concat([enc, cipher.getAuthTag()]));\n }\n return Buffer.concat(out);\n}\n","'use strict';\n\nimport crypto from 'crypto';\nimport { base64url } from '../utils/base64url';\nimport { u16be, u32be } from '../utils/binary';\nimport { buildPlaintextRecords, encryptRecords } from './rfc8188';\n\nfunction hmacSha256(key: Buffer, data: Buffer): Buffer {\n return crypto.createHmac('sha256', key).update(data).digest();\n}\n\nfunction assertSubscriptionKeys(p256dh: string, auth: string): void {\n if (!p256dh || typeof p256dh !== 'string') throw new Error('subscription.keys.p256dh must be a string');\n if (!auth || typeof auth !== 'string') throw new Error('subscription.keys.auth must be a string');\n\n const p256dhBytes = base64url.toBuffer(p256dh);\n if (p256dhBytes.length !== 65 || p256dhBytes[0] !== 0x04) {\n throw new Error('subscription.keys.p256dh must decode to a 65-byte uncompressed P-256 public key');\n }\n\n const authBytes = base64url.toBuffer(auth);\n if (authBytes.length < 16) throw new Error('subscription.keys.auth must decode to at least 16 bytes');\n}\n\n/**\n * Encrypt a push payload using RFC8291 + RFC8188 with aes128gcm.\n *\n * Returns the full RFC8188 body:\n * salt(16) || rs(4) || idlen(1) || keyid(=sender pubkey) || encryptedRecords\n */\nexport function encryptAes128GcmBody(params: {\n p256dh: string;\n auth: string;\n payload: Buffer;\n rs: number;\n allowMultipleRecords: boolean;\n finalRecordPadding: number;\n}): Buffer {\n assertSubscriptionKeys(params.p256dh, params.auth);\n\n const uaPublic = base64url.toBuffer(params.p256dh);\n const authSecret = base64url.toBuffer(params.auth);\n\n // Sender ephemeral key\n const ecdh = crypto.createECDH('prime256v1');\n ecdh.generateKeys();\n const asPublic = ecdh.getPublicKey(); // 65 bytes\n const ecdhSecret = ecdh.computeSecret(uaPublic);\n\n // RFC8291: random salt\n const salt = crypto.randomBytes(16);\n\n // RFC8291: PRK_key = HMAC(auth_secret, ecdh_secret)\n const prkKey = hmacSha256(authSecret, ecdhSecret);\n\n // key_info = \"WebPush: info\" || 0x00 || ua_public || as_public\n const keyInfo = Buffer.concat([Buffer.from('WebPush: info', 'utf8'), Buffer.from([0x00]), uaPublic, asPublic]);\n\n // IKM = HMAC(PRK_key, key_info || 0x01)\n const ikm = hmacSha256(prkKey, Buffer.concat([keyInfo, Buffer.from([0x01])]));\n\n // RFC8188: PRK = HMAC(salt, IKM)\n const prk = hmacSha256(salt, ikm);\n\n // RFC8188: CEK / nonce derivation\n const cekInfo = Buffer.concat([Buffer.from('Content-Encoding: aes128gcm', 'utf8'), Buffer.from([0x00])]);\n const nonceInfo = Buffer.concat([Buffer.from('Content-Encoding: nonce', 'utf8'), Buffer.from([0x00])]);\n\n const cek = hmacSha256(prk, Buffer.concat([cekInfo, Buffer.from([0x01])])).subarray(0, 16);\n const baseNonce = hmacSha256(prk, Buffer.concat([nonceInfo, Buffer.from([0x01])])).subarray(0, 12);\n\n const plaintextRecords = buildPlaintextRecords({\n message: params.payload,\n rs: params.rs,\n allowMultipleRecords: params.allowMultipleRecords,\n finalRecordPadding: params.finalRecordPadding,\n });\n\n const encryptedRecords = encryptRecords({ cek, baseNonce, plaintextRecords });\n\n // RFC8188 header: salt || rs || idlen || keyid\n if (asPublic.length !== 65) throw new Error('Sender public key must be 65 bytes');\n\n return Buffer.concat([salt, u32be(params.rs), Buffer.from([asPublic.length]), asPublic, encryptedRecords]);\n}\n\n/**\n * Legacy aesgcm encryption for interop.\n * Returns ciphertext plus values needed for headers: Encryption (salt) and Crypto-Key (dh).\n */\nexport function encryptAesGcmLegacy(params: {\n p256dh: string;\n auth: string;\n payload: Buffer;\n}): { saltB64Url: string; localPublicKey: Buffer; ciphertext: Buffer } {\n assertSubscriptionKeys(params.p256dh, params.auth);\n\n const receiverPub = base64url.toBuffer(params.p256dh);\n const authSecret = base64url.toBuffer(params.auth);\n\n const ecdh = crypto.createECDH('prime256v1');\n ecdh.generateKeys();\n const senderPub = ecdh.getPublicKey();\n const ecdhSecret = ecdh.computeSecret(receiverPub);\n\n const salt = crypto.randomBytes(16);\n\n const prkKey = hmacSha256(authSecret, ecdhSecret);\n const keyInfo = Buffer.concat([Buffer.from('WebPush: info', 'utf8'), Buffer.from([0x00]), receiverPub, senderPub]);\n const ikm = hmacSha256(prkKey, Buffer.concat([keyInfo, Buffer.from([0x01])]));\n const prk = hmacSha256(salt, ikm);\n\n const cekInfo = Buffer.concat([Buffer.from('Content-Encoding: aesgcm', 'utf8'), Buffer.from([0x00])]);\n const nonceInfo = Buffer.concat([Buffer.from('Content-Encoding: nonce', 'utf8'), Buffer.from([0x00])]);\n\n const cek = hmacSha256(prk, Buffer.concat([cekInfo, Buffer.from([0x01])])).subarray(0, 16);\n const nonce = hmacSha256(prk, Buffer.concat([nonceInfo, Buffer.from([0x01])])).subarray(0, 12);\n\n const padLen = 0;\n const plaintext = Buffer.concat([u16be(padLen), Buffer.alloc(padLen, 0x00), params.payload]);\n\n const cipher = crypto.createCipheriv('aes-128-gcm', cek, nonce);\n const enc = Buffer.concat([cipher.update(plaintext), cipher.final()]);\n const tag = cipher.getAuthTag();\n\n return { saltB64Url: base64url.toString(salt), localPublicKey: senderPub, ciphertext: Buffer.concat([enc, tag]) };\n}\n","'use strict';\n\nimport {\n DEFAULT_RS,\n DEFAULT_TTL,\n MIN_RS,\n SupportedContentEncoding,\n SupportedUrgency,\n} from './constants';\nimport type {\n GenerateRequestOptions,\n PushSubscription,\n WebPushConfig,\n WebPushRequestDetails,\n} from './types';\nimport {base64url} from './utils/base64url';\nimport {encryptAes128GcmBody, encryptAesGcmLegacy} from './crypto/webpush-encryption';\nimport {GenerateHeaders, Validate} from \"./vapid\";\n\nexport class WebPushError extends Error {\n constructor(message: string, public readonly response: Response) {\n super(message);\n }\n}\n\nfunction validateTopic(topic: string): void {\n if (!base64url.validate(topic)) throw new Error('Topic contains invalid characters; must be URL-safe base64 chars.');\n if (topic.length > 32) throw new Error('Topic must be <= 32 characters.');\n}\n\nfunction isGcmEndpoint(endpoint: string): boolean {\n return endpoint.startsWith('https://android.googleapis.com/gcm/send');\n}\n\nfunction isFcmEndpoint(endpoint: string): boolean {\n return endpoint.startsWith('https://fcm.googleapis.com/fcm/send') || endpoint.includes('fcm.googleapis.com');\n}\n\nfunction getAudienceFromEndpoint(endpoint: string): string {\n return new URL(endpoint).origin;\n}\n\nfunction assertSupportedEncoding(e: SupportedContentEncoding): void {\n if (!Object.values(SupportedContentEncoding).includes(e)) throw new Error(`Unsupported content encoding: ${e}`);\n}\n\nfunction assertSupportedUrgency(u: SupportedUrgency): void {\n if (!Object.values(SupportedUrgency).includes(u)) throw new Error(`Unsupported urgency: ${u}`);\n}\n\nexport class WebPush {\n constructor(public readonly config: WebPushConfig) {\n Validate.subject(config.vapid.subject);\n Validate.privateKey(config.vapid.privateKey);\n Validate.publicKey(config.vapid.publicKey);\n\n if (config.gcm?.apiKey != null && config.gcm.apiKey.length === 0) {\n throw new Error('The GCM/FCM API Key should be a non-empty string, null, or undefined.');\n }\n }\n\n /**\n * Generate the request (endpoint + fetch init) to send a push message.\n *\n * - For `aes128gcm`, this produces an RFC8188 payload body (header block + encrypted records).\n * - For Web Push, defaults to a single record per RFC8291; multi-record requires `allowMultipleRecords: true`.\n */\n generateRequest(\n subscription: PushSubscription,\n payload?: string | Buffer | Uint8Array | null,\n options?: GenerateRequestOptions\n ): WebPushRequestDetails {\n if (!subscription?.endpoint || typeof subscription.endpoint !== 'string' || subscription.endpoint.length === 0) {\n throw new Error('You must pass a subscription with a valid endpoint URL.');\n }\n\n const timeToLive = options?.TTL ?? DEFAULT_TTL;\n if (!Number.isFinite(timeToLive) || timeToLive < 0) throw new Error('TTL must be a number >= 0.');\n\n const contentEncoding = options?.contentEncoding ?? SupportedContentEncoding.AES_128_GCM;\n assertSupportedEncoding(contentEncoding);\n\n const urgency = options?.urgency ?? SupportedUrgency.NORMAL;\n assertSupportedUrgency(urgency);\n\n const topic = options?.topic;\n if (topic) validateTopic(topic);\n\n const allowMultipleRecords = options?.allowMultipleRecords ?? false;\n const rs = options?.rs ?? DEFAULT_RS;\n if (!Number.isInteger(rs) || rs < MIN_RS) throw new Error(`rs must be an integer >= ${MIN_RS}.`);\n\n const finalRecordPadding = options?.finalRecordPadding ?? 0;\n\n const extraHeaders = options?.headers ?? {};\n const currentGcmKey = options?.gcmAPIKey ?? this.config.gcm?.apiKey ?? null;\n\n const vapidDetails = options?.vapidDetails === undefined ? this.config.vapid : options.vapidDetails;\n\n const headers: Record<string, string> = {\n ...extraHeaders,\n // Keep protocol-critical defaults authoritative even when custom headers are supplied.\n TTL: String(timeToLive),\n Urgency: urgency,\n };\n\n if (topic) headers.Topic = topic;\n\n let body: Buffer | undefined;\n\n const hasPayload = payload != null && payload !== '';\n if (hasPayload) {\n if (!subscription.keys?.p256dh || !subscription.keys?.auth) {\n throw new Error(\"To send a payload, the subscription must include 'keys.p256dh' and 'keys.auth'.\");\n }\n\n const payloadBuf =\n Buffer.isBuffer(payload) ? payload :\n payload instanceof Uint8Array ? Buffer.from(payload) :\n Buffer.from(payload, 'utf8');\n\n headers['Content-Type'] = 'application/octet-stream';\n\n if (contentEncoding === SupportedContentEncoding.AES_128_GCM) {\n const encBody = encryptAes128GcmBody({\n p256dh: subscription.keys.p256dh,\n auth: subscription.keys.auth,\n payload: payloadBuf,\n rs,\n allowMultipleRecords,\n finalRecordPadding,\n });\n\n body = encBody;\n headers['Content-Encoding'] = SupportedContentEncoding.AES_128_GCM;\n headers['Content-Length'] = String(encBody.length);\n } else {\n const enc = encryptAesGcmLegacy({\n p256dh: subscription.keys.p256dh,\n auth: subscription.keys.auth,\n payload: payloadBuf,\n });\n\n body = enc.ciphertext;\n headers['Content-Encoding'] = SupportedContentEncoding.AES_GCM;\n headers['Content-Length'] = String(enc.ciphertext.length);\n headers['Encryption'] = `salt=${enc.saltB64Url}`;\n headers['Crypto-Key'] = `dh=${base64url.toString(enc.localPublicKey)}`;\n }\n } else {\n headers['Content-Length'] = '0';\n }\n\n // Auth: GCM/FCM key OR VAPID\n const endpoint = subscription.endpoint;\n const gcm = isGcmEndpoint(endpoint);\n const fcm = isFcmEndpoint(endpoint);\n\n if (gcm) {\n if (currentGcmKey) headers.Authorization = `key=${currentGcmKey}`;\n else console.warn('GCM endpoint detected but no GCM API key provided.');\n } else if (vapidDetails) {\n const audience = getAudienceFromEndpoint(endpoint);\n const vapidHeaders = GenerateHeaders({\n audience,\n subject: vapidDetails.subject,\n publicKey: vapidDetails.publicKey,\n privateKey: vapidDetails.privateKey,\n contentEncoding,\n });\n\n const auth = vapidHeaders.get('Authorization');\n if (auth) headers.Authorization = auth;\n\n const ck = vapidHeaders.get('Crypto-Key');\n if (ck && contentEncoding === SupportedContentEncoding.AES_GCM) {\n headers['Crypto-Key'] = headers['Crypto-Key'] ? `${headers['Crypto-Key']}; ${ck}` : ck;\n }\n } else if (fcm && currentGcmKey) {\n headers.Authorization = `key=${currentGcmKey}`;\n }\n\n const init: RequestInit = {\n method: 'POST',\n headers,\n body: body as any,\n };\n\n return {endpoint, init};\n }\n\n /**\n * Method to send notification to subscribed device\n * @param subscription\n * @param payload\n * @param options\n */\n async notify(\n subscription: PushSubscription,\n payload?: string | Buffer | Uint8Array | null,\n options?: GenerateRequestOptions & { throwOnInvalidResponse?: boolean },\n ): Promise<Response> {\n const {endpoint, init} = this.generateRequest(subscription, payload, options);\n const res = await fetch(endpoint, init);\n if (!res.ok && options?.throwOnInvalidResponse) throw new WebPushError('Received unexpected response code', res);\n return res;\n }\n}\n"],"names":["SupportedContentEncoding","SupportedUrgency","DEFAULT_TTL","DEFAULT_RS","MIN_RS","base64url","input","obj","DEFAULT_EXPIRATION_SECONDS","MAX_EXPIRATION_SECONDS","getFutureExpirationTimestamp","numSeconds","future","Validate","subject","u","publicKey","pk","privateKey","expiration","maxExp","audience","a","GenerateKeys","ecdh","crypto","pub","priv","publicKeyToJwk","publicKeyB64Url","privateKeyToJwk","privateKeyB64Url","d","derToJose","derSig","keySizeBytes","off","seqLen","n","rLen","r","sLen","rPad","sPad","signJwtES256","payload","header","encodedHeader","encodedPayload","signingInput","x","y","keyObject","joseSig","encodedSig","GenerateHeaders","exp","jwt","headers","u16be","b","u32be","xorBytes","out","i","seq96be","seq","hi","lo","buildPlaintextRecords","params","message","rs","allowMultipleRecords","finalRecordPadding","maxPlain","maxDataPerFullRecord","records","offset","remaining","isLast","dataLen","data","delimiter","paddingLen","encryptRecords","cek","baseNonce","plaintextRecords","nonce","cipher","enc","hmacSha256","key","assertSubscriptionKeys","p256dh","auth","p256dhBytes","encryptAes128GcmBody","uaPublic","authSecret","asPublic","ecdhSecret","salt","prkKey","keyInfo","ikm","prk","cekInfo","nonceInfo","encryptedRecords","encryptAesGcmLegacy","receiverPub","senderPub","padLen","plaintext","tag","WebPushError","response","validateTopic","topic","isGcmEndpoint","endpoint","isFcmEndpoint","getAudienceFromEndpoint","assertSupportedEncoding","assertSupportedUrgency","WebPush","config","subscription","options","timeToLive","contentEncoding","urgency","extraHeaders","currentGcmKey","vapidDetails","body","payloadBuf","encBody","gcm","fcm","vapidHeaders","ck","init","res"],"mappings":"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"}
|
package/dist/index.es.js
CHANGED
|
@@ -254,9 +254,10 @@ class ie {
|
|
|
254
254
|
const u = n?.allowMultipleRecords ?? !1, d = n?.rs ?? N;
|
|
255
255
|
if (!Number.isInteger(d) || d < k) throw new Error(`rs must be an integer >= ${k}.`);
|
|
256
256
|
const l = n?.finalRecordPadding ?? 0, y = n?.headers ?? {}, h = n?.gcmAPIKey ?? this.config.gcm?.apiKey ?? null, g = n?.vapidDetails === void 0 ? this.config.vapid : n.vapidDetails, a = {
|
|
257
|
+
...y,
|
|
258
|
+
// Keep protocol-critical defaults authoritative even when custom headers are supplied.
|
|
257
259
|
TTL: String(c),
|
|
258
|
-
Urgency: i
|
|
259
|
-
...y
|
|
260
|
+
Urgency: i
|
|
260
261
|
};
|
|
261
262
|
s && (a.Topic = s);
|
|
262
263
|
let B;
|
package/dist/index.es.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.es.js","sources":["../src/constants.ts","../src/utils/base64url.ts","../src/vapid.ts","../src/utils/binary.ts","../src/crypto/rfc8188.ts","../src/crypto/webpush-encryption.ts","../src/webpush.ts"],"sourcesContent":["'use strict';\n\nexport enum SupportedContentEncoding {\n AES_GCM = 'aesgcm',\n AES_128_GCM = 'aes128gcm',\n}\n\nexport enum SupportedUrgency {\n VERY_LOW = 'very-low',\n LOW = 'low',\n NORMAL = 'normal',\n HIGH = 'high',\n}\n\n/** Default TTL is 4 weeks (seconds). */\nexport const DEFAULT_TTL = 2419200;\n\n/**\n * Default record size (RFC8188 `rs`) for aes128gcm.\n * For Web Push (RFC8291), a single record is required, but `rs` still appears in the body header.\n */\nexport const DEFAULT_RS = 4096;\n\n/** Minimum valid `rs` for aes128gcm per RFC8188 (must be >= 18). */\nexport const MIN_RS = 18;\n","'use strict';\n\nexport const base64url = {\n validate(input: string): boolean {\n // URL-safe base64 without \"=\" padding\n return /^[A-Za-z0-9\\-_]+$/.test(input);\n },\n toBuffer(input: string): Buffer {\n return Buffer.from(input, 'base64url');\n },\n toString(input: Buffer | Uint8Array | string): string {\n const buf = typeof input === 'string' ? Buffer.from(input, 'utf8') : Buffer.from(input);\n return buf.toString('base64url');\n },\n toJSON(obj: any) {\n return this.toString(Buffer.from(JSON.stringify(obj), 'utf8'));\n }\n};\n","'use strict';\n\nimport crypto from 'crypto';\nimport {base64url} from './utils/base64url';\nimport {SupportedContentEncoding} from './constants';\n\nconst DEFAULT_EXPIRATION_SECONDS = 12 * 60 * 60;\nconst MAX_EXPIRATION_SECONDS = 24 * 60 * 60;\n\nfunction getFutureExpirationTimestamp(numSeconds: number): number {\n const future = new Date();\n future.setSeconds(future.getSeconds() + numSeconds);\n return Math.floor(future.getTime() / 1000);\n}\n\nexport const Validate = {\n subject(subject: string | URL): void {\n if (!subject) throw new Error('No subject set in VAPID details.');\n const u = typeof subject === 'string' ? new URL(subject) : subject;\n if (!['https:', 'mailto:'].includes(u.protocol)) {\n throw new Error(`VAPID subject must be https: or mailto:. Got: ${u.toString()}`);\n }\n if (u.hostname === 'localhost') {\n console.warn('VAPID subject points to localhost; some push services may reject this.');\n }\n },\n\n publicKey(publicKey: string): void {\n if (!publicKey) throw new Error('No VAPID public key set.');\n if (typeof publicKey !== 'string' || !base64url.validate(publicKey)) {\n throw new Error('VAPID public key must be base64url without \"=\" padding.');\n }\n const pk = base64url.toBuffer(publicKey);\n if (pk.length !== 65 || pk[0] !== 0x04) {\n throw new Error('VAPID public key must decode to 65-byte uncompressed P-256.');\n }\n },\n\n privateKey(privateKey: string): void {\n if (!privateKey) throw new Error('No VAPID private key set.');\n if (typeof privateKey !== 'string' || !base64url.validate(privateKey)) {\n throw new Error('VAPID private key must be base64url without \"=\" padding.');\n }\n const sk = base64url.toBuffer(privateKey);\n if (sk.length !== 32) throw new Error('VAPID private key must decode to 32 bytes.');\n },\n\n expiration(expiration: number): void {\n if (!Number.isInteger(expiration)) throw new Error('VAPID expiration must be an integer.');\n if (expiration < 0) throw new Error('VAPID expiration must be positive.');\n const maxExp = getFutureExpirationTimestamp(MAX_EXPIRATION_SECONDS);\n if (expiration >= maxExp) throw new Error('VAPID expiration exceeds 24 hours.');\n },\n\n audience(audience: string | URL): void {\n if (!audience) throw new Error('No audience set for VAPID.');\n const a = typeof audience === 'string' ? new URL(audience) : audience;\n if (!a.origin || a.origin === 'null') {\n throw new Error(`VAPID audience must be an origin. Got: ${a.toString()}`);\n }\n },\n};\n\nexport function GenerateKeys(): { publicKey: string; privateKey: string } {\n const ecdh = crypto.createECDH('prime256v1');\n ecdh.generateKeys();\n\n let pub = ecdh.getPublicKey();\n let priv = ecdh.getPrivateKey();\n\n if (priv.length < 32) priv = Buffer.concat([Buffer.alloc(32 - priv.length, 0x00), priv]);\n if (pub.length < 65) pub = Buffer.concat([Buffer.alloc(65 - pub.length, 0x00), pub]);\n\n return {publicKey: base64url.toString(pub), privateKey: base64url.toString(priv)};\n}\n\nfunction publicKeyToJwk(publicKeyB64Url: string): { x: string; y: string } {\n const pub = base64url.toBuffer(publicKeyB64Url);\n if (pub.length !== 65 || pub[0] !== 0x04) throw new Error('Invalid uncompressed P-256 public key.');\n return {x: base64url.toString(pub.subarray(1, 33)), y: base64url.toString(pub.subarray(33, 65))};\n}\n\nfunction privateKeyToJwk(privateKeyB64Url: string): string {\n const d = base64url.toBuffer(privateKeyB64Url);\n if (d.length !== 32) throw new Error('Invalid P-256 private key scalar.');\n return base64url.toString(d);\n}\n\nfunction derToJose(derSig: Buffer, keySizeBytes = 32): Buffer {\n let off = 0;\n if (derSig[off++] !== 0x30) throw new Error('Invalid DER signature (expected SEQUENCE).');\n\n let seqLen = derSig[off++];\n if (seqLen & 0x80) {\n const n = seqLen & 0x7f;\n off += n;\n }\n\n if (derSig[off++] !== 0x02) throw new Error('Invalid DER signature (expected INTEGER r).');\n const rLen = derSig[off++];\n let r = derSig.subarray(off, off + rLen);\n off += rLen;\n\n if (derSig[off++] !== 0x02) throw new Error('Invalid DER signature (expected INTEGER s).');\n const sLen = derSig[off++];\n let s = derSig.subarray(off, off + sLen);\n\n if (r[0] === 0x00) r = r.subarray(1);\n if (s[0] === 0x00) s = s.subarray(1);\n\n if (r.length > keySizeBytes || s.length > keySizeBytes) throw new Error('Invalid DER signature size.');\n const rPad = Buffer.concat([Buffer.alloc(keySizeBytes - r.length, 0x00), r]);\n const sPad = Buffer.concat([Buffer.alloc(keySizeBytes - s.length, 0x00), s]);\n return Buffer.concat([rPad, sPad]);\n}\n\nfunction signJwtES256(payload: Record<string, unknown>, privateKeyB64Url: string, publicKeyB64Url: string): string {\n const header = {typ: 'JWT', alg: 'ES256'};\n const encodedHeader = base64url.toJSON(header);\n const encodedPayload = base64url.toJSON(payload);\n const signingInput = `${encodedHeader}.${encodedPayload}`;\n\n const {x, y} = publicKeyToJwk(publicKeyB64Url);\n const d = privateKeyToJwk(privateKeyB64Url);\n\n const keyObject = crypto.createPrivateKey({\n key: {kty: 'EC', crv: 'P-256', x, y, d},\n format: 'jwk',\n });\n\n const derSig = crypto.sign('sha256', Buffer.from(signingInput, 'utf8'), {\n key: keyObject,\n dsaEncoding: 'der',\n });\n\n const joseSig = derToJose(derSig, 32);\n const encodedSig = base64url.toString(joseSig);\n return `${signingInput}.${encodedSig}`;\n}\n\nexport function GenerateHeaders(input: {\n audience: string | URL;\n subject: string | URL;\n publicKey: string;\n privateKey: string;\n contentEncoding: SupportedContentEncoding;\n expiration?: number;\n}): Headers {\n Validate.audience(input.audience);\n Validate.subject(input.subject);\n Validate.publicKey(input.publicKey);\n Validate.privateKey(input.privateKey);\n\n const audience = (typeof input.audience === 'string' ? new URL(input.audience) : input.audience).origin;\n const subject = (typeof input.subject === 'string' ? new URL(input.subject) : input.subject).toString();\n\n const exp = input.expiration ?? getFutureExpirationTimestamp(DEFAULT_EXPIRATION_SECONDS);\n Validate.expiration(exp);\n\n const jwt = signJwtES256({aud: audience, exp, sub: subject}, input.privateKey, input.publicKey);\n\n const headers = new Headers();\n if (input.contentEncoding === SupportedContentEncoding.AES_128_GCM) {\n headers.set('Authorization', `vapid t=${jwt}, k=${input.publicKey}`);\n } else if (input.contentEncoding === SupportedContentEncoding.AES_GCM) {\n headers.set('Authorization', `WebPush ${jwt}`);\n headers.set('Crypto-Key', `p256ecdsa=${input.publicKey}`);\n } else {\n throw new Error(`Unsupported content encoding: ${input.contentEncoding}`);\n }\n return headers;\n}\n\n","'use strict';\n\n/** Write UInt16BE. */\nexport function u16be(n: number): Buffer {\n const b = Buffer.alloc(2);\n b.writeUInt16BE(n, 0);\n return b;\n}\n\n/** Write UInt32BE. */\nexport function u32be(n: number): Buffer {\n const b = Buffer.alloc(4);\n b.writeUInt32BE(n >>> 0, 0);\n return b;\n}\n\nexport function xorBytes(a: Buffer, b: Buffer): Buffer {\n if (a.length !== b.length) throw new Error('xorBytes: length mismatch');\n const out = Buffer.alloc(a.length);\n for (let i = 0; i < a.length; i++) out[i] = a[i] ^ b[i];\n return out;\n}\n\n/**\n * Convert a record sequence number into a 96-bit (12-octet) big-endian buffer.\n * RFC8188 defines SEQ as a 96-bit unsigned integer starting at 0.\n */\nexport function seq96be(seq: number): Buffer {\n if (!Number.isSafeInteger(seq) || seq < 0) throw new Error('SEQ must be a non-negative safe integer');\n const out = Buffer.alloc(12, 0x00);\n\n // Write into low 64 bits; high 32 bits remain zero.\n const hi = Math.floor(seq / 0x100000000);\n const lo = seq >>> 0;\n out.writeUInt32BE(hi >>> 0, 4);\n out.writeUInt32BE(lo, 8);\n return out;\n}\n","'use strict';\n\nimport crypto from 'crypto';\nimport { MIN_RS } from '../constants';\nimport { xorBytes, seq96be } from '../utils/binary';\n\n/**\n * Build RFC8188 plaintext records from message bytes.\n *\n * Record plaintext length for full records is `rs - 16` because AES-GCM adds a 16-byte tag.\n * The padding delimiter must be present in every record:\n * - 0x01 for all non-last records\n * - 0x02 for the last record\n */\nexport function buildPlaintextRecords(params: {\n message: Buffer;\n rs: number;\n allowMultipleRecords: boolean;\n finalRecordPadding: number;\n}): Buffer[] {\n const { message, rs, allowMultipleRecords, finalRecordPadding } = params;\n\n if (!Number.isInteger(rs) || rs < MIN_RS) {\n throw new Error(`Invalid rs; must be an integer >= ${MIN_RS}.`);\n }\n\n const maxPlain = rs - 16; // AES-GCM tag length is 16\n if (maxPlain <= 1) throw new Error('Invalid rs (too small)');\n\n const maxDataPerFullRecord = maxPlain - 1; // reserve delimiter\n\n if (!allowMultipleRecords && message.length > maxDataPerFullRecord) {\n throw new Error(\n `Payload too large for a single RFC8188 record: ${message.length} bytes. ` +\n `Max is ${maxDataPerFullRecord} bytes for rs=${rs}.`\n );\n }\n\n if (!Number.isInteger(finalRecordPadding) || finalRecordPadding < 0) {\n throw new Error('finalRecordPadding must be a non-negative integer');\n }\n\n const records: Buffer[] = [];\n let offset = 0;\n\n while (offset < message.length) {\n const remaining = message.length - offset;\n const isLast = remaining <= maxDataPerFullRecord;\n\n const dataLen = isLast ? remaining : maxDataPerFullRecord;\n const data = message.subarray(offset, offset + dataLen);\n offset += dataLen;\n\n const delimiter = Buffer.from([isLast ? 0x02 : 0x01]);\n\n if (!isLast) {\n // Full-sized non-last record.\n const paddingLen = maxPlain - (data.length + 1);\n records.push(Buffer.concat([data, delimiter, Buffer.alloc(paddingLen, 0x00)]));\n } else {\n // Last record can be partial and may include padding after delimiter.\n records.push(Buffer.concat([data, delimiter, Buffer.alloc(finalRecordPadding, 0x00)]));\n }\n }\n\n // Empty message -> final record with only delimiter (0x02)\n if (message.length === 0) records.push(Buffer.from([0x02]));\n\n return records;\n}\n\n/**\n * Encrypt RFC8188 plaintext records using AES-128-GCM.\n *\n * Nonce per record: nonce = baseNonce XOR SEQ (96-bit big-endian SEQ starting at 0).\n * AAD is empty.\n */\nexport function encryptRecords(params: {\n cek: Buffer; // 16 bytes\n baseNonce: Buffer; // 12 bytes\n plaintextRecords: Buffer[];\n}): Buffer {\n const { cek, baseNonce, plaintextRecords } = params;\n if (cek.length !== 16) throw new Error('CEK must be 16 bytes');\n if (baseNonce.length !== 12) throw new Error('baseNonce must be 12 bytes');\n\n const out: Buffer[] = [];\n for (let i = 0; i < plaintextRecords.length; i++) {\n const nonce = xorBytes(baseNonce, seq96be(i));\n const cipher = crypto.createCipheriv('aes-128-gcm', cek, nonce);\n\n const enc = Buffer.concat([cipher.update(plaintextRecords[i]), cipher.final()]);\n out.push(Buffer.concat([enc, cipher.getAuthTag()]));\n }\n return Buffer.concat(out);\n}\n","'use strict';\n\nimport crypto from 'crypto';\nimport { base64url } from '../utils/base64url';\nimport { u16be, u32be } from '../utils/binary';\nimport { buildPlaintextRecords, encryptRecords } from './rfc8188';\n\nfunction hmacSha256(key: Buffer, data: Buffer): Buffer {\n return crypto.createHmac('sha256', key).update(data).digest();\n}\n\nfunction assertSubscriptionKeys(p256dh: string, auth: string): void {\n if (!p256dh || typeof p256dh !== 'string') throw new Error('subscription.keys.p256dh must be a string');\n if (!auth || typeof auth !== 'string') throw new Error('subscription.keys.auth must be a string');\n\n const p256dhBytes = base64url.toBuffer(p256dh);\n if (p256dhBytes.length !== 65 || p256dhBytes[0] !== 0x04) {\n throw new Error('subscription.keys.p256dh must decode to a 65-byte uncompressed P-256 public key');\n }\n\n const authBytes = base64url.toBuffer(auth);\n if (authBytes.length < 16) throw new Error('subscription.keys.auth must decode to at least 16 bytes');\n}\n\n/**\n * Encrypt a push payload using RFC8291 + RFC8188 with aes128gcm.\n *\n * Returns the full RFC8188 body:\n * salt(16) || rs(4) || idlen(1) || keyid(=sender pubkey) || encryptedRecords\n */\nexport function encryptAes128GcmBody(params: {\n p256dh: string;\n auth: string;\n payload: Buffer;\n rs: number;\n allowMultipleRecords: boolean;\n finalRecordPadding: number;\n}): Buffer {\n assertSubscriptionKeys(params.p256dh, params.auth);\n\n const uaPublic = base64url.toBuffer(params.p256dh);\n const authSecret = base64url.toBuffer(params.auth);\n\n // Sender ephemeral key\n const ecdh = crypto.createECDH('prime256v1');\n ecdh.generateKeys();\n const asPublic = ecdh.getPublicKey(); // 65 bytes\n const ecdhSecret = ecdh.computeSecret(uaPublic);\n\n // RFC8291: random salt\n const salt = crypto.randomBytes(16);\n\n // RFC8291: PRK_key = HMAC(auth_secret, ecdh_secret)\n const prkKey = hmacSha256(authSecret, ecdhSecret);\n\n // key_info = \"WebPush: info\" || 0x00 || ua_public || as_public\n const keyInfo = Buffer.concat([Buffer.from('WebPush: info', 'utf8'), Buffer.from([0x00]), uaPublic, asPublic]);\n\n // IKM = HMAC(PRK_key, key_info || 0x01)\n const ikm = hmacSha256(prkKey, Buffer.concat([keyInfo, Buffer.from([0x01])]));\n\n // RFC8188: PRK = HMAC(salt, IKM)\n const prk = hmacSha256(salt, ikm);\n\n // RFC8188: CEK / nonce derivation\n const cekInfo = Buffer.concat([Buffer.from('Content-Encoding: aes128gcm', 'utf8'), Buffer.from([0x00])]);\n const nonceInfo = Buffer.concat([Buffer.from('Content-Encoding: nonce', 'utf8'), Buffer.from([0x00])]);\n\n const cek = hmacSha256(prk, Buffer.concat([cekInfo, Buffer.from([0x01])])).subarray(0, 16);\n const baseNonce = hmacSha256(prk, Buffer.concat([nonceInfo, Buffer.from([0x01])])).subarray(0, 12);\n\n const plaintextRecords = buildPlaintextRecords({\n message: params.payload,\n rs: params.rs,\n allowMultipleRecords: params.allowMultipleRecords,\n finalRecordPadding: params.finalRecordPadding,\n });\n\n const encryptedRecords = encryptRecords({ cek, baseNonce, plaintextRecords });\n\n // RFC8188 header: salt || rs || idlen || keyid\n if (asPublic.length !== 65) throw new Error('Sender public key must be 65 bytes');\n\n return Buffer.concat([salt, u32be(params.rs), Buffer.from([asPublic.length]), asPublic, encryptedRecords]);\n}\n\n/**\n * Legacy aesgcm encryption for interop.\n * Returns ciphertext plus values needed for headers: Encryption (salt) and Crypto-Key (dh).\n */\nexport function encryptAesGcmLegacy(params: {\n p256dh: string;\n auth: string;\n payload: Buffer;\n}): { saltB64Url: string; localPublicKey: Buffer; ciphertext: Buffer } {\n assertSubscriptionKeys(params.p256dh, params.auth);\n\n const receiverPub = base64url.toBuffer(params.p256dh);\n const authSecret = base64url.toBuffer(params.auth);\n\n const ecdh = crypto.createECDH('prime256v1');\n ecdh.generateKeys();\n const senderPub = ecdh.getPublicKey();\n const ecdhSecret = ecdh.computeSecret(receiverPub);\n\n const salt = crypto.randomBytes(16);\n\n const prkKey = hmacSha256(authSecret, ecdhSecret);\n const keyInfo = Buffer.concat([Buffer.from('WebPush: info', 'utf8'), Buffer.from([0x00]), receiverPub, senderPub]);\n const ikm = hmacSha256(prkKey, Buffer.concat([keyInfo, Buffer.from([0x01])]));\n const prk = hmacSha256(salt, ikm);\n\n const cekInfo = Buffer.concat([Buffer.from('Content-Encoding: aesgcm', 'utf8'), Buffer.from([0x00])]);\n const nonceInfo = Buffer.concat([Buffer.from('Content-Encoding: nonce', 'utf8'), Buffer.from([0x00])]);\n\n const cek = hmacSha256(prk, Buffer.concat([cekInfo, Buffer.from([0x01])])).subarray(0, 16);\n const nonce = hmacSha256(prk, Buffer.concat([nonceInfo, Buffer.from([0x01])])).subarray(0, 12);\n\n const padLen = 0;\n const plaintext = Buffer.concat([u16be(padLen), Buffer.alloc(padLen, 0x00), params.payload]);\n\n const cipher = crypto.createCipheriv('aes-128-gcm', cek, nonce);\n const enc = Buffer.concat([cipher.update(plaintext), cipher.final()]);\n const tag = cipher.getAuthTag();\n\n return { saltB64Url: base64url.toString(salt), localPublicKey: senderPub, ciphertext: Buffer.concat([enc, tag]) };\n}\n","'use strict';\n\nimport {\n DEFAULT_RS,\n DEFAULT_TTL,\n MIN_RS,\n SupportedContentEncoding,\n SupportedUrgency,\n} from './constants';\nimport type {\n GenerateRequestOptions,\n PushSubscription,\n WebPushConfig,\n WebPushRequestDetails,\n} from './types';\nimport {base64url} from './utils/base64url';\nimport {encryptAes128GcmBody, encryptAesGcmLegacy} from './crypto/webpush-encryption';\nimport {GenerateHeaders, Validate} from \"./vapid\";\n\nexport class WebPushError extends Error {\n constructor(message: string, public readonly response: Response) {\n super(message);\n }\n}\n\nfunction validateTopic(topic: string): void {\n if (!base64url.validate(topic)) throw new Error('Topic contains invalid characters; must be URL-safe base64 chars.');\n if (topic.length > 32) throw new Error('Topic must be <= 32 characters.');\n}\n\nfunction isGcmEndpoint(endpoint: string): boolean {\n return endpoint.startsWith('https://android.googleapis.com/gcm/send');\n}\n\nfunction isFcmEndpoint(endpoint: string): boolean {\n return endpoint.startsWith('https://fcm.googleapis.com/fcm/send') || endpoint.includes('fcm.googleapis.com');\n}\n\nfunction getAudienceFromEndpoint(endpoint: string): string {\n return new URL(endpoint).origin;\n}\n\nfunction assertSupportedEncoding(e: SupportedContentEncoding): void {\n if (!Object.values(SupportedContentEncoding).includes(e)) throw new Error(`Unsupported content encoding: ${e}`);\n}\n\nfunction assertSupportedUrgency(u: SupportedUrgency): void {\n if (!Object.values(SupportedUrgency).includes(u)) throw new Error(`Unsupported urgency: ${u}`);\n}\n\nexport class WebPush {\n constructor(public readonly config: WebPushConfig) {\n Validate.subject(config.vapid.subject);\n Validate.privateKey(config.vapid.privateKey);\n Validate.publicKey(config.vapid.publicKey);\n\n if (config.gcm?.apiKey != null && config.gcm.apiKey.length === 0) {\n throw new Error('The GCM/FCM API Key should be a non-empty string, null, or undefined.');\n }\n }\n\n /**\n * Generate the request (endpoint + fetch init) to send a push message.\n *\n * - For `aes128gcm`, this produces an RFC8188 payload body (header block + encrypted records).\n * - For Web Push, defaults to a single record per RFC8291; multi-record requires `allowMultipleRecords: true`.\n */\n generateRequest(\n subscription: PushSubscription,\n payload?: string | Buffer | Uint8Array | null,\n options?: GenerateRequestOptions\n ): WebPushRequestDetails {\n if (!subscription?.endpoint || typeof subscription.endpoint !== 'string' || subscription.endpoint.length === 0) {\n throw new Error('You must pass a subscription with a valid endpoint URL.');\n }\n\n const timeToLive = options?.TTL ?? DEFAULT_TTL;\n if (!Number.isFinite(timeToLive) || timeToLive < 0) throw new Error('TTL must be a number >= 0.');\n\n const contentEncoding = options?.contentEncoding ?? SupportedContentEncoding.AES_128_GCM;\n assertSupportedEncoding(contentEncoding);\n\n const urgency = options?.urgency ?? SupportedUrgency.NORMAL;\n assertSupportedUrgency(urgency);\n\n const topic = options?.topic;\n if (topic) validateTopic(topic);\n\n const allowMultipleRecords = options?.allowMultipleRecords ?? false;\n const rs = options?.rs ?? DEFAULT_RS;\n if (!Number.isInteger(rs) || rs < MIN_RS) throw new Error(`rs must be an integer >= ${MIN_RS}.`);\n\n const finalRecordPadding = options?.finalRecordPadding ?? 0;\n\n const extraHeaders = options?.headers ?? {};\n const currentGcmKey = options?.gcmAPIKey ?? this.config.gcm?.apiKey ?? null;\n\n const vapidDetails = options?.vapidDetails === undefined ? this.config.vapid : options.vapidDetails;\n\n const headers: Record<string, string> = {\n TTL: String(timeToLive),\n Urgency: urgency,\n ...extraHeaders,\n };\n\n if (topic) headers.Topic = topic;\n\n let body: Buffer | undefined;\n\n const hasPayload = payload != null && payload !== '';\n if (hasPayload) {\n if (!subscription.keys?.p256dh || !subscription.keys?.auth) {\n throw new Error(\"To send a payload, the subscription must include 'keys.p256dh' and 'keys.auth'.\");\n }\n\n const payloadBuf =\n Buffer.isBuffer(payload) ? payload :\n payload instanceof Uint8Array ? Buffer.from(payload) :\n Buffer.from(payload, 'utf8');\n\n headers['Content-Type'] = 'application/octet-stream';\n\n if (contentEncoding === SupportedContentEncoding.AES_128_GCM) {\n const encBody = encryptAes128GcmBody({\n p256dh: subscription.keys.p256dh,\n auth: subscription.keys.auth,\n payload: payloadBuf,\n rs,\n allowMultipleRecords,\n finalRecordPadding,\n });\n\n body = encBody;\n headers['Content-Encoding'] = SupportedContentEncoding.AES_128_GCM;\n headers['Content-Length'] = String(encBody.length);\n } else {\n const enc = encryptAesGcmLegacy({\n p256dh: subscription.keys.p256dh,\n auth: subscription.keys.auth,\n payload: payloadBuf,\n });\n\n body = enc.ciphertext;\n headers['Content-Encoding'] = SupportedContentEncoding.AES_GCM;\n headers['Content-Length'] = String(enc.ciphertext.length);\n headers['Encryption'] = `salt=${enc.saltB64Url}`;\n headers['Crypto-Key'] = `dh=${base64url.toString(enc.localPublicKey)}`;\n }\n } else {\n headers['Content-Length'] = '0';\n }\n\n // Auth: GCM/FCM key OR VAPID\n const endpoint = subscription.endpoint;\n const gcm = isGcmEndpoint(endpoint);\n const fcm = isFcmEndpoint(endpoint);\n\n if (gcm) {\n if (currentGcmKey) headers.Authorization = `key=${currentGcmKey}`;\n else console.warn('GCM endpoint detected but no GCM API key provided.');\n } else if (vapidDetails) {\n const audience = getAudienceFromEndpoint(endpoint);\n const vapidHeaders = GenerateHeaders({\n audience,\n subject: vapidDetails.subject,\n publicKey: vapidDetails.publicKey,\n privateKey: vapidDetails.privateKey,\n contentEncoding,\n });\n\n const auth = vapidHeaders.get('Authorization');\n if (auth) headers.Authorization = auth;\n\n const ck = vapidHeaders.get('Crypto-Key');\n if (ck && contentEncoding === SupportedContentEncoding.AES_GCM) {\n headers['Crypto-Key'] = headers['Crypto-Key'] ? `${headers['Crypto-Key']}; ${ck}` : ck;\n }\n } else if (fcm && currentGcmKey) {\n headers.Authorization = `key=${currentGcmKey}`;\n }\n\n const init: RequestInit = {\n method: 'POST',\n headers,\n body: body as any,\n };\n\n return {endpoint, init};\n }\n\n /**\n * Method to send notification to subscribed device\n * @param subscription\n * @param payload\n * @param options\n */\n async notify(\n subscription: PushSubscription,\n payload?: string | Buffer | Uint8Array | null,\n options?: GenerateRequestOptions & { throwOnInvalidResponse?: boolean },\n ): Promise<Response> {\n const {endpoint, init} = this.generateRequest(subscription, payload, options);\n const res = await fetch(endpoint, init);\n if (!res.ok && options?.throwOnInvalidResponse) throw new WebPushError('Received unexpected response code', res);\n return res;\n }\n}\n"],"names":["SupportedContentEncoding","SupportedUrgency","DEFAULT_TTL","DEFAULT_RS","MIN_RS","base64url","input","obj","DEFAULT_EXPIRATION_SECONDS","MAX_EXPIRATION_SECONDS","getFutureExpirationTimestamp","numSeconds","future","Validate","subject","u","publicKey","pk","privateKey","expiration","maxExp","audience","a","GenerateKeys","ecdh","crypto","pub","priv","publicKeyToJwk","publicKeyB64Url","privateKeyToJwk","privateKeyB64Url","d","derToJose","derSig","keySizeBytes","off","seqLen","n","rLen","r","sLen","rPad","sPad","signJwtES256","payload","header","encodedHeader","encodedPayload","signingInput","x","y","keyObject","joseSig","encodedSig","GenerateHeaders","exp","jwt","headers","u16be","b","u32be","xorBytes","out","i","seq96be","seq","hi","lo","buildPlaintextRecords","params","message","rs","allowMultipleRecords","finalRecordPadding","maxPlain","maxDataPerFullRecord","records","offset","remaining","isLast","dataLen","data","delimiter","paddingLen","encryptRecords","cek","baseNonce","plaintextRecords","nonce","cipher","enc","hmacSha256","key","assertSubscriptionKeys","p256dh","auth","p256dhBytes","encryptAes128GcmBody","uaPublic","authSecret","asPublic","ecdhSecret","salt","prkKey","keyInfo","ikm","prk","cekInfo","nonceInfo","encryptedRecords","encryptAesGcmLegacy","receiverPub","senderPub","padLen","plaintext","tag","WebPushError","response","validateTopic","topic","isGcmEndpoint","endpoint","isFcmEndpoint","getAudienceFromEndpoint","assertSupportedEncoding","assertSupportedUrgency","WebPush","config","subscription","options","timeToLive","contentEncoding","urgency","extraHeaders","currentGcmKey","vapidDetails","body","payloadBuf","encBody","gcm","fcm","vapidHeaders","ck","init","res"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.es.js","sources":["../src/constants.ts","../src/utils/base64url.ts","../src/vapid.ts","../src/utils/binary.ts","../src/crypto/rfc8188.ts","../src/crypto/webpush-encryption.ts","../src/webpush.ts"],"sourcesContent":["'use strict';\n\nexport enum SupportedContentEncoding {\n AES_GCM = 'aesgcm',\n AES_128_GCM = 'aes128gcm',\n}\n\nexport enum SupportedUrgency {\n VERY_LOW = 'very-low',\n LOW = 'low',\n NORMAL = 'normal',\n HIGH = 'high',\n}\n\n/** Default TTL is 4 weeks (seconds). */\nexport const DEFAULT_TTL = 2419200;\n\n/**\n * Default record size (RFC8188 `rs`) for aes128gcm.\n * For Web Push (RFC8291), a single record is required, but `rs` still appears in the body header.\n */\nexport const DEFAULT_RS = 4096;\n\n/** Minimum valid `rs` for aes128gcm per RFC8188 (must be >= 18). */\nexport const MIN_RS = 18;\n","'use strict';\n\nexport const base64url = {\n validate(input: string): boolean {\n // URL-safe base64 without \"=\" padding\n return /^[A-Za-z0-9\\-_]+$/.test(input);\n },\n toBuffer(input: string): Buffer {\n return Buffer.from(input, 'base64url');\n },\n toString(input: Buffer | Uint8Array | string): string {\n const buf = typeof input === 'string' ? Buffer.from(input, 'utf8') : Buffer.from(input);\n return buf.toString('base64url');\n },\n toJSON(obj: any) {\n return this.toString(Buffer.from(JSON.stringify(obj), 'utf8'));\n }\n};\n","'use strict';\n\nimport crypto from 'crypto';\nimport {base64url} from './utils/base64url';\nimport {SupportedContentEncoding} from './constants';\n\nconst DEFAULT_EXPIRATION_SECONDS = 12 * 60 * 60;\nconst MAX_EXPIRATION_SECONDS = 24 * 60 * 60;\n\nfunction getFutureExpirationTimestamp(numSeconds: number): number {\n const future = new Date();\n future.setSeconds(future.getSeconds() + numSeconds);\n return Math.floor(future.getTime() / 1000);\n}\n\nexport const Validate = {\n subject(subject: string | URL): void {\n if (!subject) throw new Error('No subject set in VAPID details.');\n const u = typeof subject === 'string' ? new URL(subject) : subject;\n if (!['https:', 'mailto:'].includes(u.protocol)) {\n throw new Error(`VAPID subject must be https: or mailto:. Got: ${u.toString()}`);\n }\n if (u.hostname === 'localhost') {\n console.warn('VAPID subject points to localhost; some push services may reject this.');\n }\n },\n\n publicKey(publicKey: string): void {\n if (!publicKey) throw new Error('No VAPID public key set.');\n if (typeof publicKey !== 'string' || !base64url.validate(publicKey)) {\n throw new Error('VAPID public key must be base64url without \"=\" padding.');\n }\n const pk = base64url.toBuffer(publicKey);\n if (pk.length !== 65 || pk[0] !== 0x04) {\n throw new Error('VAPID public key must decode to 65-byte uncompressed P-256.');\n }\n },\n\n privateKey(privateKey: string): void {\n if (!privateKey) throw new Error('No VAPID private key set.');\n if (typeof privateKey !== 'string' || !base64url.validate(privateKey)) {\n throw new Error('VAPID private key must be base64url without \"=\" padding.');\n }\n const sk = base64url.toBuffer(privateKey);\n if (sk.length !== 32) throw new Error('VAPID private key must decode to 32 bytes.');\n },\n\n expiration(expiration: number): void {\n if (!Number.isInteger(expiration)) throw new Error('VAPID expiration must be an integer.');\n if (expiration < 0) throw new Error('VAPID expiration must be positive.');\n const maxExp = getFutureExpirationTimestamp(MAX_EXPIRATION_SECONDS);\n if (expiration >= maxExp) throw new Error('VAPID expiration exceeds 24 hours.');\n },\n\n audience(audience: string | URL): void {\n if (!audience) throw new Error('No audience set for VAPID.');\n const a = typeof audience === 'string' ? new URL(audience) : audience;\n if (!a.origin || a.origin === 'null') {\n throw new Error(`VAPID audience must be an origin. Got: ${a.toString()}`);\n }\n },\n};\n\nexport function GenerateKeys(): { publicKey: string; privateKey: string } {\n const ecdh = crypto.createECDH('prime256v1');\n ecdh.generateKeys();\n\n let pub = ecdh.getPublicKey();\n let priv = ecdh.getPrivateKey();\n\n if (priv.length < 32) priv = Buffer.concat([Buffer.alloc(32 - priv.length, 0x00), priv]);\n if (pub.length < 65) pub = Buffer.concat([Buffer.alloc(65 - pub.length, 0x00), pub]);\n\n return {publicKey: base64url.toString(pub), privateKey: base64url.toString(priv)};\n}\n\nfunction publicKeyToJwk(publicKeyB64Url: string): { x: string; y: string } {\n const pub = base64url.toBuffer(publicKeyB64Url);\n if (pub.length !== 65 || pub[0] !== 0x04) throw new Error('Invalid uncompressed P-256 public key.');\n return {x: base64url.toString(pub.subarray(1, 33)), y: base64url.toString(pub.subarray(33, 65))};\n}\n\nfunction privateKeyToJwk(privateKeyB64Url: string): string {\n const d = base64url.toBuffer(privateKeyB64Url);\n if (d.length !== 32) throw new Error('Invalid P-256 private key scalar.');\n return base64url.toString(d);\n}\n\nfunction derToJose(derSig: Buffer, keySizeBytes = 32): Buffer {\n let off = 0;\n if (derSig[off++] !== 0x30) throw new Error('Invalid DER signature (expected SEQUENCE).');\n\n let seqLen = derSig[off++];\n if (seqLen & 0x80) {\n const n = seqLen & 0x7f;\n off += n;\n }\n\n if (derSig[off++] !== 0x02) throw new Error('Invalid DER signature (expected INTEGER r).');\n const rLen = derSig[off++];\n let r = derSig.subarray(off, off + rLen);\n off += rLen;\n\n if (derSig[off++] !== 0x02) throw new Error('Invalid DER signature (expected INTEGER s).');\n const sLen = derSig[off++];\n let s = derSig.subarray(off, off + sLen);\n\n if (r[0] === 0x00) r = r.subarray(1);\n if (s[0] === 0x00) s = s.subarray(1);\n\n if (r.length > keySizeBytes || s.length > keySizeBytes) throw new Error('Invalid DER signature size.');\n const rPad = Buffer.concat([Buffer.alloc(keySizeBytes - r.length, 0x00), r]);\n const sPad = Buffer.concat([Buffer.alloc(keySizeBytes - s.length, 0x00), s]);\n return Buffer.concat([rPad, sPad]);\n}\n\nfunction signJwtES256(payload: Record<string, unknown>, privateKeyB64Url: string, publicKeyB64Url: string): string {\n const header = {typ: 'JWT', alg: 'ES256'};\n const encodedHeader = base64url.toJSON(header);\n const encodedPayload = base64url.toJSON(payload);\n const signingInput = `${encodedHeader}.${encodedPayload}`;\n\n const {x, y} = publicKeyToJwk(publicKeyB64Url);\n const d = privateKeyToJwk(privateKeyB64Url);\n\n const keyObject = crypto.createPrivateKey({\n key: {kty: 'EC', crv: 'P-256', x, y, d},\n format: 'jwk',\n });\n\n const derSig = crypto.sign('sha256', Buffer.from(signingInput, 'utf8'), {\n key: keyObject,\n dsaEncoding: 'der',\n });\n\n const joseSig = derToJose(derSig, 32);\n const encodedSig = base64url.toString(joseSig);\n return `${signingInput}.${encodedSig}`;\n}\n\nexport function GenerateHeaders(input: {\n audience: string | URL;\n subject: string | URL;\n publicKey: string;\n privateKey: string;\n contentEncoding: SupportedContentEncoding;\n expiration?: number;\n}): Headers {\n Validate.audience(input.audience);\n Validate.subject(input.subject);\n Validate.publicKey(input.publicKey);\n Validate.privateKey(input.privateKey);\n\n const audience = (typeof input.audience === 'string' ? new URL(input.audience) : input.audience).origin;\n const subject = (typeof input.subject === 'string' ? new URL(input.subject) : input.subject).toString();\n\n const exp = input.expiration ?? getFutureExpirationTimestamp(DEFAULT_EXPIRATION_SECONDS);\n Validate.expiration(exp);\n\n const jwt = signJwtES256({aud: audience, exp, sub: subject}, input.privateKey, input.publicKey);\n\n const headers = new Headers();\n if (input.contentEncoding === SupportedContentEncoding.AES_128_GCM) {\n headers.set('Authorization', `vapid t=${jwt}, k=${input.publicKey}`);\n } else if (input.contentEncoding === SupportedContentEncoding.AES_GCM) {\n headers.set('Authorization', `WebPush ${jwt}`);\n headers.set('Crypto-Key', `p256ecdsa=${input.publicKey}`);\n } else {\n throw new Error(`Unsupported content encoding: ${input.contentEncoding}`);\n }\n return headers;\n}\n\n","'use strict';\n\n/** Write UInt16BE. */\nexport function u16be(n: number): Buffer {\n const b = Buffer.alloc(2);\n b.writeUInt16BE(n, 0);\n return b;\n}\n\n/** Write UInt32BE. */\nexport function u32be(n: number): Buffer {\n const b = Buffer.alloc(4);\n b.writeUInt32BE(n >>> 0, 0);\n return b;\n}\n\nexport function xorBytes(a: Buffer, b: Buffer): Buffer {\n if (a.length !== b.length) throw new Error('xorBytes: length mismatch');\n const out = Buffer.alloc(a.length);\n for (let i = 0; i < a.length; i++) out[i] = a[i] ^ b[i];\n return out;\n}\n\n/**\n * Convert a record sequence number into a 96-bit (12-octet) big-endian buffer.\n * RFC8188 defines SEQ as a 96-bit unsigned integer starting at 0.\n */\nexport function seq96be(seq: number): Buffer {\n if (!Number.isSafeInteger(seq) || seq < 0) throw new Error('SEQ must be a non-negative safe integer');\n const out = Buffer.alloc(12, 0x00);\n\n // Write into low 64 bits; high 32 bits remain zero.\n const hi = Math.floor(seq / 0x100000000);\n const lo = seq >>> 0;\n out.writeUInt32BE(hi >>> 0, 4);\n out.writeUInt32BE(lo, 8);\n return out;\n}\n","'use strict';\n\nimport crypto from 'crypto';\nimport { MIN_RS } from '../constants';\nimport { xorBytes, seq96be } from '../utils/binary';\n\n/**\n * Build RFC8188 plaintext records from message bytes.\n *\n * Record plaintext length for full records is `rs - 16` because AES-GCM adds a 16-byte tag.\n * The padding delimiter must be present in every record:\n * - 0x01 for all non-last records\n * - 0x02 for the last record\n */\nexport function buildPlaintextRecords(params: {\n message: Buffer;\n rs: number;\n allowMultipleRecords: boolean;\n finalRecordPadding: number;\n}): Buffer[] {\n const { message, rs, allowMultipleRecords, finalRecordPadding } = params;\n\n if (!Number.isInteger(rs) || rs < MIN_RS) {\n throw new Error(`Invalid rs; must be an integer >= ${MIN_RS}.`);\n }\n\n const maxPlain = rs - 16; // AES-GCM tag length is 16\n if (maxPlain <= 1) throw new Error('Invalid rs (too small)');\n\n const maxDataPerFullRecord = maxPlain - 1; // reserve delimiter\n\n if (!allowMultipleRecords && message.length > maxDataPerFullRecord) {\n throw new Error(\n `Payload too large for a single RFC8188 record: ${message.length} bytes. ` +\n `Max is ${maxDataPerFullRecord} bytes for rs=${rs}.`\n );\n }\n\n if (!Number.isInteger(finalRecordPadding) || finalRecordPadding < 0) {\n throw new Error('finalRecordPadding must be a non-negative integer');\n }\n\n const records: Buffer[] = [];\n let offset = 0;\n\n while (offset < message.length) {\n const remaining = message.length - offset;\n const isLast = remaining <= maxDataPerFullRecord;\n\n const dataLen = isLast ? remaining : maxDataPerFullRecord;\n const data = message.subarray(offset, offset + dataLen);\n offset += dataLen;\n\n const delimiter = Buffer.from([isLast ? 0x02 : 0x01]);\n\n if (!isLast) {\n // Full-sized non-last record.\n const paddingLen = maxPlain - (data.length + 1);\n records.push(Buffer.concat([data, delimiter, Buffer.alloc(paddingLen, 0x00)]));\n } else {\n // Last record can be partial and may include padding after delimiter.\n records.push(Buffer.concat([data, delimiter, Buffer.alloc(finalRecordPadding, 0x00)]));\n }\n }\n\n // Empty message -> final record with only delimiter (0x02)\n if (message.length === 0) records.push(Buffer.from([0x02]));\n\n return records;\n}\n\n/**\n * Encrypt RFC8188 plaintext records using AES-128-GCM.\n *\n * Nonce per record: nonce = baseNonce XOR SEQ (96-bit big-endian SEQ starting at 0).\n * AAD is empty.\n */\nexport function encryptRecords(params: {\n cek: Buffer; // 16 bytes\n baseNonce: Buffer; // 12 bytes\n plaintextRecords: Buffer[];\n}): Buffer {\n const { cek, baseNonce, plaintextRecords } = params;\n if (cek.length !== 16) throw new Error('CEK must be 16 bytes');\n if (baseNonce.length !== 12) throw new Error('baseNonce must be 12 bytes');\n\n const out: Buffer[] = [];\n for (let i = 0; i < plaintextRecords.length; i++) {\n const nonce = xorBytes(baseNonce, seq96be(i));\n const cipher = crypto.createCipheriv('aes-128-gcm', cek, nonce);\n\n const enc = Buffer.concat([cipher.update(plaintextRecords[i]), cipher.final()]);\n out.push(Buffer.concat([enc, cipher.getAuthTag()]));\n }\n return Buffer.concat(out);\n}\n","'use strict';\n\nimport crypto from 'crypto';\nimport { base64url } from '../utils/base64url';\nimport { u16be, u32be } from '../utils/binary';\nimport { buildPlaintextRecords, encryptRecords } from './rfc8188';\n\nfunction hmacSha256(key: Buffer, data: Buffer): Buffer {\n return crypto.createHmac('sha256', key).update(data).digest();\n}\n\nfunction assertSubscriptionKeys(p256dh: string, auth: string): void {\n if (!p256dh || typeof p256dh !== 'string') throw new Error('subscription.keys.p256dh must be a string');\n if (!auth || typeof auth !== 'string') throw new Error('subscription.keys.auth must be a string');\n\n const p256dhBytes = base64url.toBuffer(p256dh);\n if (p256dhBytes.length !== 65 || p256dhBytes[0] !== 0x04) {\n throw new Error('subscription.keys.p256dh must decode to a 65-byte uncompressed P-256 public key');\n }\n\n const authBytes = base64url.toBuffer(auth);\n if (authBytes.length < 16) throw new Error('subscription.keys.auth must decode to at least 16 bytes');\n}\n\n/**\n * Encrypt a push payload using RFC8291 + RFC8188 with aes128gcm.\n *\n * Returns the full RFC8188 body:\n * salt(16) || rs(4) || idlen(1) || keyid(=sender pubkey) || encryptedRecords\n */\nexport function encryptAes128GcmBody(params: {\n p256dh: string;\n auth: string;\n payload: Buffer;\n rs: number;\n allowMultipleRecords: boolean;\n finalRecordPadding: number;\n}): Buffer {\n assertSubscriptionKeys(params.p256dh, params.auth);\n\n const uaPublic = base64url.toBuffer(params.p256dh);\n const authSecret = base64url.toBuffer(params.auth);\n\n // Sender ephemeral key\n const ecdh = crypto.createECDH('prime256v1');\n ecdh.generateKeys();\n const asPublic = ecdh.getPublicKey(); // 65 bytes\n const ecdhSecret = ecdh.computeSecret(uaPublic);\n\n // RFC8291: random salt\n const salt = crypto.randomBytes(16);\n\n // RFC8291: PRK_key = HMAC(auth_secret, ecdh_secret)\n const prkKey = hmacSha256(authSecret, ecdhSecret);\n\n // key_info = \"WebPush: info\" || 0x00 || ua_public || as_public\n const keyInfo = Buffer.concat([Buffer.from('WebPush: info', 'utf8'), Buffer.from([0x00]), uaPublic, asPublic]);\n\n // IKM = HMAC(PRK_key, key_info || 0x01)\n const ikm = hmacSha256(prkKey, Buffer.concat([keyInfo, Buffer.from([0x01])]));\n\n // RFC8188: PRK = HMAC(salt, IKM)\n const prk = hmacSha256(salt, ikm);\n\n // RFC8188: CEK / nonce derivation\n const cekInfo = Buffer.concat([Buffer.from('Content-Encoding: aes128gcm', 'utf8'), Buffer.from([0x00])]);\n const nonceInfo = Buffer.concat([Buffer.from('Content-Encoding: nonce', 'utf8'), Buffer.from([0x00])]);\n\n const cek = hmacSha256(prk, Buffer.concat([cekInfo, Buffer.from([0x01])])).subarray(0, 16);\n const baseNonce = hmacSha256(prk, Buffer.concat([nonceInfo, Buffer.from([0x01])])).subarray(0, 12);\n\n const plaintextRecords = buildPlaintextRecords({\n message: params.payload,\n rs: params.rs,\n allowMultipleRecords: params.allowMultipleRecords,\n finalRecordPadding: params.finalRecordPadding,\n });\n\n const encryptedRecords = encryptRecords({ cek, baseNonce, plaintextRecords });\n\n // RFC8188 header: salt || rs || idlen || keyid\n if (asPublic.length !== 65) throw new Error('Sender public key must be 65 bytes');\n\n return Buffer.concat([salt, u32be(params.rs), Buffer.from([asPublic.length]), asPublic, encryptedRecords]);\n}\n\n/**\n * Legacy aesgcm encryption for interop.\n * Returns ciphertext plus values needed for headers: Encryption (salt) and Crypto-Key (dh).\n */\nexport function encryptAesGcmLegacy(params: {\n p256dh: string;\n auth: string;\n payload: Buffer;\n}): { saltB64Url: string; localPublicKey: Buffer; ciphertext: Buffer } {\n assertSubscriptionKeys(params.p256dh, params.auth);\n\n const receiverPub = base64url.toBuffer(params.p256dh);\n const authSecret = base64url.toBuffer(params.auth);\n\n const ecdh = crypto.createECDH('prime256v1');\n ecdh.generateKeys();\n const senderPub = ecdh.getPublicKey();\n const ecdhSecret = ecdh.computeSecret(receiverPub);\n\n const salt = crypto.randomBytes(16);\n\n const prkKey = hmacSha256(authSecret, ecdhSecret);\n const keyInfo = Buffer.concat([Buffer.from('WebPush: info', 'utf8'), Buffer.from([0x00]), receiverPub, senderPub]);\n const ikm = hmacSha256(prkKey, Buffer.concat([keyInfo, Buffer.from([0x01])]));\n const prk = hmacSha256(salt, ikm);\n\n const cekInfo = Buffer.concat([Buffer.from('Content-Encoding: aesgcm', 'utf8'), Buffer.from([0x00])]);\n const nonceInfo = Buffer.concat([Buffer.from('Content-Encoding: nonce', 'utf8'), Buffer.from([0x00])]);\n\n const cek = hmacSha256(prk, Buffer.concat([cekInfo, Buffer.from([0x01])])).subarray(0, 16);\n const nonce = hmacSha256(prk, Buffer.concat([nonceInfo, Buffer.from([0x01])])).subarray(0, 12);\n\n const padLen = 0;\n const plaintext = Buffer.concat([u16be(padLen), Buffer.alloc(padLen, 0x00), params.payload]);\n\n const cipher = crypto.createCipheriv('aes-128-gcm', cek, nonce);\n const enc = Buffer.concat([cipher.update(plaintext), cipher.final()]);\n const tag = cipher.getAuthTag();\n\n return { saltB64Url: base64url.toString(salt), localPublicKey: senderPub, ciphertext: Buffer.concat([enc, tag]) };\n}\n","'use strict';\n\nimport {\n DEFAULT_RS,\n DEFAULT_TTL,\n MIN_RS,\n SupportedContentEncoding,\n SupportedUrgency,\n} from './constants';\nimport type {\n GenerateRequestOptions,\n PushSubscription,\n WebPushConfig,\n WebPushRequestDetails,\n} from './types';\nimport {base64url} from './utils/base64url';\nimport {encryptAes128GcmBody, encryptAesGcmLegacy} from './crypto/webpush-encryption';\nimport {GenerateHeaders, Validate} from \"./vapid\";\n\nexport class WebPushError extends Error {\n constructor(message: string, public readonly response: Response) {\n super(message);\n }\n}\n\nfunction validateTopic(topic: string): void {\n if (!base64url.validate(topic)) throw new Error('Topic contains invalid characters; must be URL-safe base64 chars.');\n if (topic.length > 32) throw new Error('Topic must be <= 32 characters.');\n}\n\nfunction isGcmEndpoint(endpoint: string): boolean {\n return endpoint.startsWith('https://android.googleapis.com/gcm/send');\n}\n\nfunction isFcmEndpoint(endpoint: string): boolean {\n return endpoint.startsWith('https://fcm.googleapis.com/fcm/send') || endpoint.includes('fcm.googleapis.com');\n}\n\nfunction getAudienceFromEndpoint(endpoint: string): string {\n return new URL(endpoint).origin;\n}\n\nfunction assertSupportedEncoding(e: SupportedContentEncoding): void {\n if (!Object.values(SupportedContentEncoding).includes(e)) throw new Error(`Unsupported content encoding: ${e}`);\n}\n\nfunction assertSupportedUrgency(u: SupportedUrgency): void {\n if (!Object.values(SupportedUrgency).includes(u)) throw new Error(`Unsupported urgency: ${u}`);\n}\n\nexport class WebPush {\n constructor(public readonly config: WebPushConfig) {\n Validate.subject(config.vapid.subject);\n Validate.privateKey(config.vapid.privateKey);\n Validate.publicKey(config.vapid.publicKey);\n\n if (config.gcm?.apiKey != null && config.gcm.apiKey.length === 0) {\n throw new Error('The GCM/FCM API Key should be a non-empty string, null, or undefined.');\n }\n }\n\n /**\n * Generate the request (endpoint + fetch init) to send a push message.\n *\n * - For `aes128gcm`, this produces an RFC8188 payload body (header block + encrypted records).\n * - For Web Push, defaults to a single record per RFC8291; multi-record requires `allowMultipleRecords: true`.\n */\n generateRequest(\n subscription: PushSubscription,\n payload?: string | Buffer | Uint8Array | null,\n options?: GenerateRequestOptions\n ): WebPushRequestDetails {\n if (!subscription?.endpoint || typeof subscription.endpoint !== 'string' || subscription.endpoint.length === 0) {\n throw new Error('You must pass a subscription with a valid endpoint URL.');\n }\n\n const timeToLive = options?.TTL ?? DEFAULT_TTL;\n if (!Number.isFinite(timeToLive) || timeToLive < 0) throw new Error('TTL must be a number >= 0.');\n\n const contentEncoding = options?.contentEncoding ?? SupportedContentEncoding.AES_128_GCM;\n assertSupportedEncoding(contentEncoding);\n\n const urgency = options?.urgency ?? SupportedUrgency.NORMAL;\n assertSupportedUrgency(urgency);\n\n const topic = options?.topic;\n if (topic) validateTopic(topic);\n\n const allowMultipleRecords = options?.allowMultipleRecords ?? false;\n const rs = options?.rs ?? DEFAULT_RS;\n if (!Number.isInteger(rs) || rs < MIN_RS) throw new Error(`rs must be an integer >= ${MIN_RS}.`);\n\n const finalRecordPadding = options?.finalRecordPadding ?? 0;\n\n const extraHeaders = options?.headers ?? {};\n const currentGcmKey = options?.gcmAPIKey ?? this.config.gcm?.apiKey ?? null;\n\n const vapidDetails = options?.vapidDetails === undefined ? this.config.vapid : options.vapidDetails;\n\n const headers: Record<string, string> = {\n ...extraHeaders,\n // Keep protocol-critical defaults authoritative even when custom headers are supplied.\n TTL: String(timeToLive),\n Urgency: urgency,\n };\n\n if (topic) headers.Topic = topic;\n\n let body: Buffer | undefined;\n\n const hasPayload = payload != null && payload !== '';\n if (hasPayload) {\n if (!subscription.keys?.p256dh || !subscription.keys?.auth) {\n throw new Error(\"To send a payload, the subscription must include 'keys.p256dh' and 'keys.auth'.\");\n }\n\n const payloadBuf =\n Buffer.isBuffer(payload) ? payload :\n payload instanceof Uint8Array ? Buffer.from(payload) :\n Buffer.from(payload, 'utf8');\n\n headers['Content-Type'] = 'application/octet-stream';\n\n if (contentEncoding === SupportedContentEncoding.AES_128_GCM) {\n const encBody = encryptAes128GcmBody({\n p256dh: subscription.keys.p256dh,\n auth: subscription.keys.auth,\n payload: payloadBuf,\n rs,\n allowMultipleRecords,\n finalRecordPadding,\n });\n\n body = encBody;\n headers['Content-Encoding'] = SupportedContentEncoding.AES_128_GCM;\n headers['Content-Length'] = String(encBody.length);\n } else {\n const enc = encryptAesGcmLegacy({\n p256dh: subscription.keys.p256dh,\n auth: subscription.keys.auth,\n payload: payloadBuf,\n });\n\n body = enc.ciphertext;\n headers['Content-Encoding'] = SupportedContentEncoding.AES_GCM;\n headers['Content-Length'] = String(enc.ciphertext.length);\n headers['Encryption'] = `salt=${enc.saltB64Url}`;\n headers['Crypto-Key'] = `dh=${base64url.toString(enc.localPublicKey)}`;\n }\n } else {\n headers['Content-Length'] = '0';\n }\n\n // Auth: GCM/FCM key OR VAPID\n const endpoint = subscription.endpoint;\n const gcm = isGcmEndpoint(endpoint);\n const fcm = isFcmEndpoint(endpoint);\n\n if (gcm) {\n if (currentGcmKey) headers.Authorization = `key=${currentGcmKey}`;\n else console.warn('GCM endpoint detected but no GCM API key provided.');\n } else if (vapidDetails) {\n const audience = getAudienceFromEndpoint(endpoint);\n const vapidHeaders = GenerateHeaders({\n audience,\n subject: vapidDetails.subject,\n publicKey: vapidDetails.publicKey,\n privateKey: vapidDetails.privateKey,\n contentEncoding,\n });\n\n const auth = vapidHeaders.get('Authorization');\n if (auth) headers.Authorization = auth;\n\n const ck = vapidHeaders.get('Crypto-Key');\n if (ck && contentEncoding === SupportedContentEncoding.AES_GCM) {\n headers['Crypto-Key'] = headers['Crypto-Key'] ? `${headers['Crypto-Key']}; ${ck}` : ck;\n }\n } else if (fcm && currentGcmKey) {\n headers.Authorization = `key=${currentGcmKey}`;\n }\n\n const init: RequestInit = {\n method: 'POST',\n headers,\n body: body as any,\n };\n\n return {endpoint, init};\n }\n\n /**\n * Method to send notification to subscribed device\n * @param subscription\n * @param payload\n * @param options\n */\n async notify(\n subscription: PushSubscription,\n payload?: string | Buffer | Uint8Array | null,\n options?: GenerateRequestOptions & { throwOnInvalidResponse?: boolean },\n ): Promise<Response> {\n const {endpoint, init} = this.generateRequest(subscription, payload, options);\n const res = await fetch(endpoint, init);\n if (!res.ok && options?.throwOnInvalidResponse) throw new WebPushError('Received unexpected response code', res);\n return res;\n }\n}\n"],"names":["SupportedContentEncoding","SupportedUrgency","DEFAULT_TTL","DEFAULT_RS","MIN_RS","base64url","input","obj","DEFAULT_EXPIRATION_SECONDS","MAX_EXPIRATION_SECONDS","getFutureExpirationTimestamp","numSeconds","future","Validate","subject","u","publicKey","pk","privateKey","expiration","maxExp","audience","a","GenerateKeys","ecdh","crypto","pub","priv","publicKeyToJwk","publicKeyB64Url","privateKeyToJwk","privateKeyB64Url","d","derToJose","derSig","keySizeBytes","off","seqLen","n","rLen","r","sLen","rPad","sPad","signJwtES256","payload","header","encodedHeader","encodedPayload","signingInput","x","y","keyObject","joseSig","encodedSig","GenerateHeaders","exp","jwt","headers","u16be","b","u32be","xorBytes","out","i","seq96be","seq","hi","lo","buildPlaintextRecords","params","message","rs","allowMultipleRecords","finalRecordPadding","maxPlain","maxDataPerFullRecord","records","offset","remaining","isLast","dataLen","data","delimiter","paddingLen","encryptRecords","cek","baseNonce","plaintextRecords","nonce","cipher","enc","hmacSha256","key","assertSubscriptionKeys","p256dh","auth","p256dhBytes","encryptAes128GcmBody","uaPublic","authSecret","asPublic","ecdhSecret","salt","prkKey","keyInfo","ikm","prk","cekInfo","nonceInfo","encryptedRecords","encryptAesGcmLegacy","receiverPub","senderPub","padLen","plaintext","tag","WebPushError","response","validateTopic","topic","isGcmEndpoint","endpoint","isFcmEndpoint","getAudienceFromEndpoint","assertSupportedEncoding","assertSupportedUrgency","WebPush","config","subscription","options","timeToLive","contentEncoding","urgency","extraHeaders","currentGcmKey","vapidDetails","body","payloadBuf","encBody","gcm","fcm","vapidHeaders","ck","init","res"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-webpush",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "A dependency-free Web Push implementation for Node.js",
|
|
5
5
|
"main": "./dist/index.es.js",
|
|
6
6
|
"module": "./dist/index.es.js",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
],
|
|
18
18
|
"scripts": {
|
|
19
19
|
"build": "tsc && vite build",
|
|
20
|
+
"typecheck": "tsc --noEmit",
|
|
20
21
|
"preview": "vite preview",
|
|
21
22
|
"test": "vitest",
|
|
22
23
|
"test:ui": "vitest --ui",
|
|
@@ -50,17 +51,17 @@
|
|
|
50
51
|
"@semantic-release/changelog": "^6.0.3",
|
|
51
52
|
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
52
53
|
"@semantic-release/git": "^10.0.1",
|
|
53
|
-
"@semantic-release/npm": "^13.1.
|
|
54
|
+
"@semantic-release/npm": "^13.1.5",
|
|
54
55
|
"@semantic-release/release-notes-generator": "^14.1.0",
|
|
55
|
-
"@types/node": "^
|
|
56
|
-
"@vitest/coverage-v8": "^4.0.
|
|
57
|
-
"@vitest/ui": "^4.0.
|
|
56
|
+
"@types/node": "^25.3.5",
|
|
57
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
58
|
+
"@vitest/ui": "^4.0.18",
|
|
58
59
|
"tsx": "^4.20.4",
|
|
59
60
|
"typescript": "^5.9.3",
|
|
60
|
-
"vite": "^7.3.
|
|
61
|
+
"vite": "^7.3.1",
|
|
61
62
|
"vite-plugin-dts": "^4.5.4",
|
|
62
|
-
"vitest": "^4.0.
|
|
63
|
-
"typedoc": "^0.28.
|
|
63
|
+
"vitest": "^4.0.18",
|
|
64
|
+
"typedoc": "^0.28.17"
|
|
64
65
|
},
|
|
65
66
|
"release": {
|
|
66
67
|
"branches": [
|