@tencentcloud/web-push 1.0.2 → 1.0.4
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/CHANGELOG.md +8 -0
- package/README.md +23 -88
- package/index.d.ts +186 -37
- package/index.esm.js +1118 -158
- package/index.umd.js +1 -1
- package/package.json +1 -1
- package/src/components/message-popup.d.ts +63 -0
- package/src/core/web-push-sdk.d.ts +23 -1
- package/src/index.d.ts +1 -0
- package/src/types/inner.d.ts +2 -10
- package/src/types/outer.d.ts +90 -36
- package/src/utils/logger.d.ts +6 -0
- package/src/utils/validator.d.ts +0 -13
- package/sw.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.0.4] - 2025-01-27
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **Online Push Popup Feature**: Real-time in-app message popup display to enhance user experience
|
|
8
|
+
- **Multiple Message Type Support**: Standard (standard template), HTML (custom HTML), Custom (fully customizable)
|
|
9
|
+
- **Log Level Control**: Added `logLevel` parameter to registerPush, supporting 5 levels of log control
|
|
10
|
+
|
|
3
11
|
## [1.0.2] - 2025-01-16
|
|
4
12
|
|
|
5
13
|
- ✨ Added UMD build support for direct `<script>` tag integration
|
package/README.md
CHANGED
|
@@ -6,6 +6,8 @@ A web-based push notification SDK built on modern Web APIs including Service Wor
|
|
|
6
6
|
|
|
7
7
|
- 🚀 Built on modern Web standard APIs
|
|
8
8
|
- 📱 Cross-platform push notification support
|
|
9
|
+
- 💬 **Online Push Popup**: Real-time in-app message popup display
|
|
10
|
+
- 🎨 **Custom Popup Styles**: Full customization support for position, styling, and behavior
|
|
9
11
|
- 🔧 TypeScript support
|
|
10
12
|
- 📊 Built-in analytics functionality
|
|
11
13
|
- 🎯 Event-driven architecture
|
|
@@ -52,23 +54,13 @@ Include directly via `<script>` tag:
|
|
|
52
54
|
|
|
53
55
|
// Usage is the same as ES6 modules
|
|
54
56
|
webPush.registerPush({
|
|
55
|
-
SDKAppID:
|
|
56
|
-
appKey: '
|
|
57
|
-
userID: '
|
|
57
|
+
SDKAppID: 0,
|
|
58
|
+
appKey: '',
|
|
59
|
+
userID: '',
|
|
58
60
|
});
|
|
59
61
|
</script>
|
|
60
62
|
```
|
|
61
63
|
|
|
62
|
-
Or download and use locally:
|
|
63
|
-
|
|
64
|
-
```html
|
|
65
|
-
<!-- Include Chat SDK -->
|
|
66
|
-
<script src="./path/to/chat-sdk.js"></script>
|
|
67
|
-
|
|
68
|
-
<!-- Include Web Push SDK -->
|
|
69
|
-
<script src="./dist/index.umd.js"></script>
|
|
70
|
-
```
|
|
71
|
-
|
|
72
64
|
### Step 2: Configure the Service Worker File
|
|
73
65
|
|
|
74
66
|
After integrating `@tencentcloud/web-push`, copy the **Service Worker (sw.js)** to your project's **root directory**. After website deployment, ensure this file can be accessed through `https://your-domain.com/sw.js`. Otherwise, the browser will be unable to register the **Service Worker**.
|
|
@@ -136,22 +128,22 @@ In your homepage (for example: `index.js`), add `@tencentcloud/web-push` and reg
|
|
|
136
128
|
</table>
|
|
137
129
|
|
|
138
130
|
```javascript
|
|
139
|
-
import
|
|
131
|
+
import webPush from '@tencentcloud/web-push';
|
|
140
132
|
|
|
141
133
|
const SDKAppID = 0; // Your SDKAppID
|
|
142
134
|
const appKey = ''; // client key
|
|
143
135
|
const userID = ''; // user ID
|
|
144
136
|
|
|
145
|
-
// Register
|
|
146
|
-
await
|
|
137
|
+
// Register push service
|
|
138
|
+
await webPush.registerPush({ SDKAppID, appKey, userID });
|
|
147
139
|
|
|
148
140
|
// Listen to push messages
|
|
149
|
-
|
|
141
|
+
webPush.addPushListener(webPush.EVENT.MESSAGE_RECEIVED, (message) => {
|
|
150
142
|
console.log('received push message:', message);
|
|
151
143
|
});
|
|
152
144
|
|
|
153
145
|
// Listen to notification click
|
|
154
|
-
|
|
146
|
+
webPush.addPushListener(webPush.EVENT.NOTIFICATION_CLICKED, (data) => {
|
|
155
147
|
console.log('notification clicked:', data);
|
|
156
148
|
});
|
|
157
149
|
```
|
|
@@ -180,9 +172,9 @@ If you're using UMD integration, here's an example:
|
|
|
180
172
|
const webPush = WebPushSDK.webPush;
|
|
181
173
|
|
|
182
174
|
// Configuration parameters
|
|
183
|
-
const SDKAppID =
|
|
184
|
-
const appKey = '
|
|
185
|
-
const userID = '
|
|
175
|
+
const SDKAppID = 0; // Your SDKAppID
|
|
176
|
+
const appKey = ''; // Client key
|
|
177
|
+
const userID = ''; // User ID
|
|
186
178
|
|
|
187
179
|
// Register push service
|
|
188
180
|
webPush
|
|
@@ -226,8 +218,16 @@ interface RegisterPushOptions {
|
|
|
226
218
|
SDKAppID: number;
|
|
227
219
|
appKey: string;
|
|
228
220
|
userID: string;
|
|
229
|
-
serviceWorkerPath?: string;
|
|
230
221
|
chat?: any;
|
|
222
|
+
/**
|
|
223
|
+
* Log level:
|
|
224
|
+
* 0 - Normal level, more logs, recommended for integration
|
|
225
|
+
* 1 - Release level, SDK outputs key information, recommended for production (default)
|
|
226
|
+
* 2 - Warning level, SDK only outputs warning and error level logs
|
|
227
|
+
* 3 - Error level, SDK only outputs error level logs
|
|
228
|
+
* 4 - No log level, SDK will not print any logs
|
|
229
|
+
*/
|
|
230
|
+
logLevel?: LogLevel;
|
|
231
231
|
}
|
|
232
232
|
|
|
233
233
|
enum EVENT {
|
|
@@ -235,76 +235,11 @@ enum EVENT {
|
|
|
235
235
|
MESSAGE_REVOKED = 'message_revoked',
|
|
236
236
|
NOTIFICATION_CLICKED = 'notification_clicked',
|
|
237
237
|
}
|
|
238
|
-
|
|
239
|
-
interface Message {
|
|
240
|
-
ID: string;
|
|
241
|
-
type:
|
|
242
|
-
| 'TIMTextElem'
|
|
243
|
-
| 'TIMImageElem'
|
|
244
|
-
| 'TIMSoundElem'
|
|
245
|
-
| 'TIMVideoFileElem'
|
|
246
|
-
| 'TIMFileElem'
|
|
247
|
-
| 'TIMCustomElem'
|
|
248
|
-
| 'TIMRelayElem'
|
|
249
|
-
| 'TIMLocationElem'
|
|
250
|
-
| 'TIMFaceElem';
|
|
251
|
-
payload: any;
|
|
252
|
-
conversationID: string;
|
|
253
|
-
conversationType: 'C2C' | 'GROUP';
|
|
254
|
-
to: string;
|
|
255
|
-
from: string;
|
|
256
|
-
flow: string;
|
|
257
|
-
time: number;
|
|
258
|
-
status: string;
|
|
259
|
-
isRevoked: boolean;
|
|
260
|
-
nick: string;
|
|
261
|
-
avatar: string;
|
|
262
|
-
isPeerRead: boolean;
|
|
263
|
-
nameCard: string;
|
|
264
|
-
atUserList: string[];
|
|
265
|
-
cloudCustomData: string;
|
|
266
|
-
isDeleted: boolean;
|
|
267
|
-
isModified: boolean;
|
|
268
|
-
needReadReceipt: boolean;
|
|
269
|
-
readReceiptInfo: any;
|
|
270
|
-
isBroadcastMessage: boolean;
|
|
271
|
-
isSupportExtension: boolean;
|
|
272
|
-
receiverList?: string[];
|
|
273
|
-
revoker: string;
|
|
274
|
-
sequence: number;
|
|
275
|
-
progress: number;
|
|
276
|
-
revokerInfo: {
|
|
277
|
-
userID: string;
|
|
278
|
-
nick: string;
|
|
279
|
-
avatar: string;
|
|
280
|
-
};
|
|
281
|
-
revokeReason: string;
|
|
282
|
-
hasRiskContent: boolean;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
interface MessageReceivedResult {
|
|
286
|
-
message: Message;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
interface MessageRevokedResult {
|
|
290
|
-
messageID: string;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
interface MessageNotificationClickedResult {
|
|
294
|
-
ext: string;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
interface EventResult {
|
|
298
|
-
data:
|
|
299
|
-
| MessageReceivedResult
|
|
300
|
-
| MessageRevokedResult
|
|
301
|
-
| MessageNotificationClickedResult;
|
|
302
|
-
}
|
|
303
238
|
```
|
|
304
239
|
|
|
305
240
|
### Event Types
|
|
306
241
|
|
|
307
|
-
- `MESSAGE_RECEIVED`: Received a push message
|
|
242
|
+
- `MESSAGE_RECEIVED`: Received a push message (including all types: standard, html, custom)
|
|
308
243
|
- `MESSAGE_REVOKED`: Message was revoked
|
|
309
244
|
- `NOTIFICATION_CLICKED`: Notification was clicked
|
|
310
245
|
|
package/index.d.ts
CHANGED
|
@@ -1,74 +1,201 @@
|
|
|
1
|
+
export declare interface CustomMessage {
|
|
2
|
+
id: string;
|
|
3
|
+
MsgType: 'custom';
|
|
4
|
+
MsgContent: {
|
|
5
|
+
Data: string | object;
|
|
6
|
+
};
|
|
7
|
+
Ext?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
1
10
|
export declare enum EVENT {
|
|
2
11
|
MESSAGE_RECEIVED = "message_received",
|
|
3
12
|
MESSAGE_REVOKED = "message_revoked",
|
|
4
|
-
NOTIFICATION_CLICKED = "notification_clicked"
|
|
13
|
+
NOTIFICATION_CLICKED = "notification_clicked",
|
|
14
|
+
CUSTOM_MESSAGE_RECEIVED = "custom_message_received"
|
|
5
15
|
}
|
|
6
16
|
|
|
7
17
|
export declare interface EventResult {
|
|
8
18
|
data: MessageReceivedResult | MessageRevokedResult | MessageNotificationClickedResult;
|
|
9
19
|
}
|
|
10
20
|
|
|
11
|
-
export declare interface
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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;
|
|
21
|
+
export declare interface HtmlMessage {
|
|
22
|
+
id: string;
|
|
23
|
+
MsgType: 'html';
|
|
24
|
+
MsgContent: {
|
|
25
|
+
Placement?: Placement;
|
|
26
|
+
Duration?: number;
|
|
27
|
+
Html: string;
|
|
43
28
|
};
|
|
44
|
-
|
|
45
|
-
hasRiskContent: Boolean;
|
|
29
|
+
Ext?: string;
|
|
46
30
|
}
|
|
47
31
|
|
|
32
|
+
export declare type LogLevel = 0 | 1 | 2 | 3 | 4;
|
|
33
|
+
|
|
48
34
|
export declare interface MessageNotificationClickedResult {
|
|
49
35
|
ext: String;
|
|
50
36
|
}
|
|
51
37
|
|
|
38
|
+
export declare class MessagePopup {
|
|
39
|
+
private container;
|
|
40
|
+
private popups;
|
|
41
|
+
private timers;
|
|
42
|
+
private messageHandlers;
|
|
43
|
+
private animationTimers;
|
|
44
|
+
private adjustHeightTimers;
|
|
45
|
+
private config;
|
|
46
|
+
private readonly defaultConfig;
|
|
47
|
+
constructor(config?: PopupConfig);
|
|
48
|
+
private init;
|
|
49
|
+
private createContainer;
|
|
50
|
+
private injectStyles;
|
|
51
|
+
show(message: StandardMessage | HtmlMessage): void;
|
|
52
|
+
private createStandardPopup;
|
|
53
|
+
private createHtmlPopup;
|
|
54
|
+
private addPopup;
|
|
55
|
+
private removeOldestPopup;
|
|
56
|
+
close(messageId: string): void;
|
|
57
|
+
/**
|
|
58
|
+
* 检查指定消息是否正在显示弹窗
|
|
59
|
+
* @param messageId 消息ID
|
|
60
|
+
* @returns 是否正在显示
|
|
61
|
+
*/
|
|
62
|
+
hasMessage(messageId: string): boolean;
|
|
63
|
+
closeAll(): void;
|
|
64
|
+
updateConfig(newConfig: Partial<PopupConfig>): void;
|
|
65
|
+
destroy(): void;
|
|
66
|
+
/**
|
|
67
|
+
* 根据 Placement 值应用容器位置
|
|
68
|
+
* 0-中间,1-左上,2-上中,3-右上,4-右中,5-右下,6-下中,7-左下,8-左中
|
|
69
|
+
*/
|
|
70
|
+
private applyPlacement;
|
|
71
|
+
/**
|
|
72
|
+
* 调整iframe高度以适应内容
|
|
73
|
+
*/
|
|
74
|
+
private adjustIframeHeight;
|
|
75
|
+
/**
|
|
76
|
+
* 创建iframe的完整HTML文档
|
|
77
|
+
*/
|
|
78
|
+
private createIframeHtml;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export declare interface MessageReceivedData {
|
|
82
|
+
content: string;
|
|
83
|
+
info: WebPushInfo;
|
|
84
|
+
extension: WebPushExt;
|
|
85
|
+
}
|
|
86
|
+
|
|
52
87
|
export declare interface MessageReceivedResult {
|
|
53
|
-
message:
|
|
88
|
+
message: PushMessage | NotificationMessage;
|
|
54
89
|
}
|
|
55
90
|
|
|
56
91
|
export declare interface MessageRevokedResult {
|
|
57
92
|
messageID: String;
|
|
58
93
|
}
|
|
59
94
|
|
|
95
|
+
export declare interface NotificationMessage {
|
|
96
|
+
messageID: string;
|
|
97
|
+
title: string;
|
|
98
|
+
body: string;
|
|
99
|
+
icon?: string;
|
|
100
|
+
tag?: string;
|
|
101
|
+
data?: any;
|
|
102
|
+
timestamp: number;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export declare enum Placement {
|
|
106
|
+
CENTER = 0,// 中间
|
|
107
|
+
TOP_LEFT = 1,// 左上
|
|
108
|
+
TOP_CENTER = 2,// 上中
|
|
109
|
+
TOP_RIGHT = 3,// 右上
|
|
110
|
+
MIDDLE_RIGHT = 4,// 右中
|
|
111
|
+
BOTTOM_RIGHT = 5,// 右下
|
|
112
|
+
BOTTOM_CENTER = 6,// 下中
|
|
113
|
+
BOTTOM_LEFT = 7,// 左下
|
|
114
|
+
MIDDLE_LEFT = 8
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
declare interface PopupConfig {
|
|
118
|
+
/** 是否启用弹窗 */
|
|
119
|
+
enabled?: boolean;
|
|
120
|
+
/** 弹窗显示时长(毫秒),0 表示不自动关闭 */
|
|
121
|
+
duration?: number;
|
|
122
|
+
/** 弹窗位置 */
|
|
123
|
+
position?: 'center' | 'top-left' | 'top-center' | 'top-right' | 'right-center' | 'bottom-right' | 'bottom-center' | 'bottom-left' | 'left-center';
|
|
124
|
+
/** 最大显示数量 */
|
|
125
|
+
maxCount?: number;
|
|
126
|
+
/** 是否显示关闭按钮 */
|
|
127
|
+
showCloseButton?: boolean;
|
|
128
|
+
/** 是否允许点击弹窗 */
|
|
129
|
+
clickable?: boolean;
|
|
130
|
+
/** 自定义渲染函数 */
|
|
131
|
+
customRender?: (message: any, container: HTMLElement) => HTMLElement;
|
|
132
|
+
/** 点击回调 */
|
|
133
|
+
onClick?: (message: any, popup: HTMLElement) => void;
|
|
134
|
+
/** 关闭回调 */
|
|
135
|
+
onClose?: (message: any, popup: HTMLElement) => void;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export declare type PushMessage = StandardMessage | HtmlMessage | CustomMessage;
|
|
139
|
+
|
|
60
140
|
export declare interface RegisterPushOptions {
|
|
61
141
|
SDKAppID: number;
|
|
62
142
|
appKey: string;
|
|
63
143
|
userID: string;
|
|
64
144
|
serviceWorkerPath?: string;
|
|
65
145
|
chat?: any;
|
|
146
|
+
/**
|
|
147
|
+
* 日志级别:
|
|
148
|
+
* 0 - 普通级别,日志量较多,接入时建议使用
|
|
149
|
+
* 1 - release级别,SDK 输出关键信息,生产环境时建议使用(默认)
|
|
150
|
+
* 2 - 告警级别,SDK 只输出告警和错误级别的日志
|
|
151
|
+
* 3 - 错误级别,SDK 只输出错误级别的日志
|
|
152
|
+
* 4 - 无日志级别,SDK 将不打印任何日志
|
|
153
|
+
*/
|
|
154
|
+
logLevel?: LogLevel;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export declare interface StandardMessage {
|
|
158
|
+
id: string;
|
|
159
|
+
MsgType: 'standard';
|
|
160
|
+
MsgContent: {
|
|
161
|
+
Text: {
|
|
162
|
+
Title: string;
|
|
163
|
+
Desc: string;
|
|
164
|
+
};
|
|
165
|
+
Image?: {
|
|
166
|
+
URL: string;
|
|
167
|
+
LinkURL: string;
|
|
168
|
+
};
|
|
169
|
+
Close: 0 | 1;
|
|
170
|
+
Placement?: Placement;
|
|
171
|
+
Duration?: number;
|
|
172
|
+
Button?: Array<{
|
|
173
|
+
Text: string;
|
|
174
|
+
Icon?: string;
|
|
175
|
+
Url: string;
|
|
176
|
+
}>;
|
|
177
|
+
};
|
|
178
|
+
Ext?: string;
|
|
66
179
|
}
|
|
67
180
|
|
|
68
181
|
declare const webPush: WebPushSDK_2;
|
|
69
182
|
export default webPush;
|
|
70
183
|
export { webPush }
|
|
71
184
|
|
|
185
|
+
export declare interface WebPushExt {
|
|
186
|
+
WebpushReportUrl: string;
|
|
187
|
+
OnlineClickExt: string;
|
|
188
|
+
TaskId: string;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export declare interface WebPushInfo {
|
|
192
|
+
Title: string;
|
|
193
|
+
Desc: string;
|
|
194
|
+
Icon: string;
|
|
195
|
+
Image: string;
|
|
196
|
+
URL: string;
|
|
197
|
+
}
|
|
198
|
+
|
|
72
199
|
export declare interface WebPushSDK {
|
|
73
200
|
registerPush(options?: RegisterPushOptions): Promise<any>;
|
|
74
201
|
unRegisterPush(): Promise<any>;
|
|
@@ -80,12 +207,14 @@ declare class WebPushSDK_2 implements WebPushSDK {
|
|
|
80
207
|
static instance: WebPushSDK_2;
|
|
81
208
|
private eventEmitter;
|
|
82
209
|
private serviceWorkerManager;
|
|
210
|
+
private messagePopup;
|
|
83
211
|
private isRegistered;
|
|
84
212
|
private registrationID;
|
|
85
213
|
private chat;
|
|
86
214
|
private SDKAppID;
|
|
87
215
|
private appKey;
|
|
88
216
|
private vapidPublicKey;
|
|
217
|
+
private pendingMessages;
|
|
89
218
|
EVENT: typeof EVENT;
|
|
90
219
|
VERSION: string;
|
|
91
220
|
constructor();
|
|
@@ -93,18 +222,38 @@ declare class WebPushSDK_2 implements WebPushSDK {
|
|
|
93
222
|
registerPush(options?: RegisterPushOptions): Promise<string>;
|
|
94
223
|
unRegisterPush(): Promise<boolean>;
|
|
95
224
|
addPushListener(eventName: EVENT, listener: Function): string;
|
|
225
|
+
/**
|
|
226
|
+
* 内部方法:向 Service Worker 发送日志级别配置
|
|
227
|
+
* @param logLevel 日志级别 0-4
|
|
228
|
+
*/
|
|
229
|
+
private sendLogLevelToServiceWorker;
|
|
96
230
|
removePushListener(eventName: EVENT, listener: Function): boolean;
|
|
97
231
|
private checkBrowserSupport;
|
|
98
232
|
private requestNotificationPermission;
|
|
99
|
-
private
|
|
233
|
+
private pushLogin;
|
|
234
|
+
private onMessageReceived;
|
|
235
|
+
private onMessageRevoked;
|
|
236
|
+
private addChatListener;
|
|
237
|
+
private removeChatListener;
|
|
100
238
|
private getSystemPermissionMessage;
|
|
101
239
|
private showSystemPermissionAlert;
|
|
102
240
|
private getBrowserInstType;
|
|
103
241
|
private setToken;
|
|
104
242
|
private initializeBrowserCompatibility;
|
|
243
|
+
private setupVisibilityChangeListener;
|
|
105
244
|
private setupInternalListeners;
|
|
106
245
|
private pushStatistics;
|
|
107
246
|
private clearState;
|
|
247
|
+
/**
|
|
248
|
+
* 向 service-worker 发送通知消息,使用与 push 事件相同的数据格式
|
|
249
|
+
* @param webpushInfo webpush 信息,格式与 SW push 事件接收的数据一致
|
|
250
|
+
*/
|
|
251
|
+
private sendNotificationToServiceWorker;
|
|
252
|
+
/**
|
|
253
|
+
* 向 service-worker 发送消息撤回请求
|
|
254
|
+
* @param messageID 要撤回的消息ID
|
|
255
|
+
*/
|
|
256
|
+
private sendMessageRevokeToServiceWorker;
|
|
108
257
|
}
|
|
109
258
|
|
|
110
259
|
export { }
|