@tencentcloud/web-push 1.0.2 → 1.0.3
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/README.md +102 -175
- package/dist/index.d.ts +259 -0
- package/{index.esm.js → dist/index.esm.js} +1118 -158
- package/dist/index.umd.js +1 -0
- package/dist/src/components/message-popup.d.ts +63 -0
- package/{src → dist/src}/core/web-push-sdk.d.ts +23 -1
- package/{src → dist/src}/index.d.ts +1 -0
- package/{src → dist/src}/types/inner.d.ts +2 -10
- package/dist/src/types/outer.d.ts +120 -0
- package/{src → dist/src}/utils/logger.d.ts +6 -0
- package/{src → dist/src}/utils/validator.d.ts +0 -13
- package/dist/sw.js +1 -0
- package/package.json +47 -9
- package/src/__tests__/index.test.ts +120 -0
- package/src/__tests__/integration.test.ts +285 -0
- package/src/__tests__/setup.ts +210 -0
- package/src/__tests__/types.test.ts +303 -0
- package/src/__tests__/web-push-sdk.test.ts +257 -0
- package/src/components/message-popup.ts +1007 -0
- package/src/core/event-emitter.ts +61 -0
- package/src/core/service-worker-manager.ts +614 -0
- package/src/core/web-push-sdk.ts +690 -0
- package/src/debug/GenerateTestUserSig.js +37 -0
- package/src/debug/index.d.ts +6 -0
- package/src/debug/index.js +1 -0
- package/src/debug/lib-generate-test-usersig-es.min.js +2 -0
- package/src/index.ts +9 -0
- package/src/service-worker/sw.ts +494 -0
- package/src/types/index.ts +2 -0
- package/src/types/inner.ts +44 -0
- package/src/types/outer.ts +142 -0
- package/src/utils/browser-support.ts +412 -0
- package/src/utils/logger.ts +66 -0
- package/src/utils/storage.ts +51 -0
- package/src/utils/validator.ts +267 -0
- package/CHANGELOG.md +0 -55
- package/index.d.ts +0 -110
- package/index.umd.js +0 -1
- package/src/types/outer.d.ts +0 -66
- package/sw.js +0 -1
- /package/{src → dist/src}/core/event-emitter.d.ts +0 -0
- /package/{src → dist/src}/core/service-worker-manager.d.ts +0 -0
- /package/{src → dist/src}/service-worker/sw.d.ts +0 -0
- /package/{src → dist/src}/types/index.d.ts +0 -0
- /package/{src → dist/src}/utils/browser-support.d.ts +0 -0
- /package/{src → dist/src}/utils/storage.d.ts +0 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parameter validation utility class
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface ValidationRule {
|
|
6
|
+
required?: boolean;
|
|
7
|
+
type?: 'string' | 'number' | 'boolean' | 'object' | 'array' | 'function';
|
|
8
|
+
min?: number;
|
|
9
|
+
max?: number;
|
|
10
|
+
pattern?: RegExp;
|
|
11
|
+
custom?: (value: any) => boolean | string;
|
|
12
|
+
message?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ValidationSchema {
|
|
16
|
+
[key: string]: ValidationRule;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class ValidationError extends Error {
|
|
20
|
+
public field: string;
|
|
21
|
+
public value: any;
|
|
22
|
+
|
|
23
|
+
constructor(field: string, message: string, value?: any) {
|
|
24
|
+
super(`Parameter validation failed: ${field} - ${message}`);
|
|
25
|
+
this.name = 'ValidationError';
|
|
26
|
+
this.field = field;
|
|
27
|
+
this.value = value;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class Validator {
|
|
32
|
+
/**
|
|
33
|
+
* Validate single value
|
|
34
|
+
*/
|
|
35
|
+
static validateValue(
|
|
36
|
+
fieldName: string,
|
|
37
|
+
value: any,
|
|
38
|
+
rule: ValidationRule
|
|
39
|
+
): void {
|
|
40
|
+
if (
|
|
41
|
+
rule.required &&
|
|
42
|
+
(value === undefined || value === null || value === '')
|
|
43
|
+
) {
|
|
44
|
+
throw new ValidationError(
|
|
45
|
+
fieldName,
|
|
46
|
+
rule.message || `${fieldName} is required`,
|
|
47
|
+
value
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (
|
|
52
|
+
!rule.required &&
|
|
53
|
+
(value === undefined || value === null || value === '')
|
|
54
|
+
) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (rule.type && !this.checkType(value, rule.type)) {
|
|
59
|
+
throw new ValidationError(
|
|
60
|
+
fieldName,
|
|
61
|
+
rule.message || `${fieldName} must be of type ${rule.type}`,
|
|
62
|
+
value
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (rule.type === 'string' && typeof value === 'string') {
|
|
67
|
+
if (rule.min !== undefined && value.length < rule.min) {
|
|
68
|
+
throw new ValidationError(
|
|
69
|
+
fieldName,
|
|
70
|
+
rule.message ||
|
|
71
|
+
`${fieldName} length cannot be less than ${rule.min} characters`,
|
|
72
|
+
value
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
if (rule.max !== undefined && value.length > rule.max) {
|
|
76
|
+
throw new ValidationError(
|
|
77
|
+
fieldName,
|
|
78
|
+
rule.message ||
|
|
79
|
+
`${fieldName} length cannot exceed ${rule.max} characters`,
|
|
80
|
+
value
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (rule.type === 'number' && typeof value === 'number') {
|
|
86
|
+
if (rule.min !== undefined && value < rule.min) {
|
|
87
|
+
throw new ValidationError(
|
|
88
|
+
fieldName,
|
|
89
|
+
rule.message || `${fieldName} cannot be less than ${rule.min}`,
|
|
90
|
+
value
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
if (rule.max !== undefined && value > rule.max) {
|
|
94
|
+
throw new ValidationError(
|
|
95
|
+
fieldName,
|
|
96
|
+
rule.message || `${fieldName} cannot be greater than ${rule.max}`,
|
|
97
|
+
value
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (rule.pattern && typeof value === 'string') {
|
|
103
|
+
if (!rule.pattern.test(value)) {
|
|
104
|
+
throw new ValidationError(
|
|
105
|
+
fieldName,
|
|
106
|
+
rule.message || `${fieldName} format is incorrect`,
|
|
107
|
+
value
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (rule.custom) {
|
|
113
|
+
const result = rule.custom(value);
|
|
114
|
+
if (result !== true) {
|
|
115
|
+
const message =
|
|
116
|
+
typeof result === 'string'
|
|
117
|
+
? result
|
|
118
|
+
: rule.message || `${fieldName} validation failed`;
|
|
119
|
+
throw new ValidationError(fieldName, message, value);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Validate object
|
|
126
|
+
*/
|
|
127
|
+
static validateObject(obj: any, schema: ValidationSchema): void {
|
|
128
|
+
if (!obj || typeof obj !== 'object') {
|
|
129
|
+
throw new ValidationError('root', 'Parameter must be an object type');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
for (const [fieldName, rule] of Object.entries(schema)) {
|
|
133
|
+
const value = obj[fieldName];
|
|
134
|
+
this.validateValue(fieldName, value, rule);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Check type
|
|
140
|
+
*/
|
|
141
|
+
private static checkType(value: any, expectedType: string): boolean {
|
|
142
|
+
switch (expectedType) {
|
|
143
|
+
case 'string':
|
|
144
|
+
return typeof value === 'string';
|
|
145
|
+
case 'number':
|
|
146
|
+
return typeof value === 'number' && !isNaN(value);
|
|
147
|
+
case 'boolean':
|
|
148
|
+
return typeof value === 'boolean';
|
|
149
|
+
case 'object':
|
|
150
|
+
return (
|
|
151
|
+
value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
152
|
+
);
|
|
153
|
+
case 'array':
|
|
154
|
+
return Array.isArray(value);
|
|
155
|
+
case 'function':
|
|
156
|
+
return typeof value === 'function';
|
|
157
|
+
default:
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
static validateSDKAppID(SDKAppID: any): void {
|
|
163
|
+
this.validateValue('SDKAppID', SDKAppID, {
|
|
164
|
+
required: true,
|
|
165
|
+
type: 'number',
|
|
166
|
+
custom: (value) => {
|
|
167
|
+
if (!Number.isInteger(value)) {
|
|
168
|
+
return 'SDKAppID must be an integer';
|
|
169
|
+
}
|
|
170
|
+
return true;
|
|
171
|
+
},
|
|
172
|
+
message: 'SDKAppID must be an integer between 1-999999999',
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
static validateAppKey(appKey: any): void {
|
|
177
|
+
this.validateValue('appKey', appKey, {
|
|
178
|
+
required: true,
|
|
179
|
+
type: 'string',
|
|
180
|
+
pattern: /^[a-zA-Z0-9_-]+$/,
|
|
181
|
+
message:
|
|
182
|
+
'appKey must be a 1-256 character string composed of letters, numbers, underscores or hyphens',
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
static validateUserID(userID: any): void {
|
|
187
|
+
this.validateValue('userID', userID, {
|
|
188
|
+
required: true,
|
|
189
|
+
type: 'string',
|
|
190
|
+
pattern: /^[a-zA-Z0-9_.-]+$/,
|
|
191
|
+
message:
|
|
192
|
+
'userID must be a 1-128 character string composed of letters, numbers, underscores, dots or hyphens',
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
static validateEventType(eventType: any): void {
|
|
197
|
+
const validEvents = [
|
|
198
|
+
'message_received',
|
|
199
|
+
'message_revoked',
|
|
200
|
+
'notification_clicked',
|
|
201
|
+
];
|
|
202
|
+
this.validateValue('eventType', eventType, {
|
|
203
|
+
required: true,
|
|
204
|
+
type: 'string',
|
|
205
|
+
custom: (value) => {
|
|
206
|
+
if (!validEvents.includes(value)) {
|
|
207
|
+
return `eventType must be one of: ${validEvents.join(', ')}`;
|
|
208
|
+
}
|
|
209
|
+
return true;
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
static validateListener(listener: any): void {
|
|
215
|
+
this.validateValue('listener', listener, {
|
|
216
|
+
required: true,
|
|
217
|
+
type: 'function',
|
|
218
|
+
message: 'listener must be a function',
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
static validateRegisterPushOptions(options: any): void {
|
|
223
|
+
if (!options) {
|
|
224
|
+
throw new ValidationError(
|
|
225
|
+
'options',
|
|
226
|
+
'Registration options cannot be empty'
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const schema: ValidationSchema = {
|
|
231
|
+
SDKAppID: {
|
|
232
|
+
required: true,
|
|
233
|
+
type: 'number',
|
|
234
|
+
custom: (value) => {
|
|
235
|
+
if (!Number.isInteger(value)) {
|
|
236
|
+
return 'SDKAppID must be an integer';
|
|
237
|
+
}
|
|
238
|
+
return true;
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
appKey: {
|
|
242
|
+
required: true,
|
|
243
|
+
type: 'string',
|
|
244
|
+
pattern: /^[a-zA-Z0-9_-]+$/,
|
|
245
|
+
},
|
|
246
|
+
userID: {
|
|
247
|
+
required: true,
|
|
248
|
+
type: 'string',
|
|
249
|
+
pattern: /^[a-zA-Z0-9_.-]+$/,
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
this.validateObject(options, schema);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Validate Service Worker path
|
|
258
|
+
*/
|
|
259
|
+
static validateServiceWorkerPath(path: any): void {
|
|
260
|
+
this.validateValue('serviceWorkerPath', path, {
|
|
261
|
+
required: false,
|
|
262
|
+
type: 'string',
|
|
263
|
+
pattern: /^[a-zA-Z0-9_./-]+\.js$/,
|
|
264
|
+
message: 'serviceWorkerPath must be a valid JavaScript file path',
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
}
|
package/CHANGELOG.md
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
## [1.0.2] - 2025-01-16
|
|
4
|
-
|
|
5
|
-
- ✨ Added UMD build support for direct `<script>` tag integration
|
|
6
|
-
- 📚 Enhanced UMD usage documentation and examples
|
|
7
|
-
|
|
8
|
-
## [1.0.1] - 2025-12-19
|
|
9
|
-
|
|
10
|
-
- Optimize subscription logic
|
|
11
|
-
- Optimized state persistence logic, only persisting VAPID public key while resetting temporary states each session
|
|
12
|
-
|
|
13
|
-
## [1.0.0] - 2025-12-17
|
|
14
|
-
|
|
15
|
-
### Added
|
|
16
|
-
|
|
17
|
-
- 🎉 Initial release
|
|
18
|
-
- ✨ Service Worker-based push notification support
|
|
19
|
-
- ✨ Complete TypeScript type definitions
|
|
20
|
-
- ✨ Event-driven message handling mechanism
|
|
21
|
-
- ✨ Built-in analytics functionality
|
|
22
|
-
- ✨ Local state persistence
|
|
23
|
-
- ✨ VAPID authentication support
|
|
24
|
-
- ✨ Cross-platform push notification support
|
|
25
|
-
|
|
26
|
-
### Features
|
|
27
|
-
|
|
28
|
-
- 📱 Support for message received, notification clicked, and message revoked events
|
|
29
|
-
- 🔧 Flexible device identifier management
|
|
30
|
-
- 📊 Automatic message delivery and click rate statistics
|
|
31
|
-
- 🎯 Seamless integration with Chat SDK
|
|
32
|
-
- 💾 Automatic state recovery
|
|
33
|
-
- 🔒 Secure push subscription management
|
|
34
|
-
|
|
35
|
-
### API Interface
|
|
36
|
-
|
|
37
|
-
- `registerPush()` - Register push service
|
|
38
|
-
- `unRegisterPush()` - Unregister push service
|
|
39
|
-
- `addPushListener()` - Add event listener
|
|
40
|
-
- `removePushListener()` - Remove event listener
|
|
41
|
-
|
|
42
|
-
### Browser Support
|
|
43
|
-
|
|
44
|
-
- Chrome 50+
|
|
45
|
-
- Firefox 44+
|
|
46
|
-
- Safari 16+
|
|
47
|
-
- Edge 17+
|
|
48
|
-
|
|
49
|
-
### Development Tools
|
|
50
|
-
|
|
51
|
-
- Vite build tool
|
|
52
|
-
- TypeScript support
|
|
53
|
-
- Jest unit testing
|
|
54
|
-
- ESLint code linting
|
|
55
|
-
- Complete example application
|
package/index.d.ts
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
export declare enum EVENT {
|
|
2
|
-
MESSAGE_RECEIVED = "message_received",
|
|
3
|
-
MESSAGE_REVOKED = "message_revoked",
|
|
4
|
-
NOTIFICATION_CLICKED = "notification_clicked"
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export declare interface EventResult {
|
|
8
|
-
data: MessageReceivedResult | MessageRevokedResult | MessageNotificationClickedResult;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export declare interface Message {
|
|
12
|
-
ID: String;
|
|
13
|
-
type: 'TIMTextElem' | 'TIMImageElem' | 'TIMSoundElem' | 'TIMVideoFileElem' | 'TIMFileElem' | 'TIMCustomElem' | 'TIMRelayElem' | 'TIMLocationElem' | 'TIMFaceElem';
|
|
14
|
-
payload: any;
|
|
15
|
-
conversationID: String;
|
|
16
|
-
conversationType: 'C2C' | 'GROUP';
|
|
17
|
-
to: String;
|
|
18
|
-
from: String;
|
|
19
|
-
flow: String;
|
|
20
|
-
time: Number;
|
|
21
|
-
status: String;
|
|
22
|
-
isRevoked: Boolean;
|
|
23
|
-
nick: String;
|
|
24
|
-
avatar: String;
|
|
25
|
-
isPeerRead: Boolean;
|
|
26
|
-
nameCard: String;
|
|
27
|
-
atUserList: String[];
|
|
28
|
-
cloudCustomData: String;
|
|
29
|
-
isDeleted: Boolean;
|
|
30
|
-
isModified: Boolean;
|
|
31
|
-
needReadReceipt: Boolean;
|
|
32
|
-
readReceiptInfo: any;
|
|
33
|
-
isBroadcastMessage: Boolean;
|
|
34
|
-
isSupportExtension: Boolean;
|
|
35
|
-
receiverList?: String[];
|
|
36
|
-
revoker: String;
|
|
37
|
-
sequence: Number;
|
|
38
|
-
progress: Number;
|
|
39
|
-
revokerInfo: {
|
|
40
|
-
userID: String;
|
|
41
|
-
nick: String;
|
|
42
|
-
avatar: String;
|
|
43
|
-
};
|
|
44
|
-
revokeReason: String;
|
|
45
|
-
hasRiskContent: Boolean;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export declare interface MessageNotificationClickedResult {
|
|
49
|
-
ext: String;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export declare interface MessageReceivedResult {
|
|
53
|
-
message: Message;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export declare interface MessageRevokedResult {
|
|
57
|
-
messageID: String;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export declare interface RegisterPushOptions {
|
|
61
|
-
SDKAppID: number;
|
|
62
|
-
appKey: string;
|
|
63
|
-
userID: string;
|
|
64
|
-
serviceWorkerPath?: string;
|
|
65
|
-
chat?: any;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
declare const webPush: WebPushSDK_2;
|
|
69
|
-
export default webPush;
|
|
70
|
-
export { webPush }
|
|
71
|
-
|
|
72
|
-
export declare interface WebPushSDK {
|
|
73
|
-
registerPush(options?: RegisterPushOptions): Promise<any>;
|
|
74
|
-
unRegisterPush(): Promise<any>;
|
|
75
|
-
addPushListener(eventName: EVENT, listener: (data: EventResult) => void): any;
|
|
76
|
-
removePushListener(eventName: EVENT, listener: (data: EventResult) => void): any;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
declare class WebPushSDK_2 implements WebPushSDK {
|
|
80
|
-
static instance: WebPushSDK_2;
|
|
81
|
-
private eventEmitter;
|
|
82
|
-
private serviceWorkerManager;
|
|
83
|
-
private isRegistered;
|
|
84
|
-
private registrationID;
|
|
85
|
-
private chat;
|
|
86
|
-
private SDKAppID;
|
|
87
|
-
private appKey;
|
|
88
|
-
private vapidPublicKey;
|
|
89
|
-
EVENT: typeof EVENT;
|
|
90
|
-
VERSION: string;
|
|
91
|
-
constructor();
|
|
92
|
-
static getInstance(): WebPushSDK_2;
|
|
93
|
-
registerPush(options?: RegisterPushOptions): Promise<string>;
|
|
94
|
-
unRegisterPush(): Promise<boolean>;
|
|
95
|
-
addPushListener(eventName: EVENT, listener: Function): string;
|
|
96
|
-
removePushListener(eventName: EVENT, listener: Function): boolean;
|
|
97
|
-
private checkBrowserSupport;
|
|
98
|
-
private requestNotificationPermission;
|
|
99
|
-
private chatLogin;
|
|
100
|
-
private getSystemPermissionMessage;
|
|
101
|
-
private showSystemPermissionAlert;
|
|
102
|
-
private getBrowserInstType;
|
|
103
|
-
private setToken;
|
|
104
|
-
private initializeBrowserCompatibility;
|
|
105
|
-
private setupInternalListeners;
|
|
106
|
-
private pushStatistics;
|
|
107
|
-
private clearState;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export { }
|