magicbell-js 1.0.0 → 1.1.1

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.
Files changed (114) hide show
  1. package/README.md +4 -0
  2. package/dist/commonjs/project-client/services/broadcasts/models/broadcast-collection.d.ts +265 -265
  3. package/dist/commonjs/project-client/services/broadcasts/models/broadcast.d.ts +168 -168
  4. package/dist/commonjs/project-client/services/broadcasts/models/email.d.ts +3 -3
  5. package/dist/commonjs/project-client/services/broadcasts/models/in-app.d.ts +3 -3
  6. package/dist/commonjs/project-client/services/broadcasts/models/mobile-push.d.ts +3 -3
  7. package/dist/commonjs/project-client/services/broadcasts/models/overrides-channels.d.ts +27 -27
  8. package/dist/commonjs/project-client/services/broadcasts/models/overrides.d.ts +39 -39
  9. package/dist/commonjs/project-client/services/broadcasts/models/sms.d.ts +3 -3
  10. package/dist/commonjs/project-client/services/channels/models/apns-token-collection.d.ts +14 -14
  11. package/dist/commonjs/project-client/services/channels/models/apns-token.d.ts +8 -8
  12. package/dist/commonjs/project-client/services/channels/models/discard-result.d.ts +8 -8
  13. package/dist/commonjs/project-client/services/channels/models/expo-token-collection.d.ts +14 -14
  14. package/dist/commonjs/project-client/services/channels/models/expo-token.d.ts +8 -8
  15. package/dist/commonjs/project-client/services/channels/models/fcm-token-collection.d.ts +14 -14
  16. package/dist/commonjs/project-client/services/channels/models/fcm-token.d.ts +8 -8
  17. package/dist/commonjs/project-client/services/channels/models/inbox-token-response-collection.d.ts +28 -28
  18. package/dist/commonjs/project-client/services/channels/models/inbox-token-response.d.ts +16 -16
  19. package/dist/commonjs/project-client/services/channels/models/slack-token-collection.d.ts +14 -14
  20. package/dist/commonjs/project-client/services/channels/models/slack-token.d.ts +8 -8
  21. package/dist/commonjs/project-client/services/channels/models/teams-token-collection.d.ts +14 -14
  22. package/dist/commonjs/project-client/services/channels/models/teams-token.d.ts +8 -8
  23. package/dist/commonjs/project-client/services/channels/models/web-push-token-collection.d.ts +14 -14
  24. package/dist/commonjs/project-client/services/channels/models/web-push-token.d.ts +8 -8
  25. package/dist/commonjs/project-client/services/common/user.d.ts +6 -6
  26. package/dist/commonjs/project-client/services/integrations/models/inbox-config-collection.d.ts +182 -182
  27. package/dist/commonjs/project-client/services/integrations/models/inbox-config-payload.d.ts +98 -98
  28. package/dist/commonjs/project-client/services/integrations/models/inbox-config.d.ts +140 -140
  29. package/dist/commonjs/project-client/services/integrations/models/theme.d.ts +56 -56
  30. package/dist/commonjs/project-client/services/users/models/user-collection.d.ts +10 -10
  31. package/dist/commonjs/socket.d.ts +11 -0
  32. package/dist/commonjs/socket.d.ts.map +1 -0
  33. package/dist/commonjs/socket.js +200 -0
  34. package/dist/commonjs/socket.js.map +1 -0
  35. package/dist/commonjs/user-client/services/channels/models/apns-token-collection.d.ts +14 -14
  36. package/dist/commonjs/user-client/services/channels/models/apns-token.d.ts +8 -8
  37. package/dist/commonjs/user-client/services/channels/models/discard-result.d.ts +8 -8
  38. package/dist/commonjs/user-client/services/channels/models/expo-token-collection.d.ts +14 -14
  39. package/dist/commonjs/user-client/services/channels/models/expo-token.d.ts +8 -8
  40. package/dist/commonjs/user-client/services/channels/models/fcm-token-collection.d.ts +14 -14
  41. package/dist/commonjs/user-client/services/channels/models/fcm-token.d.ts +8 -8
  42. package/dist/commonjs/user-client/services/channels/models/inbox-token-response-collection.d.ts +28 -28
  43. package/dist/commonjs/user-client/services/channels/models/inbox-token-response.d.ts +16 -16
  44. package/dist/commonjs/user-client/services/channels/models/slack-token-collection.d.ts +14 -14
  45. package/dist/commonjs/user-client/services/channels/models/slack-token.d.ts +8 -8
  46. package/dist/commonjs/user-client/services/channels/models/teams-token-collection.d.ts +14 -14
  47. package/dist/commonjs/user-client/services/channels/models/teams-token.d.ts +8 -8
  48. package/dist/commonjs/user-client/services/channels/models/web-push-token-collection.d.ts +14 -14
  49. package/dist/commonjs/user-client/services/channels/models/web-push-token.d.ts +8 -8
  50. package/dist/commonjs/user-client/services/integrations/models/authed-user.d.ts +8 -8
  51. package/dist/commonjs/user-client/services/integrations/models/inbox-config-payload.d.ts +98 -98
  52. package/dist/commonjs/user-client/services/integrations/models/slack-installation.d.ts +57 -57
  53. package/dist/commonjs/user-client/services/integrations/models/slack-start-install.d.ts +8 -8
  54. package/dist/commonjs/user-client/services/integrations/models/theme.d.ts +56 -56
  55. package/dist/commonjs/user-client/services/notifications/models/notification-collection.d.ts +56 -56
  56. package/dist/commonjs/user-client/services/notifications/models/notification.d.ts +32 -32
  57. package/dist/esm/project-client/services/broadcasts/models/broadcast-collection.d.ts +265 -265
  58. package/dist/esm/project-client/services/broadcasts/models/broadcast.d.ts +168 -168
  59. package/dist/esm/project-client/services/broadcasts/models/email.d.ts +3 -3
  60. package/dist/esm/project-client/services/broadcasts/models/in-app.d.ts +3 -3
  61. package/dist/esm/project-client/services/broadcasts/models/mobile-push.d.ts +3 -3
  62. package/dist/esm/project-client/services/broadcasts/models/overrides-channels.d.ts +27 -27
  63. package/dist/esm/project-client/services/broadcasts/models/overrides.d.ts +39 -39
  64. package/dist/esm/project-client/services/broadcasts/models/sms.d.ts +3 -3
  65. package/dist/esm/project-client/services/channels/models/apns-token-collection.d.ts +14 -14
  66. package/dist/esm/project-client/services/channels/models/apns-token.d.ts +8 -8
  67. package/dist/esm/project-client/services/channels/models/discard-result.d.ts +8 -8
  68. package/dist/esm/project-client/services/channels/models/expo-token-collection.d.ts +14 -14
  69. package/dist/esm/project-client/services/channels/models/expo-token.d.ts +8 -8
  70. package/dist/esm/project-client/services/channels/models/fcm-token-collection.d.ts +14 -14
  71. package/dist/esm/project-client/services/channels/models/fcm-token.d.ts +8 -8
  72. package/dist/esm/project-client/services/channels/models/inbox-token-response-collection.d.ts +28 -28
  73. package/dist/esm/project-client/services/channels/models/inbox-token-response.d.ts +16 -16
  74. package/dist/esm/project-client/services/channels/models/slack-token-collection.d.ts +14 -14
  75. package/dist/esm/project-client/services/channels/models/slack-token.d.ts +8 -8
  76. package/dist/esm/project-client/services/channels/models/teams-token-collection.d.ts +14 -14
  77. package/dist/esm/project-client/services/channels/models/teams-token.d.ts +8 -8
  78. package/dist/esm/project-client/services/channels/models/web-push-token-collection.d.ts +14 -14
  79. package/dist/esm/project-client/services/channels/models/web-push-token.d.ts +8 -8
  80. package/dist/esm/project-client/services/common/user.d.ts +6 -6
  81. package/dist/esm/project-client/services/integrations/models/inbox-config-collection.d.ts +182 -182
  82. package/dist/esm/project-client/services/integrations/models/inbox-config-payload.d.ts +98 -98
  83. package/dist/esm/project-client/services/integrations/models/inbox-config.d.ts +140 -140
  84. package/dist/esm/project-client/services/integrations/models/theme.d.ts +56 -56
  85. package/dist/esm/project-client/services/users/models/user-collection.d.ts +10 -10
  86. package/dist/esm/socket.d.ts +11 -0
  87. package/dist/esm/socket.d.ts.map +1 -0
  88. package/dist/esm/socket.js +196 -0
  89. package/dist/esm/socket.js.map +1 -0
  90. package/dist/esm/user-client/services/channels/models/apns-token-collection.d.ts +14 -14
  91. package/dist/esm/user-client/services/channels/models/apns-token.d.ts +8 -8
  92. package/dist/esm/user-client/services/channels/models/discard-result.d.ts +8 -8
  93. package/dist/esm/user-client/services/channels/models/expo-token-collection.d.ts +14 -14
  94. package/dist/esm/user-client/services/channels/models/expo-token.d.ts +8 -8
  95. package/dist/esm/user-client/services/channels/models/fcm-token-collection.d.ts +14 -14
  96. package/dist/esm/user-client/services/channels/models/fcm-token.d.ts +8 -8
  97. package/dist/esm/user-client/services/channels/models/inbox-token-response-collection.d.ts +28 -28
  98. package/dist/esm/user-client/services/channels/models/inbox-token-response.d.ts +16 -16
  99. package/dist/esm/user-client/services/channels/models/slack-token-collection.d.ts +14 -14
  100. package/dist/esm/user-client/services/channels/models/slack-token.d.ts +8 -8
  101. package/dist/esm/user-client/services/channels/models/teams-token-collection.d.ts +14 -14
  102. package/dist/esm/user-client/services/channels/models/teams-token.d.ts +8 -8
  103. package/dist/esm/user-client/services/channels/models/web-push-token-collection.d.ts +14 -14
  104. package/dist/esm/user-client/services/channels/models/web-push-token.d.ts +8 -8
  105. package/dist/esm/user-client/services/integrations/models/authed-user.d.ts +8 -8
  106. package/dist/esm/user-client/services/integrations/models/inbox-config-payload.d.ts +98 -98
  107. package/dist/esm/user-client/services/integrations/models/slack-installation.d.ts +57 -57
  108. package/dist/esm/user-client/services/integrations/models/slack-start-install.d.ts +8 -8
  109. package/dist/esm/user-client/services/integrations/models/theme.d.ts +56 -56
  110. package/dist/esm/user-client/services/notifications/models/notification-collection.d.ts +56 -56
  111. package/dist/esm/user-client/services/notifications/models/notification.d.ts +32 -32
  112. package/package.json +14 -3
  113. package/socket/package.json +4 -0
  114. package/src/socket.ts +232 -0
