{"version":3,"file":"web-push.mjs","names":[],"sources":["../src/web-push.ts"],"sourcesContent":["import { base64UrlToUint8Array, validateBase64Url } from './base64url.ts';\nimport { DEFAULT_TTL, Urgency } from './constants.ts';\nimport { encryptPayload } from './encryption.ts';\nimport { WebPushError } from './error.ts';\nimport type { UrgencyType } from './constants.ts';\nimport {\n\tcreateVapidAuthHeader,\n\tvalidatePrivateKey,\n\tvalidatePublicKey,\n\tvalidateSubject,\n} from './vapid.ts';\n\nexport interface PushSubscription {\n\tendpoint: string;\n\tkeys: {\n\t\tp256dh: string;\n\t\tauth: string;\n\t};\n}\n\nexport interface VapidDetails {\n\tsubject: string;\n\tpublicKey: string;\n\tprivateKey: string;\n}\n\nexport interface SendNotificationOptions {\n\tTTL?: number;\n\theaders?: Record<string, string>;\n\turgency?: UrgencyType;\n\ttopic?: string;\n\tvapidDetails?: VapidDetails;\n\tsignal?: AbortSignal;\n}\n\nexport interface SendResult {\n\tstatusCode: number;\n\theaders: Headers;\n\tbody: string;\n}\n\nexport interface RequestDetails {\n\tendpoint: string;\n\tmethod: string;\n\theaders: Record<string, string>;\n\tbody: Uint8Array<ArrayBuffer> | null;\n}\n\nconst URGENCY_VALUES: ReadonlySet<string> = new Set(Object.values(Urgency));\n\nfunction validateUrgency(urgency: string): urgency is UrgencyType {\n\treturn URGENCY_VALUES.has(urgency);\n}\n\nfunction validateSubscription(subscription: PushSubscription, hasPayload: boolean): void {\n\tif (typeof subscription?.endpoint !== 'string' || subscription.endpoint.length === 0) {\n\t\tthrow new Error('You must pass in a subscription with at least an endpoint.');\n\t}\n\n\tif (!hasPayload) {\n\t\treturn;\n\t}\n\n\tif (\n\t\ttypeof subscription.keys?.p256dh !== 'string' ||\n\t\ttypeof subscription.keys?.auth !== 'string' ||\n\t\tsubscription.keys.p256dh.length === 0 ||\n\t\tsubscription.keys.auth.length === 0\n\t) {\n\t\tthrow new Error(\n\t\t\t\"To send a message with a payload, the subscription must have 'auth' and 'p256dh' keys.\",\n\t\t);\n\t}\n\n\tconst p256dhBytes = base64UrlToUint8Array(subscription.keys.p256dh);\n\tif (p256dhBytes.length !== 65) {\n\t\tthrow new Error('The subscription p256dh value should be 65 bytes long.');\n\t}\n\n\tconst authBytes = base64UrlToUint8Array(subscription.keys.auth);\n\tif (authBytes.length < 16) {\n\t\tthrow new Error('The subscription auth key should be at least 16 bytes long.');\n\t}\n}\n\nfunction validateOptions(options?: SendNotificationOptions): {\n\tttl: number;\n\turgency: UrgencyType;\n\ttopic: string | undefined;\n} {\n\tconst ttl = options?.TTL ?? DEFAULT_TTL;\n\tif (typeof ttl !== 'number' || !Number.isInteger(ttl) || ttl < 0) {\n\t\tthrow new Error('TTL should be a non-negative integer.');\n\t}\n\n\tconst urgency = options?.urgency ?? Urgency.NORMAL;\n\tif (!validateUrgency(urgency)) {\n\t\tthrow new Error('Unsupported urgency specified.');\n\t}\n\n\tconst topic = options?.topic;\n\tif (topic !== undefined && !validateBase64Url(topic)) {\n\t\tthrow new Error('Topic must use URL or filename-safe Base64 characters.');\n\t}\n\tif (topic !== undefined && topic.length > 32) {\n\t\tthrow new Error('Topic must be maximum 32 characters.');\n\t}\n\n\treturn { ttl, urgency, topic };\n}\n\nexport async function generateRequestDetails(\n\tsubscription: PushSubscription,\n\tpayload?: string | Uint8Array<ArrayBuffer> | null,\n\toptions?: SendNotificationOptions,\n): Promise<RequestDetails> {\n\t// oxlint-disable-next-line no-negated-condition -- != null intentionally checks both null and undefined\n\tconst hasPayload = payload != null;\n\tvalidateSubscription(subscription, hasPayload);\n\tconst { ttl, urgency, topic } = validateOptions(options);\n\n\tconst headers: Record<string, string> = {\n\t\tTTL: String(ttl),\n\t\tUrgency: urgency,\n\t\t...options?.headers,\n\t};\n\n\tif (topic !== undefined) {\n\t\theaders['Topic'] = topic;\n\t}\n\n\tlet body: Uint8Array<ArrayBuffer> | null = null;\n\n\tif (hasPayload) {\n\t\tconst subscriberPublicKey = base64UrlToUint8Array(subscription.keys.p256dh);\n\t\tconst authSecret = base64UrlToUint8Array(subscription.keys.auth);\n\n\t\tbody = await encryptPayload(payload, subscriberPublicKey, authSecret);\n\n\t\theaders['Content-Length'] = String(body.length);\n\t\theaders['Content-Type'] = 'application/octet-stream';\n\t\theaders['Content-Encoding'] = 'aes128gcm';\n\t} else {\n\t\theaders['Content-Length'] = '0';\n\t}\n\n\tif (options?.vapidDetails !== undefined) {\n\t\tconst { subject, publicKey, privateKey } = options.vapidDetails;\n\t\tvalidateSubject(subject);\n\t\tvalidatePublicKey(publicKey);\n\t\tvalidatePrivateKey(privateKey);\n\n\t\theaders['Authorization'] = await createVapidAuthHeader(\n\t\t\tsubscription.endpoint,\n\t\t\tpublicKey,\n\t\t\tprivateKey,\n\t\t\tsubject,\n\t\t);\n\t}\n\n\treturn {\n\t\tendpoint: subscription.endpoint,\n\t\tmethod: 'POST',\n\t\theaders,\n\t\tbody,\n\t};\n}\n\nexport async function sendNotification(\n\tsubscription: PushSubscription,\n\tpayload?: string | Uint8Array<ArrayBuffer> | null,\n\toptions?: SendNotificationOptions,\n): Promise<SendResult> {\n\tconst requestDetails = await generateRequestDetails(subscription, payload, options);\n\n\tconst response = await fetch(requestDetails.endpoint, {\n\t\tmethod: requestDetails.method,\n\t\theaders: requestDetails.headers,\n\t\tbody: requestDetails.body,\n\t\tsignal: options?.signal,\n\t});\n\n\tconst responseBody = await response.text();\n\n\tif (!response.ok) {\n\t\tthrow new WebPushError(\n\t\t\t'Received unexpected response code',\n\t\t\tresponse.status,\n\t\t\tresponse.headers,\n\t\t\tresponseBody,\n\t\t\trequestDetails.endpoint,\n\t\t);\n\t}\n\n\treturn {\n\t\tstatusCode: response.status,\n\t\theaders: response.headers,\n\t\tbody: responseBody,\n\t};\n}\n"],"mappings":";;;;;;AAgDA,MAAM,iBAAsC,IAAI,IAAI,OAAO,OAAO,QAAQ,CAAC;AAE3E,SAAS,gBAAgB,SAAyC;AACjE,QAAO,eAAe,IAAI,QAAQ;;AAGnC,SAAS,qBAAqB,cAAgC,YAA2B;AACxF,KAAI,OAAO,cAAc,aAAa,YAAY,aAAa,SAAS,WAAW,EAClF,OAAM,IAAI,MAAM,6DAA6D;AAG9E,KAAI,CAAC,WACJ;AAGD,KACC,OAAO,aAAa,MAAM,WAAW,YACrC,OAAO,aAAa,MAAM,SAAS,YACnC,aAAa,KAAK,OAAO,WAAW,KACpC,aAAa,KAAK,KAAK,WAAW,EAElC,OAAM,IAAI,MACT,yFACA;AAIF,KADoB,sBAAsB,aAAa,KAAK,OAAO,CACnD,WAAW,GAC1B,OAAM,IAAI,MAAM,yDAAyD;AAI1E,KADkB,sBAAsB,aAAa,KAAK,KAAK,CACjD,SAAS,GACtB,OAAM,IAAI,MAAM,8DAA8D;;AAIhF,SAAS,gBAAgB,SAIvB;CACD,MAAM,MAAM,SAAS,OAAA;AACrB,KAAI,OAAO,QAAQ,YAAY,CAAC,OAAO,UAAU,IAAI,IAAI,MAAM,EAC9D,OAAM,IAAI,MAAM,wCAAwC;CAGzD,MAAM,UAAU,SAAS,WAAW,QAAQ;AAC5C,KAAI,CAAC,gBAAgB,QAAQ,CAC5B,OAAM,IAAI,MAAM,iCAAiC;CAGlD,MAAM,QAAQ,SAAS;AACvB,KAAI,UAAU,KAAA,KAAa,CAAC,kBAAkB,MAAM,CACnD,OAAM,IAAI,MAAM,yDAAyD;AAE1E,KAAI,UAAU,KAAA,KAAa,MAAM,SAAS,GACzC,OAAM,IAAI,MAAM,uCAAuC;AAGxD,QAAO;EAAE;EAAK;EAAS;EAAO;;AAG/B,eAAsB,uBACrB,cACA,SACA,SAC0B;CAE1B,MAAM,aAAa,WAAW;AAC9B,sBAAqB,cAAc,WAAW;CAC9C,MAAM,EAAE,KAAK,SAAS,UAAU,gBAAgB,QAAQ;CAExD,MAAM,UAAkC;EACvC,KAAK,OAAO,IAAI;EAChB,SAAS;EACT,GAAG,SAAS;EACZ;AAED,KAAI,UAAU,KAAA,EACb,SAAQ,WAAW;CAGpB,IAAI,OAAuC;AAE3C,KAAI,YAAY;AAIf,SAAO,MAAM,eAAe,SAHA,sBAAsB,aAAa,KAAK,OAAO,EACxD,sBAAsB,aAAa,KAAK,KAAK,CAEK;AAErE,UAAQ,oBAAoB,OAAO,KAAK,OAAO;AAC/C,UAAQ,kBAAkB;AAC1B,UAAQ,sBAAsB;OAE9B,SAAQ,oBAAoB;AAG7B,KAAI,SAAS,iBAAiB,KAAA,GAAW;EACxC,MAAM,EAAE,SAAS,WAAW,eAAe,QAAQ;AACnD,kBAAgB,QAAQ;AACxB,oBAAkB,UAAU;AAC5B,qBAAmB,WAAW;AAE9B,UAAQ,mBAAmB,MAAM,sBAChC,aAAa,UACb,WACA,YACA,QACA;;AAGF,QAAO;EACN,UAAU,aAAa;EACvB,QAAQ;EACR;EACA;EACA;;AAGF,eAAsB,iBACrB,cACA,SACA,SACsB;CACtB,MAAM,iBAAiB,MAAM,uBAAuB,cAAc,SAAS,QAAQ;CAEnF,MAAM,WAAW,MAAM,MAAM,eAAe,UAAU;EACrD,QAAQ,eAAe;EACvB,SAAS,eAAe;EACxB,MAAM,eAAe;EACrB,QAAQ,SAAS;EACjB,CAAC;CAEF,MAAM,eAAe,MAAM,SAAS,MAAM;AAE1C,KAAI,CAAC,SAAS,GACb,OAAM,IAAI,aACT,qCACA,SAAS,QACT,SAAS,SACT,cACA,eAAe,SACf;AAGF,QAAO;EACN,YAAY,SAAS;EACrB,SAAS,SAAS;EAClB,MAAM;EACN"}