@bootdesk/js-web-adapter-core 0.3.3 → 0.3.5
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 +42 -11
- package/dist/chat-service-worker.js +163 -0
- package/dist/chat-service-worker.js.map +1 -0
- package/dist/index.cjs +97 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +98 -75
- package/dist/index.d.ts +98 -75
- package/dist/index.js +97 -31
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,12 +14,13 @@ npm install @bootdesk/js-web-adapter-core
|
|
|
14
14
|
import { WebChatClient } from "@bootdesk/js-web-adapter-core";
|
|
15
15
|
|
|
16
16
|
const client = new WebChatClient({
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
apiUrl: "https://your-app.com/api",
|
|
18
|
+
userId: "user-1",
|
|
19
|
+
userName: "Alice",
|
|
19
20
|
});
|
|
20
21
|
|
|
21
|
-
const unsub = client.
|
|
22
|
-
console.log("New message:", event.
|
|
22
|
+
const unsub = client.onMessagePosted((event) => {
|
|
23
|
+
console.log("New message:", event.text);
|
|
23
24
|
});
|
|
24
25
|
|
|
25
26
|
await client.connect();
|
|
@@ -33,19 +34,36 @@ await client.connect();
|
|
|
33
34
|
|--------|-------------|
|
|
34
35
|
| `connect()` | Initialize connection, start listening |
|
|
35
36
|
| `disconnect()` | Cleanup, remove listeners |
|
|
36
|
-
| `loadMessages(
|
|
37
|
+
| `loadMessages(options?)` | Fetch paginated messages |
|
|
37
38
|
| `sendMessage(text, attachments?)` | Send a new message |
|
|
39
|
+
| `sendAction(messageId, actionId, value)` | Send a button action |
|
|
38
40
|
| `editMessage(messageId, text)` | Edit an existing message |
|
|
39
41
|
| `deleteMessage(messageId)` | Delete a message |
|
|
40
42
|
| `addReaction(messageId, emoji)` | Add a reaction |
|
|
41
43
|
| `removeReaction(messageId, emoji)` | Remove a reaction |
|
|
42
|
-
| `
|
|
44
|
+
| `onMessagePosted(cb)` | Subscribe to new messages |
|
|
43
45
|
| `onMessageEdited(cb)` | Subscribe to edits |
|
|
44
46
|
| `onMessageDeleted(cb)` | Subscribe to deletions |
|
|
45
47
|
| `onReactionAdded(cb)` | Subscribe to reaction adds |
|
|
46
48
|
| `onReactionRemoved(cb)` | Subscribe to reaction removes |
|
|
47
49
|
| `onTypingStarted(cb)` | Subscribe to typing events |
|
|
48
50
|
| `onStreamingChunk(cb)` | Subscribe to streaming chunks |
|
|
51
|
+
| `reconfigure(config)` | Update identity (userId, userName, verifyToken, conversationId, headers) after construction |
|
|
52
|
+
|
|
53
|
+
### Reconfiguration
|
|
54
|
+
|
|
55
|
+
Use `reconfigure()` to update the client's identity after construction — useful for pre-entry flows where the user's info is collected first:
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
client.reconfigure({
|
|
59
|
+
userId: "user-abc",
|
|
60
|
+
userName: "Alice",
|
|
61
|
+
verifyToken: "encrypted-token",
|
|
62
|
+
conversationId: "conv-xyz",
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Updates HTTP headers (`X-User-Id`, `X-User-Name`, `X-Verify-Token`) and internal state.
|
|
49
67
|
|
|
50
68
|
### Broadcasting
|
|
51
69
|
|
|
@@ -57,20 +75,33 @@ const broadcast = new PusherBroadcastClient({
|
|
|
57
75
|
cluster: "us2",
|
|
58
76
|
});
|
|
59
77
|
|
|
60
|
-
const client = new WebChatClient({
|
|
78
|
+
const client = new WebChatClient({
|
|
79
|
+
apiUrl: "https://your-app.com/api",
|
|
80
|
+
userId: "user-1",
|
|
81
|
+
userName: "Alice",
|
|
82
|
+
broadcastClient: broadcast,
|
|
83
|
+
});
|
|
61
84
|
```
|
|
62
85
|
|
|
63
86
|
### Push Notifications
|
|
64
87
|
|
|
65
88
|
```typescript
|
|
66
89
|
import { PushManager, createPushSubscriptionHandlers } from "@bootdesk/js-web-adapter-core";
|
|
90
|
+
import { HttpClient } from "@bootdesk/js-web-adapter-core";
|
|
67
91
|
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
)
|
|
92
|
+
const httpClient = new HttpClient({ apiUrl: "https://your-app.com/api" });
|
|
93
|
+
const manager = new PushManager({
|
|
94
|
+
getVapidPublicKey: async () => "your-vapid-public-key",
|
|
95
|
+
onSubscribe: createPushSubscriptionHandlers(httpClient, "user-1").onSubscribe,
|
|
96
|
+
onUnsubscribe: createPushSubscriptionHandlers(httpClient, "user-1").onUnsubscribe,
|
|
97
|
+
});
|
|
72
98
|
|
|
99
|
+
await manager.initialize();
|
|
73
100
|
await manager.subscribe();
|
|
101
|
+
|
|
102
|
+
manager.onMessage((data) => {
|
|
103
|
+
console.log("Push received:", data);
|
|
104
|
+
});
|
|
74
105
|
```
|
|
75
106
|
|
|
76
107
|
## License
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// src/chat-service-worker.ts
|
|
2
|
+
var swLocales = {
|
|
3
|
+
en: { openChat: "Open Chat", dismiss: "Dismiss" },
|
|
4
|
+
pt: { openChat: "Abrir chat", dismiss: "Dispensar" },
|
|
5
|
+
es: { openChat: "Abrir chat", dismiss: "Descartar" },
|
|
6
|
+
da: { openChat: "\xC5bn Chat", dismiss: "Afvis" },
|
|
7
|
+
sv: { openChat: "\xD6ppna Chatt", dismiss: "Avf\xE4rda" },
|
|
8
|
+
nb: { openChat: "\xC5pne Chat", dismiss: "Avvis" },
|
|
9
|
+
fi: { openChat: "Avaa Chat", dismiss: "Hylk\xE4\xE4" },
|
|
10
|
+
fr: { openChat: "Ouvrir le Chat", dismiss: "Ignorer" },
|
|
11
|
+
de: { openChat: "Chat \xF6ffnen", dismiss: "Schlie\xDFen" },
|
|
12
|
+
it: { openChat: "Apri Chat", dismiss: "Ignora" },
|
|
13
|
+
nl: { openChat: "Open Chat", dismiss: "Sluiten" },
|
|
14
|
+
pl: { openChat: "Otw\xF3rz Czat", dismiss: "Odrzu\u0107" },
|
|
15
|
+
cs: { openChat: "Otev\u0159\xEDt Chat", dismiss: "Zav\u0159\xEDt" },
|
|
16
|
+
ro: { openChat: "Deschide Chat", dismiss: "Ignor\u0103" },
|
|
17
|
+
hu: { openChat: "Chat Megnyit\xE1sa", dismiss: "Elutas\xEDt\xE1s" },
|
|
18
|
+
uk: { openChat: "\u0412\u0456\u0434\u043A\u0440\u0438\u0442\u0438 \u0427\u0430\u0442", dismiss: "\u0412\u0456\u0434\u0445\u0438\u043B\u0438\u0442\u0438" },
|
|
19
|
+
ru: { openChat: "\u041E\u0442\u043A\u0440\u044B\u0442\u044C \u0427\u0430\u0442", dismiss: "\u0417\u0430\u043A\u0440\u044B\u0442\u044C" },
|
|
20
|
+
el: { openChat: "\u0386\u03BD\u03BF\u03B9\u03B3\u03BC\u03B1 \u03A3\u03C5\u03BD\u03BF\u03BC\u03B9\u03BB\u03AF\u03B1\u03C2", dismiss: "\u0391\u03C0\u03CC\u03C1\u03C1\u03B9\u03C8\u03B7" },
|
|
21
|
+
tr: { openChat: "Sohbeti A\xE7", dismiss: "Kapat" },
|
|
22
|
+
et: { openChat: "Ava Vestlus", dismiss: "Loobu" },
|
|
23
|
+
ja: { openChat: "\u30C1\u30E3\u30C3\u30C8\u3092\u958B\u304F", dismiss: "\u9589\u3058\u308B" },
|
|
24
|
+
"zh-CN": { openChat: "\u6253\u5F00\u804A\u5929", dismiss: "\u5173\u95ED" },
|
|
25
|
+
"zh-TW": { openChat: "\u958B\u555F\u804A\u5929", dismiss: "\u95DC\u9589" },
|
|
26
|
+
ko: { openChat: "\uCC44\uD305 \uC5F4\uAE30", dismiss: "\uB2EB\uAE30" },
|
|
27
|
+
vi: { openChat: "M\u1EDF Tr\xF2 chuy\u1EC7n", dismiss: "B\u1ECF qua" },
|
|
28
|
+
th: { openChat: "\u0E40\u0E1B\u0E34\u0E14\u0E41\u0E0A\u0E17", dismiss: "\u0E1B\u0E34\u0E14" },
|
|
29
|
+
id: { openChat: "Buka Obrolan", dismiss: "Tutup" },
|
|
30
|
+
hi: { openChat: "\u091A\u0948\u091F \u0916\u094B\u0932\u0947\u0902", dismiss: "\u0916\u093E\u0930\u093F\u091C \u0915\u0930\u0947\u0902" },
|
|
31
|
+
ar: { openChat: "\u0641\u062A\u062D \u0627\u0644\u062F\u0631\u062F\u0634\u0629", dismiss: "\u062A\u062C\u0627\u0647\u0644" }
|
|
32
|
+
};
|
|
33
|
+
function getFallbackChain(locale) {
|
|
34
|
+
const parts = locale.split("-");
|
|
35
|
+
if (parts.length === 1) return [parts[0], "en"];
|
|
36
|
+
return [locale, parts[0], "en"];
|
|
37
|
+
}
|
|
38
|
+
function resolveLocale(locale) {
|
|
39
|
+
if (!locale) return swLocales.en;
|
|
40
|
+
for (const tag of getFallbackChain(locale)) {
|
|
41
|
+
if (swLocales[tag]) return swLocales[tag];
|
|
42
|
+
}
|
|
43
|
+
return swLocales.en;
|
|
44
|
+
}
|
|
45
|
+
function getDefaultActions(locale) {
|
|
46
|
+
const strings = resolveLocale(locale);
|
|
47
|
+
return [
|
|
48
|
+
{ action: "open", title: strings.openChat },
|
|
49
|
+
{ action: "dismiss", title: strings.dismiss }
|
|
50
|
+
];
|
|
51
|
+
}
|
|
52
|
+
var sw = self;
|
|
53
|
+
sw.addEventListener("push", (event) => {
|
|
54
|
+
if (!event.data) return;
|
|
55
|
+
let data;
|
|
56
|
+
try {
|
|
57
|
+
data = event.data.json();
|
|
58
|
+
} catch {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const {
|
|
62
|
+
threadId,
|
|
63
|
+
messageId,
|
|
64
|
+
senderName,
|
|
65
|
+
preview,
|
|
66
|
+
timestamp,
|
|
67
|
+
deepLink,
|
|
68
|
+
type,
|
|
69
|
+
actions,
|
|
70
|
+
locale,
|
|
71
|
+
icon,
|
|
72
|
+
badge
|
|
73
|
+
} = data;
|
|
74
|
+
const notificationType = type ?? "chat";
|
|
75
|
+
event.waitUntil(
|
|
76
|
+
sw.clients.matchAll({ type: "window", includeUncontrolled: true }).then((clients) => {
|
|
77
|
+
clients.forEach((client) => {
|
|
78
|
+
client.postMessage({
|
|
79
|
+
type: "chat-widget:push-data",
|
|
80
|
+
data: { threadId, messageId, senderName, preview, timestamp, deepLink }
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
})
|
|
84
|
+
);
|
|
85
|
+
const notificationData = {
|
|
86
|
+
threadId,
|
|
87
|
+
messageId,
|
|
88
|
+
timestamp,
|
|
89
|
+
deepLink,
|
|
90
|
+
type: notificationType
|
|
91
|
+
};
|
|
92
|
+
const defaultIcon = "/chat-icon-192.png";
|
|
93
|
+
const defaultBadge = "/chat-badge-72.png";
|
|
94
|
+
if (notificationType === "generic") {
|
|
95
|
+
const notificationActions = actions ?? [];
|
|
96
|
+
event.waitUntil(
|
|
97
|
+
sw.registration.showNotification(senderName, {
|
|
98
|
+
body: preview,
|
|
99
|
+
icon: icon ?? defaultIcon,
|
|
100
|
+
badge: badge ?? defaultBadge,
|
|
101
|
+
tag: `generic-${threadId}`,
|
|
102
|
+
data: notificationData,
|
|
103
|
+
actions: notificationActions
|
|
104
|
+
})
|
|
105
|
+
);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const chatActions = actions ?? getDefaultActions(locale);
|
|
109
|
+
event.waitUntil(
|
|
110
|
+
sw.registration.showNotification(senderName, {
|
|
111
|
+
body: preview,
|
|
112
|
+
icon: icon ?? defaultIcon,
|
|
113
|
+
badge: badge ?? defaultBadge,
|
|
114
|
+
tag: `message-${threadId}`,
|
|
115
|
+
renotify: true,
|
|
116
|
+
data: notificationData,
|
|
117
|
+
actions: chatActions
|
|
118
|
+
})
|
|
119
|
+
);
|
|
120
|
+
});
|
|
121
|
+
sw.addEventListener("notificationclick", (event) => {
|
|
122
|
+
event.notification.close();
|
|
123
|
+
if (event.action === "dismiss") return;
|
|
124
|
+
const clickData = event.notification.data;
|
|
125
|
+
const { deepLink, threadId, messageId, timestamp, type } = clickData;
|
|
126
|
+
if (type === "generic") {
|
|
127
|
+
if (deepLink) {
|
|
128
|
+
event.waitUntil(sw.clients.openWindow(deepLink));
|
|
129
|
+
}
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
event.waitUntil(
|
|
133
|
+
sw.clients.matchAll({ type: "window", includeUncontrolled: true }).then((clientList) => {
|
|
134
|
+
const origin = sw.location.origin;
|
|
135
|
+
const sameOrigin = clientList.filter((c) => c.url.startsWith(origin));
|
|
136
|
+
for (const client of deepLink ? clientList : sameOrigin) {
|
|
137
|
+
if (!deepLink || client.url.includes(deepLink) || client.url.includes("chat-widget")) {
|
|
138
|
+
client.postMessage({
|
|
139
|
+
type: "chat-widget:notification-clicked",
|
|
140
|
+
threadId,
|
|
141
|
+
messageId,
|
|
142
|
+
timestamp,
|
|
143
|
+
instanceId: threadId
|
|
144
|
+
});
|
|
145
|
+
return client.focus();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return sw.clients.openWindow(deepLink || `/chat?thread=${threadId}`);
|
|
149
|
+
})
|
|
150
|
+
);
|
|
151
|
+
});
|
|
152
|
+
sw.addEventListener("message", (event) => {
|
|
153
|
+
const data = event.data;
|
|
154
|
+
if (data?.type === "chat-widget:notification-clicked") {
|
|
155
|
+
const source = event.source;
|
|
156
|
+
source?.postMessage({
|
|
157
|
+
type: "chat-widget:sync",
|
|
158
|
+
threadId: data.threadId,
|
|
159
|
+
after: data.timestamp
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
//# sourceMappingURL=chat-service-worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/chat-service-worker.ts"],"sourcesContent":["/// <reference lib=\"webworker\" />\n\ntype PushType = \"chat\" | \"generic\";\n\ninterface PushAction {\n action: string;\n title: string;\n}\n\ninterface PushNotificationData {\n threadId: string;\n messageId: string;\n senderName: string;\n preview: string;\n timestamp: number;\n deepLink?: string;\n type?: PushType;\n actions?: PushAction[];\n locale?: string;\n icon?: string;\n badge?: string;\n}\n\ninterface NotificationClickData {\n deepLink?: string;\n threadId: string;\n messageId: string;\n timestamp: number;\n type: PushType;\n}\n\ninterface SyncMessage {\n type: \"chat-widget:sync\";\n threadId: string;\n after: number;\n}\n\ninterface ClickedMessage {\n type: \"chat-widget:notification-clicked\";\n threadId: string;\n messageId: string;\n timestamp: number;\n instanceId: string;\n}\n\n// ── Locale strings for notification actions ──\n\ninterface NotificationLocaleStrings {\n openChat: string;\n dismiss: string;\n}\n\nconst swLocales: Record<string, NotificationLocaleStrings> = {\n en: { openChat: \"Open Chat\", dismiss: \"Dismiss\" },\n pt: { openChat: \"Abrir chat\", dismiss: \"Dispensar\" },\n es: { openChat: \"Abrir chat\", dismiss: \"Descartar\" },\n da: { openChat: \"Åbn Chat\", dismiss: \"Afvis\" },\n sv: { openChat: \"Öppna Chatt\", dismiss: \"Avfärda\" },\n nb: { openChat: \"Åpne Chat\", dismiss: \"Avvis\" },\n fi: { openChat: \"Avaa Chat\", dismiss: \"Hylkää\" },\n fr: { openChat: \"Ouvrir le Chat\", dismiss: \"Ignorer\" },\n de: { openChat: \"Chat öffnen\", dismiss: \"Schließen\" },\n it: { openChat: \"Apri Chat\", dismiss: \"Ignora\" },\n nl: { openChat: \"Open Chat\", dismiss: \"Sluiten\" },\n pl: { openChat: \"Otwórz Czat\", dismiss: \"Odrzuć\" },\n cs: { openChat: \"Otevřít Chat\", dismiss: \"Zavřít\" },\n ro: { openChat: \"Deschide Chat\", dismiss: \"Ignoră\" },\n hu: { openChat: \"Chat Megnyitása\", dismiss: \"Elutasítás\" },\n uk: { openChat: \"Відкрити Чат\", dismiss: \"Відхилити\" },\n ru: { openChat: \"Открыть Чат\", dismiss: \"Закрыть\" },\n el: { openChat: \"Άνοιγμα Συνομιλίας\", dismiss: \"Απόρριψη\" },\n tr: { openChat: \"Sohbeti Aç\", dismiss: \"Kapat\" },\n et: { openChat: \"Ava Vestlus\", dismiss: \"Loobu\" },\n ja: { openChat: \"チャットを開く\", dismiss: \"閉じる\" },\n \"zh-CN\": { openChat: \"打开聊天\", dismiss: \"关闭\" },\n \"zh-TW\": { openChat: \"開啟聊天\", dismiss: \"關閉\" },\n ko: { openChat: \"채팅 열기\", dismiss: \"닫기\" },\n vi: { openChat: \"Mở Trò chuyện\", dismiss: \"Bỏ qua\" },\n th: { openChat: \"เปิดแชท\", dismiss: \"ปิด\" },\n id: { openChat: \"Buka Obrolan\", dismiss: \"Tutup\" },\n hi: { openChat: \"चैट खोलें\", dismiss: \"खारिज करें\" },\n ar: { openChat: \"فتح الدردشة\", dismiss: \"تجاهل\" },\n};\n\nfunction getFallbackChain(locale: string): string[] {\n const parts = locale.split(\"-\");\n if (parts.length === 1) return [parts[0], \"en\"];\n return [locale, parts[0], \"en\"];\n}\n\nfunction resolveLocale(locale?: string): NotificationLocaleStrings {\n if (!locale) return swLocales.en;\n for (const tag of getFallbackChain(locale)) {\n if (swLocales[tag]) return swLocales[tag];\n }\n return swLocales.en;\n}\n\nfunction getDefaultActions(locale?: string): PushAction[] {\n const strings = resolveLocale(locale);\n return [\n { action: \"open\", title: strings.openChat },\n { action: \"dismiss\", title: strings.dismiss },\n ];\n}\n\n// ── Service Worker ──\n\nconst sw = self as unknown as ServiceWorkerGlobalScope;\n\nsw.addEventListener(\"push\", (event: PushEvent) => {\n if (!event.data) return;\n\n let data: PushNotificationData;\n try {\n data = event.data.json();\n } catch {\n return;\n }\n\n const {\n threadId,\n messageId,\n senderName,\n preview,\n timestamp,\n deepLink,\n type,\n actions,\n locale,\n icon,\n badge,\n } = data;\n\n const notificationType = type ?? \"chat\";\n\n // Forward push data to window clients for PushManager.onMessage to receive\n event.waitUntil(\n sw.clients.matchAll({ type: \"window\", includeUncontrolled: true }).then((clients) => {\n clients.forEach((client) => {\n client.postMessage({\n type: \"chat-widget:push-data\",\n data: { threadId, messageId, senderName, preview, timestamp, deepLink },\n });\n });\n }),\n );\n\n const notificationData: NotificationClickData = {\n threadId,\n messageId,\n timestamp,\n deepLink,\n type: notificationType,\n };\n\n const defaultIcon = \"/chat-icon-192.png\";\n const defaultBadge = \"/chat-badge-72.png\";\n\n if (notificationType === \"generic\") {\n const notificationActions = actions ?? [];\n\n event.waitUntil(\n sw.registration.showNotification(senderName, {\n body: preview,\n icon: icon ?? defaultIcon,\n badge: badge ?? defaultBadge,\n tag: `generic-${threadId}`,\n data: notificationData,\n actions: notificationActions,\n } as NotificationOptions),\n );\n return;\n }\n\n const chatActions = actions ?? getDefaultActions(locale);\n\n event.waitUntil(\n sw.registration.showNotification(senderName, {\n body: preview,\n icon: icon ?? defaultIcon,\n badge: badge ?? defaultBadge,\n tag: `message-${threadId}`,\n renotify: true,\n data: notificationData,\n actions: chatActions,\n } as NotificationOptions),\n );\n});\n\nsw.addEventListener(\"notificationclick\", (event: NotificationEvent) => {\n event.notification.close();\n if (event.action === \"dismiss\") return;\n\n const clickData = event.notification.data as unknown as NotificationClickData;\n const { deepLink, threadId, messageId, timestamp, type } = clickData;\n\n if (type === \"generic\") {\n if (deepLink) {\n event.waitUntil(sw.clients.openWindow(deepLink));\n }\n return;\n }\n\n event.waitUntil(\n sw.clients\n .matchAll({ type: \"window\", includeUncontrolled: true })\n .then((clientList: readonly WindowClient[]) => {\n const origin = sw.location.origin;\n const sameOrigin = clientList.filter((c: WindowClient) => c.url.startsWith(origin));\n\n for (const client of deepLink ? clientList : sameOrigin) {\n if (!deepLink || client.url.includes(deepLink) || client.url.includes(\"chat-widget\")) {\n client.postMessage({\n type: \"chat-widget:notification-clicked\",\n threadId,\n messageId,\n timestamp,\n instanceId: threadId,\n } satisfies ClickedMessage);\n return client.focus();\n }\n }\n\n return sw.clients.openWindow(deepLink || `/chat?thread=${threadId}`);\n }),\n );\n});\n\nsw.addEventListener(\"message\", (event: ExtendableMessageEvent) => {\n const data = event.data as ClickedMessage | null;\n if (data?.type === \"chat-widget:notification-clicked\") {\n const source = event.source as WindowClient | null;\n source?.postMessage({\n type: \"chat-widget:sync\",\n threadId: data.threadId,\n after: data.timestamp,\n } satisfies SyncMessage);\n }\n});\n"],"mappings":"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","names":[]}
|
package/dist/index.cjs
CHANGED
|
@@ -129,6 +129,30 @@ var HttpClient = class {
|
|
|
129
129
|
const url = this.expandTemplate(endpointTemplate, { id: messageId, emoji });
|
|
130
130
|
await this.delete(url);
|
|
131
131
|
}
|
|
132
|
+
async postFormData(url, formData, signal) {
|
|
133
|
+
const controller = new AbortController();
|
|
134
|
+
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
|
135
|
+
try {
|
|
136
|
+
const fullUrl = this.resolve(url);
|
|
137
|
+
const combined = signal ? AbortSignal.any([controller.signal, signal]) : controller.signal;
|
|
138
|
+
const response = await fetch(fullUrl, {
|
|
139
|
+
method: "POST",
|
|
140
|
+
headers: this.config.headers,
|
|
141
|
+
signal: combined,
|
|
142
|
+
body: formData
|
|
143
|
+
});
|
|
144
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
145
|
+
return response.json();
|
|
146
|
+
} finally {
|
|
147
|
+
clearTimeout(timeoutId);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
setHeader(name, value) {
|
|
151
|
+
this.config.headers[name] = value;
|
|
152
|
+
}
|
|
153
|
+
removeHeader(name) {
|
|
154
|
+
delete this.config.headers[name];
|
|
155
|
+
}
|
|
132
156
|
resolve(url) {
|
|
133
157
|
return /^https?:\/\//.test(url) ? url : `${this.config.apiUrl}${url}`;
|
|
134
158
|
}
|
|
@@ -329,6 +353,31 @@ var WebChatClient = class {
|
|
|
329
353
|
this.conversationId = config.conversationId ?? generateConversationId();
|
|
330
354
|
this.currentUserId = config.userId;
|
|
331
355
|
}
|
|
356
|
+
reconfigure(config) {
|
|
357
|
+
if (config.userId) {
|
|
358
|
+
this.currentUserId = config.userId;
|
|
359
|
+
this.httpClient.setHeader("X-User-Id", config.userId);
|
|
360
|
+
}
|
|
361
|
+
if (config.userName) {
|
|
362
|
+
this.config = { ...this.config, userName: config.userName };
|
|
363
|
+
this.httpClient.setHeader("X-User-Name", config.userName);
|
|
364
|
+
}
|
|
365
|
+
if (config.verifyToken) {
|
|
366
|
+
this.config = { ...this.config, verifyToken: config.verifyToken };
|
|
367
|
+
this.httpClient.setHeader("X-Verify-Token", config.verifyToken);
|
|
368
|
+
}
|
|
369
|
+
if (config.conversationId) {
|
|
370
|
+
this.conversationId = config.conversationId;
|
|
371
|
+
}
|
|
372
|
+
if (config.headers) {
|
|
373
|
+
Object.entries(config.headers).forEach(([key, value]) => {
|
|
374
|
+
this.httpClient.setHeader(key, value);
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
setLocaleHeader(locale) {
|
|
379
|
+
this.httpClient.setHeader("X-Locale", locale);
|
|
380
|
+
}
|
|
332
381
|
async connect() {
|
|
333
382
|
if (this.broadcastClient) {
|
|
334
383
|
this.broadcastClient.connect();
|
|
@@ -377,10 +426,14 @@ var WebChatClient = class {
|
|
|
377
426
|
`${endpoint}?${params.toString()}`,
|
|
378
427
|
signal
|
|
379
428
|
);
|
|
380
|
-
const
|
|
429
|
+
const rawMessages = response.messages ?? [];
|
|
430
|
+
const messages = rawMessages.map((msg) => ({
|
|
381
431
|
id: msg.id,
|
|
382
432
|
threadId,
|
|
383
|
-
content: {
|
|
433
|
+
content: {
|
|
434
|
+
text: msg.text,
|
|
435
|
+
cards: msg.card ? [msg.card] : void 0
|
|
436
|
+
},
|
|
384
437
|
author: {
|
|
385
438
|
id: msg.author.id,
|
|
386
439
|
name: msg.author.name,
|
|
@@ -419,7 +472,7 @@ var WebChatClient = class {
|
|
|
419
472
|
timestamp: Date.now(),
|
|
420
473
|
attachments: attachments.length > 0 ? attachments.map((a, i) => ({
|
|
421
474
|
id: `att-${messageId}-${i}`,
|
|
422
|
-
name: a.name
|
|
475
|
+
name: a.name ?? "",
|
|
423
476
|
url: a.url,
|
|
424
477
|
size: a.size,
|
|
425
478
|
mimeType: a.mimeType
|
|
@@ -434,7 +487,7 @@ var WebChatClient = class {
|
|
|
434
487
|
id: messageId,
|
|
435
488
|
role: "user",
|
|
436
489
|
text,
|
|
437
|
-
attachments: attachments
|
|
490
|
+
attachments: attachments.map((a) => ({
|
|
438
491
|
url: a.url,
|
|
439
492
|
name: a.name,
|
|
440
493
|
mime_type: a.mimeType,
|
|
@@ -453,15 +506,15 @@ var WebChatClient = class {
|
|
|
453
506
|
}
|
|
454
507
|
if (response.text && !response.events?.some((e) => e.type === "message.posted")) {
|
|
455
508
|
const assistantMessage = {
|
|
456
|
-
id: response.id
|
|
509
|
+
id: response.id ?? generateId(),
|
|
457
510
|
threadId: this.getThreadId(),
|
|
458
511
|
content: { text: response.text },
|
|
459
512
|
author: { id: "assistant", name: "Assistant", isBot: true },
|
|
460
513
|
timestamp: Date.now(),
|
|
461
514
|
attachments: response.attachments?.map((a, i) => ({
|
|
462
|
-
id: `att-${response.id
|
|
463
|
-
name: a.name
|
|
464
|
-
url: a.url
|
|
515
|
+
id: `att-${response.id ?? "msg"}-${i}`,
|
|
516
|
+
name: a.name ?? "",
|
|
517
|
+
url: a.url ?? "",
|
|
465
518
|
type: a.type,
|
|
466
519
|
mimeType: a.mime_type,
|
|
467
520
|
size: a.size
|
|
@@ -518,11 +571,23 @@ var WebChatClient = class {
|
|
|
518
571
|
onMessagePosted(handler) {
|
|
519
572
|
return this.addEventListener("message.posted", handler);
|
|
520
573
|
}
|
|
574
|
+
onMessageEdited(handler) {
|
|
575
|
+
return this.addEventListener("message:edited", handler);
|
|
576
|
+
}
|
|
577
|
+
onMessageDeleted(handler) {
|
|
578
|
+
return this.addEventListener("message:deleted", handler);
|
|
579
|
+
}
|
|
580
|
+
onReactionAdded(handler) {
|
|
581
|
+
return this.addEventListener("reaction:added", handler);
|
|
582
|
+
}
|
|
583
|
+
onReactionRemoved(handler) {
|
|
584
|
+
return this.addEventListener("reaction:removed", handler);
|
|
585
|
+
}
|
|
521
586
|
onStreamingChunk(handler) {
|
|
522
|
-
return this.addEventListener("streaming
|
|
587
|
+
return this.addEventListener("streaming:chunk", handler);
|
|
523
588
|
}
|
|
524
589
|
onTypingStarted(handler) {
|
|
525
|
-
return this.addEventListener("typing
|
|
590
|
+
return this.addEventListener("typing:started", handler);
|
|
526
591
|
}
|
|
527
592
|
getConversationId() {
|
|
528
593
|
return this.conversationId;
|
|
@@ -567,11 +632,11 @@ var WebChatClient = class {
|
|
|
567
632
|
timestamp: event.timestamp,
|
|
568
633
|
attachments: event.attachments?.map((a) => ({
|
|
569
634
|
id: `att-${event.messageId}-${Math.random().toString(36).slice(2, 8)}`,
|
|
570
|
-
name: a.name
|
|
571
|
-
url: a.url
|
|
635
|
+
name: a.name ?? "",
|
|
636
|
+
url: a.url ?? "",
|
|
572
637
|
type: a.type,
|
|
573
638
|
mimeType: a.mimeType,
|
|
574
|
-
size: a.size
|
|
639
|
+
size: a.size ?? void 0
|
|
575
640
|
}))
|
|
576
641
|
};
|
|
577
642
|
this.messages.push(message);
|
|
@@ -582,17 +647,14 @@ var WebChatClient = class {
|
|
|
582
647
|
const message = this.messages.find((m) => m.id === event.messageId);
|
|
583
648
|
if (message?.content) {
|
|
584
649
|
message.content.text = event.newText;
|
|
585
|
-
this.notifySubscribers("message:edited",
|
|
586
|
-
messageId: event.messageId,
|
|
587
|
-
newText: event.newText
|
|
588
|
-
});
|
|
650
|
+
this.notifySubscribers("message:edited", event);
|
|
589
651
|
}
|
|
590
652
|
}
|
|
591
653
|
handleMessageDeleted(event) {
|
|
592
654
|
const index = this.messages.findIndex((m) => m.id === event.messageId);
|
|
593
655
|
if (index !== -1) {
|
|
594
656
|
this.messages.splice(index, 1);
|
|
595
|
-
this.notifySubscribers("message:deleted",
|
|
657
|
+
this.notifySubscribers("message:deleted", event);
|
|
596
658
|
}
|
|
597
659
|
}
|
|
598
660
|
handleReactionAdded(event) {
|
|
@@ -606,7 +668,7 @@ var WebChatClient = class {
|
|
|
606
668
|
} else {
|
|
607
669
|
message.reactions.push({ emoji: event.emoji, count: 1, users: [event.user.id] });
|
|
608
670
|
}
|
|
609
|
-
this.notifySubscribers("reaction:added",
|
|
671
|
+
this.notifySubscribers("reaction:added", event);
|
|
610
672
|
}
|
|
611
673
|
handleReactionRemoved(event) {
|
|
612
674
|
const message = this.messages.find((m) => m.id === event.messageId);
|
|
@@ -618,13 +680,13 @@ var WebChatClient = class {
|
|
|
618
680
|
reaction.users = reaction.users.filter((id) => id !== event.user.id);
|
|
619
681
|
if (reaction.count === 0) message.reactions.splice(index, 1);
|
|
620
682
|
}
|
|
621
|
-
this.notifySubscribers("reaction:removed",
|
|
683
|
+
this.notifySubscribers("reaction:removed", event);
|
|
622
684
|
}
|
|
623
685
|
handleStreamingChunk(event) {
|
|
624
686
|
const { messageId, chunk, isFinal } = event;
|
|
625
687
|
if (!this.streamingMessages.has(messageId)) {
|
|
626
688
|
this.streamingMessages.set(messageId, { messageId, accumulatedText: "", isComplete: false });
|
|
627
|
-
this.notifySubscribers("streaming:started",
|
|
689
|
+
this.notifySubscribers("streaming:started", event);
|
|
628
690
|
}
|
|
629
691
|
const state = this.streamingMessages.get(messageId);
|
|
630
692
|
state.accumulatedText += chunk;
|
|
@@ -644,24 +706,18 @@ var WebChatClient = class {
|
|
|
644
706
|
this.streamingMessages.delete(messageId);
|
|
645
707
|
this.notifySubscribers("streaming:complete", { messageId, text: state.accumulatedText });
|
|
646
708
|
} else {
|
|
647
|
-
this.notifySubscribers("streaming:chunk",
|
|
648
|
-
messageId,
|
|
649
|
-
chunk,
|
|
650
|
-
fullText: state.accumulatedText
|
|
651
|
-
});
|
|
709
|
+
this.notifySubscribers("streaming:chunk", event);
|
|
652
710
|
}
|
|
653
|
-
this.notifySubscribers("streaming.chunk", event);
|
|
654
711
|
}
|
|
655
712
|
handleTypingStarted(event) {
|
|
656
713
|
if (this.pendingTyping) clearTimeout(this.pendingTyping);
|
|
657
|
-
this.notifySubscribers("typing:started",
|
|
714
|
+
this.notifySubscribers("typing:started", event);
|
|
658
715
|
this.pendingTyping = setTimeout(() => {
|
|
659
716
|
this.notifySubscribers("typing:stopped", { userId: event.userId });
|
|
660
717
|
}, 3e3);
|
|
661
|
-
this.notifySubscribers("typing.started", event);
|
|
662
718
|
}
|
|
663
719
|
handleDMRequested(event) {
|
|
664
|
-
this.notifySubscribers("dm.requested",
|
|
720
|
+
this.notifySubscribers("dm.requested", event);
|
|
665
721
|
}
|
|
666
722
|
notifySubscribers(eventType, data) {
|
|
667
723
|
this.subscribers.get(eventType)?.forEach((handler) => handler(data));
|
|
@@ -939,9 +995,19 @@ var PushManager = class _PushManager {
|
|
|
939
995
|
try {
|
|
940
996
|
this.registration = await navigator.serviceWorker.register(
|
|
941
997
|
this.config.serviceWorkerUrl || "/chat-service-worker.js",
|
|
942
|
-
{
|
|
998
|
+
{
|
|
999
|
+
scope: this.config.serviceWorkerScope || "/",
|
|
1000
|
+
type: this.config.serviceWorkerType
|
|
1001
|
+
}
|
|
943
1002
|
);
|
|
944
1003
|
await navigator.serviceWorker.ready;
|
|
1004
|
+
navigator.serviceWorker.addEventListener("message", (event) => {
|
|
1005
|
+
const msg = event.data;
|
|
1006
|
+
if (msg?.type === "chat-widget:push-data") {
|
|
1007
|
+
const pushData = msg.data;
|
|
1008
|
+
this.messageListeners.forEach((listener) => listener(pushData));
|
|
1009
|
+
}
|
|
1010
|
+
});
|
|
945
1011
|
const subscription = await this.registration.pushManager.getSubscription();
|
|
946
1012
|
this.setStatus(subscription ? "subscribed" : "default");
|
|
947
1013
|
} catch {
|