@tencentcloud/web-push 1.0.0

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/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@tencentcloud/web-push",
3
+ "version": "1.0.0",
4
+ "description": "Tencent Cloud Web Push SDK - Service Worker-based web push notification service",
5
+ "main": "index.umd.js",
6
+ "module": "index.esm.js",
7
+ "types": "index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./index.d.ts",
11
+ "import": "./index.esm.js",
12
+ "require": "./index.umd.js"
13
+ },
14
+ "./sw": "./sw.js"
15
+ },
16
+ "files": [
17
+ "**/*"
18
+ ],
19
+ "keywords": [
20
+ "web-push",
21
+ "service-worker",
22
+ "push-notification",
23
+ "tencent-cloud"
24
+ ],
25
+ "author": "Tencent Cloud",
26
+ "license": "MIT",
27
+ "peerDependencies": {
28
+ "@tencentcloud/lite-chat": "^4.2.0"
29
+ }
30
+ }
@@ -0,0 +1,9 @@
1
+ import { EVENT } from '../types';
2
+ export declare class EventEmitter {
3
+ private events;
4
+ on(eventName: EVENT, listener: Function): string;
5
+ off(eventName: EVENT, listener: Function): boolean;
6
+ emit(eventName: EVENT, ...args: any[]): void;
7
+ removeAllListeners(eventName?: EVENT): void;
8
+ listenerCount(eventName: EVENT): number;
9
+ }
@@ -0,0 +1,28 @@
1
+ import { EventEmitter } from './event-emitter';
2
+ import { SubscriptionInfo } from '../types';
3
+ export declare class ServiceWorkerManager {
4
+ private static instance;
5
+ private registration;
6
+ private eventEmitter;
7
+ private swUrl;
8
+ private constructor();
9
+ static getInstance(eventEmitter: EventEmitter): ServiceWorkerManager;
10
+ private checkServiceWorkerExists;
11
+ private autoDetectServiceWorkerUrl;
12
+ setServiceWorkerUrl(url: string): void;
13
+ register(): Promise<ServiceWorkerRegistration>;
14
+ private waitForServiceWorkerReady;
15
+ getPushSubscription(vapidPublicKey: string): Promise<SubscriptionInfo>;
16
+ private getSubscriptionInfo;
17
+ private generateSafariToken;
18
+ private generateFallbackToken;
19
+ private generateEndpointHash;
20
+ unsubscribe(): Promise<boolean>;
21
+ unregister(): Promise<boolean>;
22
+ private setupMessageListener;
23
+ private handleServiceWorkerMessage;
24
+ postMessage(message: any): Promise<void>;
25
+ private arrayBufferToBase64;
26
+ private urlBase64ToUint8Array;
27
+ getRegistration(): ServiceWorkerRegistration | null;
28
+ }
@@ -0,0 +1,33 @@
1
+ import { WebPushSDK as IWebPushSDK, RegisterPushOptions, EVENT } from '../types';
2
+ export declare class WebPushSDK implements IWebPushSDK {
3
+ static instance: WebPushSDK;
4
+ private eventEmitter;
5
+ private serviceWorkerManager;
6
+ private isRegistered;
7
+ private registrationID;
8
+ private chat;
9
+ private SDKAppID;
10
+ private appKey;
11
+ private vapidPublicKey;
12
+ EVENT: typeof EVENT;
13
+ VERSION: string;
14
+ constructor();
15
+ static getInstance(): WebPushSDK;
16
+ registerPush(options?: RegisterPushOptions): Promise<string>;
17
+ unRegisterPush(): Promise<boolean>;
18
+ addPushListener(eventName: EVENT, listener: Function): string;
19
+ removePushListener(eventName: EVENT, listener: Function): boolean;
20
+ private checkBrowserSupport;
21
+ private requestNotificationPermission;
22
+ private chatLogin;
23
+ private getSystemPermissionMessage;
24
+ private showSystemPermissionAlert;
25
+ private getBrowserInstType;
26
+ private setToken;
27
+ private initializeBrowserCompatibility;
28
+ private setupInternalListeners;
29
+ private pushStatistics;
30
+ private saveState;
31
+ private restoreState;
32
+ private clearState;
33
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { WebPushSDK } from './core/web-push-sdk';
2
+ export * from './types/outer';
3
+ declare const webPush: WebPushSDK;
4
+ export { webPush };
5
+ export default webPush;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export * from './inner';
2
+ export * from './outer';
@@ -0,0 +1,47 @@
1
+ export interface PushMessage {
2
+ messageID: string;
3
+ title: string;
4
+ body: string;
5
+ icon?: string;
6
+ tag?: string;
7
+ data?: any;
8
+ timestamp: number;
9
+ }
10
+ export interface NotificationClickData {
11
+ notification: PushMessage;
12
+ action?: string;
13
+ }
14
+ export interface StatisticsData {
15
+ messageID: string;
16
+ type: 'reach' | 'click';
17
+ timestamp: number;
18
+ SDKAppID: number;
19
+ registrationID: string;
20
+ }
21
+ export interface ServiceWorkerMessage {
22
+ type: 'MESSAGE_RECEIVED' | 'NOTIFICATION_CLICKED' | 'MESSAGE_REVOKED';
23
+ data: any;
24
+ }
25
+ export interface SubscriptionInfo {
26
+ subscription: PushSubscription;
27
+ token: string;
28
+ auth: string;
29
+ p256dh: string;
30
+ endpoint: string;
31
+ }
32
+ export interface WebPushCompatibilityInfo {
33
+ isSupported: boolean;
34
+ browserInfo: {
35
+ name: string;
36
+ version: string;
37
+ platform: string;
38
+ majorVersion: number;
39
+ };
40
+ features: {
41
+ serviceWorker: boolean;
42
+ pushManager: boolean;
43
+ notification: boolean;
44
+ };
45
+ warnings: string[];
46
+ recommendations: string[];
47
+ }
@@ -0,0 +1,66 @@
1
+ export interface WebPushSDK {
2
+ registerPush(options?: RegisterPushOptions): Promise<any>;
3
+ unRegisterPush(): Promise<any>;
4
+ addPushListener(eventName: EVENT, listener: (data: EventResult) => void): any;
5
+ removePushListener(eventName: EVENT, listener: (data: EventResult) => void): any;
6
+ }
7
+ export interface RegisterPushOptions {
8
+ SDKAppID: number;
9
+ appKey: string;
10
+ userID: string;
11
+ serviceWorkerPath?: string;
12
+ chat?: any;
13
+ }
14
+ export declare enum EVENT {
15
+ MESSAGE_RECEIVED = "message_received",
16
+ MESSAGE_REVOKED = "message_revoked",
17
+ NOTIFICATION_CLICKED = "notification_clicked"
18
+ }
19
+ export interface Message {
20
+ ID: String;
21
+ type: 'TIMTextElem' | 'TIMImageElem' | 'TIMSoundElem' | 'TIMVideoFileElem' | 'TIMFileElem' | 'TIMCustomElem' | 'TIMRelayElem' | 'TIMLocationElem' | 'TIMFaceElem';
22
+ payload: any;
23
+ conversationID: String;
24
+ conversationType: 'C2C' | 'GROUP';
25
+ to: String;
26
+ from: String;
27
+ flow: String;
28
+ time: Number;
29
+ status: String;
30
+ isRevoked: Boolean;
31
+ nick: String;
32
+ avatar: String;
33
+ isPeerRead: Boolean;
34
+ nameCard: String;
35
+ atUserList: String[];
36
+ cloudCustomData: String;
37
+ isDeleted: Boolean;
38
+ isModified: Boolean;
39
+ needReadReceipt: Boolean;
40
+ readReceiptInfo: any;
41
+ isBroadcastMessage: Boolean;
42
+ isSupportExtension: Boolean;
43
+ receiverList?: String[];
44
+ revoker: String;
45
+ sequence: Number;
46
+ progress: Number;
47
+ revokerInfo: {
48
+ userID: String;
49
+ nick: String;
50
+ avatar: String;
51
+ };
52
+ revokeReason: String;
53
+ hasRiskContent: Boolean;
54
+ }
55
+ export interface MessageReceivedResult {
56
+ message: Message;
57
+ }
58
+ export interface MessageRevokedResult {
59
+ messageID: String;
60
+ }
61
+ export interface MessageNotificationClickedResult {
62
+ ext: String;
63
+ }
64
+ export interface EventResult {
65
+ data: MessageReceivedResult | MessageRevokedResult | MessageNotificationClickedResult;
66
+ }
@@ -0,0 +1,69 @@
1
+ export interface BrowserInfo {
2
+ name: string;
3
+ version: string;
4
+ majorVersion: number;
5
+ platform: string;
6
+ userAgent: string;
7
+ language: string;
8
+ }
9
+ export interface WebPushCapability {
10
+ supported: boolean;
11
+ reason?: string;
12
+ browserName: string;
13
+ browserVersion: string;
14
+ }
15
+ export interface FeatureSupport {
16
+ serviceWorker: boolean;
17
+ pushManager: boolean;
18
+ notification: boolean;
19
+ webPush: boolean;
20
+ }
21
+ export interface PushSubscriptionOptions {
22
+ userVisibleOnly: boolean;
23
+ applicationServerKey?: BufferSource | string;
24
+ }
25
+ export interface NotificationOptions {
26
+ body?: string;
27
+ icon?: string;
28
+ badge?: string;
29
+ image?: string;
30
+ tag?: string;
31
+ data?: any;
32
+ requireInteraction?: boolean;
33
+ silent?: boolean;
34
+ timestamp?: number;
35
+ actions?: Array<{
36
+ action: string;
37
+ title: string;
38
+ icon?: string;
39
+ }>;
40
+ dir?: 'auto' | 'ltr' | 'rtl';
41
+ lang?: string;
42
+ renotify?: boolean;
43
+ vibrate?: number[];
44
+ }
45
+ export declare class BrowserSupport {
46
+ private static instance;
47
+ private browserInfo;
48
+ private constructor();
49
+ static getInstance(): BrowserSupport;
50
+ getBrowserInfo(): BrowserInfo;
51
+ private detectPlatform;
52
+ private detectLanguage;
53
+ getFeatureSupport(): FeatureSupport;
54
+ detectWebPushCapability(): WebPushCapability;
55
+ private checkBrowserVersionSupport;
56
+ isWebPushSupported(): boolean;
57
+ getUnsupportedReason(): string | undefined;
58
+ checkNotificationPermission(): Promise<NotificationPermission>;
59
+ requestNotificationPermission(): Promise<NotificationPermission>;
60
+ requiresUserGesture(): boolean;
61
+ handleBrowserSpecificError(error: Error): NotificationPermission;
62
+ checkBrowserSupport(): boolean;
63
+ }
64
+ export declare const browserSupport: BrowserSupport;
65
+ export declare function getBrowserInfo(): BrowserInfo;
66
+ export declare function getFeatureSupport(): FeatureSupport;
67
+ export declare function detectWebPushCapability(): WebPushCapability;
68
+ export declare function isWebPushSupported(): boolean;
69
+ export declare function getUnsupportedReason(): string | undefined;
@@ -0,0 +1,10 @@
1
+ export declare class Logger {
2
+ private static instance;
3
+ private constructor();
4
+ static getInstance(): Logger;
5
+ log(message: string, ...args: any[]): void;
6
+ warn(message: string, ...args: any[]): void;
7
+ error(message: string, ...args: any[]): void;
8
+ info(message: string, ...args: any[]): void;
9
+ }
10
+ export declare const logger: Logger;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Local storage utility class
3
+ */
4
+ export declare class Storage {
5
+ private static readonly PREFIX;
6
+ static set(key: string, value: any): void;
7
+ static get<T>(key: string, defaultValue?: T): T | null;
8
+ static remove(key: string): void;
9
+ static clear(): void;
10
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Parameter validation utility class
3
+ */
4
+ export interface ValidationRule {
5
+ required?: boolean;
6
+ type?: 'string' | 'number' | 'boolean' | 'object' | 'array' | 'function';
7
+ min?: number;
8
+ max?: number;
9
+ pattern?: RegExp;
10
+ custom?: (value: any) => boolean | string;
11
+ message?: string;
12
+ }
13
+ export interface ValidationSchema {
14
+ [key: string]: ValidationRule;
15
+ }
16
+ export declare class ValidationError extends Error {
17
+ field: string;
18
+ value: any;
19
+ constructor(field: string, message: string, value?: any);
20
+ }
21
+ export declare class Validator {
22
+ /**
23
+ * Validate single value
24
+ */
25
+ static validateValue(fieldName: string, value: any, rule: ValidationRule): void;
26
+ /**
27
+ * Validate object
28
+ */
29
+ static validateObject(obj: any, schema: ValidationSchema): void;
30
+ /**
31
+ * Check type
32
+ */
33
+ private static checkType;
34
+ static validateSDKAppID(SDKAppID: any): void;
35
+ static validateAppKey(appKey: any): void;
36
+ static validateUserID(userID: any): void;
37
+ static validateEventType(eventType: any): void;
38
+ static validateListener(listener: any): void;
39
+ static validateRegisterPushOptions(options: any): void;
40
+ static validatePushMessage(message: any): void;
41
+ /**
42
+ * Validate URL format
43
+ */
44
+ static validateURL(url: any, fieldName?: string): void;
45
+ /**
46
+ * Validate Service Worker path
47
+ */
48
+ static validateServiceWorkerPath(path: any): void;
49
+ }
50
+ /**
51
+ * Parameter validation decorator
52
+ */
53
+ export declare function validateParams(schema: ValidationSchema): (_target: any, _propertyName: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
54
+ /**
55
+ * Single parameter validation decorator
56
+ */
57
+ export declare function validateParam(paramIndex: number, rule: ValidationRule): (_target: any, _propertyName: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
package/sw.js ADDED
@@ -0,0 +1 @@
1
+ !function(){"use strict";function t(t,...e){console.log(`[WebPush SW] ${t}`,...e)}function e(t,...e){console.warn(`[WebPush SW] ${t}`,...e)}function a(t,...e){console.error(`[WebPush SW] ${t}`,...e)}async function i(t){try{(await self.clients.matchAll({includeUncontrolled:!0,type:"window"})).forEach(e=>{e.postMessage(t)})}catch(e){a("Failed to send message to clients",e)}}async function n(i){try{if(!i.rptURL||!i.rptExt)return void e("Missing rptURL or rptExt, skipping report");const a={webpushEvents:[{id:i.id,EventType:i.eventType||1,EventTime:Math.floor(Date.now()/1e3),rptExt:i.rptExt}]};t("Reporting WebPush event",{rptURL:i.rptURL,reportData:a});const n=await fetch(i.rptURL,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(a)});n.ok?t("WebPush event reported successfully"):e("Failed to report WebPush event",n.status,n.statusText)}catch(n){a("Error reporting WebPush event",n)}}self.addEventListener("install",e=>{t("Service Worker installing..."),self.skipWaiting()}),self.addEventListener("activate",e=>{t("Service Worker activating..."),e.waitUntil(self.clients.claim())}),self.addEventListener("push",o=>{if(t("Push message received",o),o.data)try{let e;try{e=o.data.json(),t("Push message data (JSON):",e)}catch{const a=o.data.text();t("Push message data (Text):",a),e={id:"",title:"WebPush Notification",desc:a||"You have a new message",url:"/",icon:"",tag:"",image:"",rptExt:"",rptURL:""}}const a={messageID:e.id||Date.now().toString(36)+Math.random().toString(36).substr(2),title:e.title||"New Message",body:e.desc||"You have a new message",icon:e.icon||"",tag:e.tag||e?.id?.slice(-100)||"push-"+Date.now()+"-"+Math.random().toString(36).substr(2,5),data:{url:e.url,image:e.image,rptExt:e.rptExt,rptURL:e.rptURL},timestamp:Date.now()},s=self.registration.showNotification(a.title,{body:a.body,icon:a.icon,image:e.image,tag:a.tag,data:a,requireInteraction:!1}),r=i({type:"MESSAGE_RECEIVED",data:a}),c=e.rptURL&&e.rptExt?n({id:e.id,rptURL:e.rptURL,rptExt:e.rptExt,eventType:1}):Promise.resolve();o.waitUntil(Promise.all([s,r,c]))}catch(s){a("Failed to process push message",s),o.waitUntil(self.registration.showNotification("WebPush Notification",{body:"You have a new message",icon:"/vite.svg",tag:"fallback"}))}else e("Push message has no data")}),self.addEventListener("notificationclick",e=>{t("Notification clicked",e);const o=e.notification,s=e.action,r=o.data;if(o.close(),"close"===s)return;const c=i({type:"NOTIFICATION_CLICKED",data:{notification:r,action:s}}),l=async function(t="/"){try{const e=await self.clients.matchAll({type:"window",includeUncontrolled:!0}),a=new URL(t,self.location.origin).href;for(const t of e)if(t.url===a&&"focus"in t)return void(await t.focus());for(const i of e)if(i.url.startsWith(self.location.origin)&&"focus"in i)return await i.focus(),void i.postMessage({type:"NAVIGATE_TO",url:t});self.clients.openWindow&&await self.clients.openWindow(t)}catch(e){a("Failed to open or focus window",e)}}(r?.data?.url||"/"),d=r?.data?.rptURL&&r?.data?.rptExt?n({id:r.messageID,rptURL:r.data.rptURL,rptExt:r.data.rptExt,eventType:2}):Promise.resolve();e.waitUntil(Promise.all([c,l,d]))}),self.addEventListener("notificationclose",e=>{t("Notification closed",e)}),self.addEventListener("message",e=>{t("Message received from main thread",e.data);const{type:n,payload:o}=e.data;switch(n){case"SHOW_NOTIFICATION":!async function(e){try{const{eventType:a,data:i,options:n}=e;t("Handling show notification request",{eventType:a,data:i,options:n}),await self.registration.showNotification(n.title,{body:n.body,icon:n.icon,badge:n.badge,tag:n.tag,requireInteraction:n.requireInteraction,silent:n.silent,data:n.data}),t("Notification shown successfully",n.title)}catch(i){a("Failed to handle show notification",i)}}(o);break;case"REVOKE_MESSAGE":!async function(t){try{(await self.registration.getNotifications()).forEach(e=>{e.data&&e.data.messageID===t&&e.close()}),await i({type:"MESSAGE_REVOKED",data:{messageID:t}})}catch(e){a("Failed to handle message revocation",e)}}(o.messageID);break;case"UPDATE_CONFIG":t("Updating configuration",o)}}),self.addEventListener("error",t=>{a("Service Worker error",t.error)}),self.addEventListener("unhandledrejection",t=>{a("Service Worker unhandled promise rejection",t.reason)})}();