package/src/socket.ts ADDED
@@ -0,0 +1,232 @@
1
+ import { type Notification, Client } from './user-client.js';
2
+
3
+ export class Socket {
4
+ #client: Client;
5
+ #socketUrl = 'wss://ws.magicbell.com';
6
+ #inboxToken: string | undefined;
7
+ #origin: string | undefined;
8
+ #websocket: WebSocket | undefined;
9
+ #isConnected = false;
10
+ #reconnectAttempts = 0;
11
+ #maxReconnectAttempts = 5;
12
+ #reconnectInterval = 1000;
13
+ #notificationHandler: ((notification: Notification) => void) | undefined;
14
+
15
+ constructor(options: { token: string } | Client) {
16
+ if (options instanceof Client) {
17
+ this.#client = options;
18
+ } else {
19
+ this.#client = new Client({ token: options.token });
20
+ }
21
+ }
22
+
23
+ async listen(onNotification: (notification: Notification) => void) {
24
+ this.#notificationHandler = onNotification;
25
+
26
+ if (this.#websocket && this.#isConnected) {
27
+ console.warn('Already connected to WebSocket');
28
+ return;
29
+ }
30
+
31
+ try {
32
+ const url = await this.#getUrl();
33
+ this.#websocket = new WebSocket(url);
34
+
35
+ this.#websocket.onopen = () => {
36
+ this.#isConnected = true;
37
+ this.#reconnectAttempts = 0;
38
+ this.#reconnectInterval = 1000;
39
+ };
40
+
41
+ this.#websocket.onmessage = (event) => {
42
+ if (event.origin !== this.#origin) return;
43
+
44
+ try {
45
+ const data = JSON.parse(event.data);
46
+ this.#handleMessage(data);
47
+ } catch (error) {
48
+ console.error('Failed to parse WebSocket message:', error);
49
+ }
50
+ };
51
+
52
+ this.#websocket.onclose = (event) => {
53
+ console.warn('WebSocket disconnected:', event.code, event.reason);
54
+ this.#isConnected = false;
55
+ this.#handleReconnect();
56
+ };
57
+
58
+ this.#websocket.onerror = (error) => {
59
+ console.error('WebSocket error:', error);
60
+ this.#isConnected = false;
61
+ };
62
+ } catch (error) {
63
+ console.error('Failed to connect to WebSocket:', error);
64
+ this.#handleReconnect();
65
+ }
66
+ }
67
+
68
+ disconnect() {
69
+ if (this.#websocket) {
70
+ this.#websocket.close();
71
+ this.#websocket = undefined;
72
+ }
73
+ this.#isConnected = false;
74
+ this.#reconnectAttempts = 0;
75
+ }
76
+
77
+ isListening() {
78
+ return this.#isConnected;
79
+ }
80
+
81
+ #handleMessage(data: any) {
82
+ if (!this.#isNewNotificationMessage(data)) {
83
+ return;
84
+ }
85
+
86
+ this.#handleNewNotification(data.data.id);
87
+ }
88
+
89
+ #isNewNotificationMessage(data: any): data is { name: 'notifications/new'; data: { id: string } } {
90
+ return (
91
+ typeof data === 'object' &&
92
+ data !== null &&
93
+ data.name === 'notifications/new' &&
94
+ typeof data.data === 'object' &&
95
+ data.data !== null &&
96
+ typeof data.data.id === 'string'
97
+ );
98
+ }
99
+
100
+ async #handleNewNotification(notificationId: string) {
101
+ if (!this.#notificationHandler) {
102
+ console.warn('No notification handler provided');
103
+ return;
104
+ }
105
+
106
+ try {
107
+ const { data: notification, metadata: res } = await this.#client.notifications.fetchNotification(notificationId);
108
+
109
+ if (!isOK(res)) {
110
+ console.error(`Failed to fetch notification ${notificationId}: ${res.status} ${res.statusText}`);
111
+ return;
112
+ }
113
+
114
+ if (notification) {
115
+ this.#notificationHandler(notification);
116
+ }
117
+ } catch (error) {
118
+ console.error(`Error fetching notification ${notificationId}:`, error);
119
+ }
120
+ }
121
+
122
+ #handleReconnect() {
123
+ if (this.#reconnectAttempts >= this.#maxReconnectAttempts) {
124
+ console.error('Max reconnection attempts reached');
125
+ return;
126
+ }
127
+
128
+ if (!this.#notificationHandler) {
129
+ console.warn('No notification handler, skipping reconnect');
130
+ return;
131
+ }
132
+
133
+ setTimeout(() => {
134
+ this.#reconnectAttempts++;
135
+ this.#reconnectInterval = Math.min(this.#reconnectInterval * 2, 30000); // Max 30 seconds
136
+ this.listen(this.#notificationHandler!);
137
+ }, this.#reconnectInterval);
138
+ }
139
+
140
+ async #getUrl() {
141
+ const jwtToken = this.#client.config.token;
142
+ invariant(jwtToken, 'Failed to get token from client');
143
+ const apiKey = getApiKeyFromToken(jwtToken);
144
+ invariant(apiKey, 'Failed to get API key from token');
145
+
146
+ const token = await this.#getToken();
147
+ const url = new URL(this.#socketUrl);
148
+ url.searchParams.set('api_key', apiKey);
149
+ url.searchParams.set('token', token);
150
+
151
+ this.#origin = url.origin;
152
+ return url.toString();
153
+ }
154
+
155
+ async #getToken() {
156
+ if (this.#inboxToken) return this.#inboxToken;
157
+
158
+ const { data, metadata: res } = await this.#client.channels.saveInboxToken({
159
+ token: getSessionId(),
160
+ });
161
+
162
+ invariant(isOK(res), `Failed to save Inbox token: ${res.status} ${res.statusText}`);
163
+ invariant(data?.token, 'Unexpected server response, missing token');
164
+
165
+ this.#inboxToken = data.token;
166
+ return data.token;
167
+ }
168
+ }
169
+
170
+ function isOK(response: { status: number }) {
171
+ return response.status >= 200 && response.status < 300;
172
+ }
173
+
174
+ function invariant(condition: any, message: string): asserts condition {
175
+ if (!condition) {
176
+ throw new Error(message);
177
+ }
178
+ }
179
+
180
+ function getSessionId() {
181
+ if (typeof sessionStorage === 'undefined') {
182
+ return generateID(64);
183
+ }
184
+
185
+ // sessionStorage gets cleared when the page session ends. A page
186
+ // session lasts for as long as the browser is open and survives
187
+ // over page reloads and restores. Opening a page in a new tab or
188
+ // window will cause a new session to be initiated. This gives us
189
+ // a stable ID per tab, and different ID's across tabs.
190
+ const stored = sessionStorage.getItem('magicbell--realtime-token');
191
+ if (stored) return stored;
192
+
193
+ const id = generateID(64);
194
+ sessionStorage.setItem('magicbell--realtime-token', id);
195
+ return id;
196
+ }
197
+
198
+ function generateID(length = 17) {
199
+ let id = '';
200
+
201
+ while (id.length < length) {
202
+ id += getRandomValues();
203
+ }
204
+
205
+ return id.substring(0, length);
206
+ }
207
+
208
+ function getApiKeyFromToken(token: string) {
209
+ const data = getTokenPayload(token);
210
+ if (!data) return null;
211
+
212
+ if (data.api_key) {
213
+ return data.api_key;
214
+ }
215
+
216
+ return null;
217
+ }
218
+
219
+ function getTokenPayload(token: string) {
220
+ try {
221
+ const [_, payload] = token.split('.');
222
+ const data = JSON.parse(atob(payload));
223
+ return data || null;
224
+ } catch {
225
+ return null;
226
+ }
227
+ }
228
+
229
+ const getRandomValues =
230
+ typeof crypto !== 'undefined' && crypto.getRandomValues
231
+ ? () => crypto.getRandomValues(new Uint32Array(1))[0].toString(36)
232
+ : () => Math.random().toString(36).substring(2, 15);