@trycourier/courier-js 1.3.0 → 2.0.0-beta

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 (38) hide show
  1. package/dist/__tests__/brand-client.test.d.ts +1 -0
  2. package/dist/__tests__/courier-client.test.d.ts +1 -0
  3. package/dist/__tests__/inbox-client.test.d.ts +1 -0
  4. package/dist/__tests__/lists-client.test.d.ts +1 -0
  5. package/dist/__tests__/preferences-client.test.d.ts +1 -0
  6. package/dist/__tests__/shared-instance.test.d.ts +1 -0
  7. package/dist/__tests__/token-client.test.d.ts +1 -0
  8. package/dist/__tests__/tracking-client.test.d.ts +1 -0
  9. package/dist/__tests__/utils.d.ts +2 -0
  10. package/dist/client/brand-client.d.ts +10 -0
  11. package/dist/client/client.d.ts +5 -0
  12. package/dist/client/courier-client.d.ts +39 -0
  13. package/dist/client/inbox-client.d.ts +61 -0
  14. package/dist/client/list-client.d.ts +17 -0
  15. package/dist/client/preference-client.d.ts +37 -0
  16. package/dist/client/token-client.d.ts +19 -0
  17. package/dist/client/tracking-client.d.ts +24 -0
  18. package/dist/index.d.ts +19 -39
  19. package/dist/index.js +1 -189
  20. package/dist/index.mjs +981 -133
  21. package/dist/jest.setup.d.ts +0 -0
  22. package/dist/shared/authentication-listener.d.ts +9 -0
  23. package/dist/shared/courier.d.ts +68 -0
  24. package/dist/socket/courier-socket.d.ts +24 -0
  25. package/dist/socket/inbox-socket.d.ts +19 -0
  26. package/dist/types/brands.d.ts +36 -0
  27. package/dist/types/courier-api-urls.d.ts +11 -0
  28. package/dist/types/inbox.d.ts +43 -0
  29. package/dist/types/pagination.d.ts +4 -0
  30. package/dist/types/preference.d.ts +37 -0
  31. package/dist/types/token.d.ts +12 -0
  32. package/dist/types/tracking-event.d.ts +1 -0
  33. package/dist/utils/coding.d.ts +2 -0
  34. package/dist/utils/logger.d.ts +10 -0
  35. package/dist/utils/request.d.ts +21 -0
  36. package/dist/utils/uuid.d.ts +3 -0
  37. package/package.json +30 -21
  38. package/README.md +0 -123
package/dist/index.mjs CHANGED
@@ -1,7 +1,660 @@
1
- // package.json
2
- var version = "1.3.0";
3
-
4
- // src/helpers/decode.ts
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+ const _CourierSocket = class _CourierSocket {
5
+ constructor(url, options) {
6
+ // Properties
7
+ __publicField(this, "webSocket", null);
8
+ __publicField(this, "pingInterval", null);
9
+ // Callbacks
10
+ __publicField(this, "onOpen");
11
+ __publicField(this, "onMessageReceived");
12
+ __publicField(this, "onClose");
13
+ __publicField(this, "onError");
14
+ // Properties
15
+ __publicField(this, "url");
16
+ __publicField(this, "options");
17
+ this.url = url;
18
+ this.options = options;
19
+ }
20
+ /**
21
+ * Dynamically checks if the WebSocket is connected
22
+ */
23
+ get isConnected() {
24
+ return this.webSocket !== null;
25
+ }
26
+ async connect() {
27
+ this.disconnect();
28
+ return new Promise((resolve, reject) => {
29
+ try {
30
+ this.webSocket = new WebSocket(this.url);
31
+ this.webSocket.onopen = () => {
32
+ var _a;
33
+ (_a = this.onOpen) == null ? void 0 : _a.call(this);
34
+ resolve();
35
+ };
36
+ this.webSocket.onmessage = (event) => {
37
+ var _a;
38
+ (_a = this.onMessageReceived) == null ? void 0 : _a.call(this, event.data);
39
+ };
40
+ this.webSocket.onclose = (event) => {
41
+ var _a;
42
+ this.webSocket = null;
43
+ (_a = this.onClose) == null ? void 0 : _a.call(this, event.code, event.reason);
44
+ };
45
+ this.webSocket.onerror = (event) => {
46
+ var _a;
47
+ this.webSocket = null;
48
+ const error = new Error("Courier Socket connection failed");
49
+ error.originalEvent = event;
50
+ (_a = this.onError) == null ? void 0 : _a.call(this, error);
51
+ reject(error);
52
+ };
53
+ } catch (error) {
54
+ this.webSocket = null;
55
+ reject(error);
56
+ }
57
+ });
58
+ }
59
+ disconnect() {
60
+ this.stopPing();
61
+ if (this.webSocket) {
62
+ this.webSocket.close(_CourierSocket.NORMAL_CLOSURE_STATUS);
63
+ this.webSocket = null;
64
+ }
65
+ }
66
+ async send(message) {
67
+ if (!this.webSocket) {
68
+ return false;
69
+ }
70
+ const json = JSON.stringify(message);
71
+ return this.webSocket.send(json) !== void 0;
72
+ }
73
+ keepAlive(props) {
74
+ this.stopPing();
75
+ this.pingInterval = setInterval(async () => {
76
+ var _a;
77
+ try {
78
+ await this.send({ action: "keepAlive" });
79
+ } catch (error) {
80
+ (_a = this.options.logger) == null ? void 0 : _a.error("Error occurred on Keep Alive:", error);
81
+ }
82
+ }, (props == null ? void 0 : props.intervalInMillis) ?? 3e5);
83
+ }
84
+ stopPing() {
85
+ if (this.pingInterval) {
86
+ clearInterval(this.pingInterval);
87
+ this.pingInterval = null;
88
+ }
89
+ }
90
+ };
91
+ // Constants
92
+ __publicField(_CourierSocket, "NORMAL_CLOSURE_STATUS", 1e3);
93
+ let CourierSocket = _CourierSocket;
94
+ const getCourierApiUrls = (urls) => ({
95
+ courier: {
96
+ rest: (urls == null ? void 0 : urls.courier.rest) || "https://api.courier.com",
97
+ graphql: (urls == null ? void 0 : urls.courier.graphql) || "https://api.courier.com/client/q"
98
+ },
99
+ inbox: {
100
+ graphql: (urls == null ? void 0 : urls.inbox.graphql) || "https://inbox.courier.com/q",
101
+ webSocket: (urls == null ? void 0 : urls.inbox.webSocket) || "wss://realtime.courier.com"
102
+ }
103
+ });
104
+ class Logger {
105
+ constructor(showLogs) {
106
+ __publicField(this, "PREFIX", "[COURIER]");
107
+ this.showLogs = showLogs;
108
+ }
109
+ warn(message, ...args) {
110
+ if (this.showLogs) {
111
+ console.warn(`${this.PREFIX} ${message}`, ...args);
112
+ }
113
+ }
114
+ log(message, ...args) {
115
+ if (this.showLogs) {
116
+ console.log(`${this.PREFIX} ${message}`, ...args);
117
+ }
118
+ }
119
+ error(message, ...args) {
120
+ if (this.showLogs) {
121
+ console.error(`${this.PREFIX} ${message}`, ...args);
122
+ }
123
+ }
124
+ debug(message, ...args) {
125
+ if (this.showLogs) {
126
+ console.debug(`${this.PREFIX} ${message}`, ...args);
127
+ }
128
+ }
129
+ info(message, ...args) {
130
+ if (this.showLogs) {
131
+ console.info(`${this.PREFIX} ${message}`, ...args);
132
+ }
133
+ }
134
+ }
135
+ class UUID {
136
+ static generate(prefix) {
137
+ const id = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
138
+ return prefix ? prefix + id : id;
139
+ }
140
+ }
141
+ class CourierRequestError extends Error {
142
+ constructor(code, message, type) {
143
+ super(message);
144
+ this.code = code;
145
+ this.type = type;
146
+ this.name = "CourierRequestError";
147
+ }
148
+ }
149
+ function logRequest(logger, uid, type, data) {
150
+ logger.log(`
151
+ 📡 New Courier ${type} Request: ${uid}
152
+ URL: ${data.url}
153
+ ${data.method ? `Method: ${data.method}` : ""}
154
+ ${data.query ? `Query: ${data.query}` : ""}
155
+ ${data.variables ? `Variables: ${JSON.stringify(data.variables, null, 2)}` : ""}
156
+ Headers: ${JSON.stringify(data.headers, null, 2)}
157
+ Body: ${data.body ? JSON.stringify(data.body, null, 2) : "Empty"}
158
+ `);
159
+ }
160
+ function logResponse(logger, uid, type, data) {
161
+ logger.log(`
162
+ 📡 New Courier ${type} Response: ${uid}
163
+ Status Code: ${data.status}
164
+ Response JSON: ${JSON.stringify(data.response, null, 2)}
165
+ `);
166
+ }
167
+ async function http(props) {
168
+ const validCodes = props.validCodes ?? [200];
169
+ const uid = props.options.showLogs ? UUID.generate() : void 0;
170
+ const request = new Request(props.url, {
171
+ method: props.method,
172
+ headers: {
173
+ "Content-Type": "application/json",
174
+ ...props.headers
175
+ },
176
+ body: props.body ? JSON.stringify(props.body) : void 0
177
+ });
178
+ if (uid) {
179
+ logRequest(props.options.logger, uid, "HTTP", {
180
+ url: request.url,
181
+ method: request.method,
182
+ headers: Object.fromEntries(request.headers.entries()),
183
+ body: props.body
184
+ });
185
+ }
186
+ const response = await fetch(request);
187
+ if (response.status === 204) {
188
+ return;
189
+ }
190
+ let data;
191
+ try {
192
+ data = await response.json();
193
+ } catch (error) {
194
+ if (response.status === 200) {
195
+ return;
196
+ }
197
+ throw new CourierRequestError(
198
+ response.status,
199
+ "Failed to parse response as JSON",
200
+ "PARSE_ERROR"
201
+ );
202
+ }
203
+ if (uid) {
204
+ logResponse(props.options.logger, uid, "HTTP", {
205
+ status: response.status,
206
+ response: data
207
+ });
208
+ }
209
+ if (!validCodes.includes(response.status)) {
210
+ throw new CourierRequestError(
211
+ response.status,
212
+ (data == null ? void 0 : data.message) || "Unknown Error",
213
+ data == null ? void 0 : data.type
214
+ );
215
+ }
216
+ return data;
217
+ }
218
+ async function graphql(props) {
219
+ const uid = props.options.showLogs ? UUID.generate() : void 0;
220
+ if (uid) {
221
+ logRequest(props.options.logger, uid, "GraphQL", {
222
+ url: props.url,
223
+ headers: props.headers,
224
+ query: props.query,
225
+ variables: props.variables
226
+ });
227
+ }
228
+ const response = await fetch(props.url, {
229
+ method: "POST",
230
+ headers: {
231
+ "Content-Type": "application/json",
232
+ ...props.headers
233
+ },
234
+ body: JSON.stringify({
235
+ query: props.query,
236
+ variables: props.variables
237
+ })
238
+ });
239
+ let data;
240
+ try {
241
+ data = await response.json();
242
+ } catch (error) {
243
+ throw new CourierRequestError(
244
+ response.status,
245
+ "Failed to parse response as JSON",
246
+ "PARSE_ERROR"
247
+ );
248
+ }
249
+ if (uid) {
250
+ logResponse(props.options.logger, uid, "GraphQL", {
251
+ status: response.status,
252
+ response: data
253
+ });
254
+ }
255
+ if (!response.ok) {
256
+ throw new CourierRequestError(
257
+ response.status,
258
+ (data == null ? void 0 : data.message) || "Unknown Error",
259
+ data == null ? void 0 : data.type
260
+ );
261
+ }
262
+ return data;
263
+ }
264
+ class Client {
265
+ constructor(options) {
266
+ this.options = options;
267
+ }
268
+ }
269
+ class BrandClient extends Client {
270
+ /**
271
+ * Get a brand by ID using GraphQL
272
+ */
273
+ async getBrand(props) {
274
+ const query = `
275
+ query GetBrand {
276
+ brand(brandId: "${props.brandId}") {
277
+ settings {
278
+ colors {
279
+ primary
280
+ secondary
281
+ tertiary
282
+ }
283
+ inapp {
284
+ borderRadius
285
+ disableCourierFooter
286
+ }
287
+ }
288
+ }
289
+ }
290
+ `;
291
+ const json = await graphql({
292
+ options: this.options,
293
+ url: this.options.urls.courier.graphql,
294
+ headers: {
295
+ "x-courier-user-id": this.options.userId,
296
+ "x-courier-client-key": "empty",
297
+ // Empty for now. Will be removed in future.
298
+ "Authorization": `Bearer ${this.options.accessToken}`
299
+ },
300
+ query,
301
+ variables: { brandId: props.brandId }
302
+ });
303
+ return json.data.brand;
304
+ }
305
+ }
306
+ class InboxSocket extends CourierSocket {
307
+ constructor(options) {
308
+ const url = InboxSocket.buildUrl(options);
309
+ super(url, options);
310
+ __publicField(this, "receivedMessage");
311
+ __publicField(this, "receivedMessageEvent");
312
+ this.onMessageReceived = (data) => this.convertToType(data);
313
+ }
314
+ convertToType(data) {
315
+ var _a, _b, _c, _d;
316
+ try {
317
+ const payload = JSON.parse(data);
318
+ switch (payload.type) {
319
+ case "event":
320
+ const messageEvent = JSON.parse(data);
321
+ (_a = this.receivedMessageEvent) == null ? void 0 : _a.call(this, messageEvent);
322
+ break;
323
+ case "message":
324
+ const message = JSON.parse(data);
325
+ (_b = this.receivedMessage) == null ? void 0 : _b.call(this, message);
326
+ break;
327
+ }
328
+ } catch (error) {
329
+ (_c = this.options.logger) == null ? void 0 : _c.error("Error parsing socket message", error);
330
+ if (error instanceof Error) {
331
+ (_d = this.onError) == null ? void 0 : _d.call(this, error);
332
+ }
333
+ }
334
+ }
335
+ async sendSubscribe(props) {
336
+ var _a;
337
+ const subscription = {
338
+ action: "subscribe",
339
+ data: {
340
+ userAgent: "courier-js",
341
+ // TODO: Equivalent to Courier.agent.value()
342
+ channel: this.options.userId,
343
+ event: "*",
344
+ version: (props == null ? void 0 : props.version) ?? 5
345
+ }
346
+ };
347
+ if (this.options.connectionId) {
348
+ subscription.data.clientSourceId = this.options.connectionId;
349
+ }
350
+ if (this.options.tenantId) {
351
+ subscription.data.accountId = this.options.tenantId;
352
+ }
353
+ (_a = this.options.logger) == null ? void 0 : _a.debug("Sending subscribe request", subscription);
354
+ await this.send(subscription);
355
+ }
356
+ static buildUrl(options) {
357
+ var _a;
358
+ let url = ((_a = options.apiUrls) == null ? void 0 : _a.inbox.webSocket) ?? "";
359
+ if (options.accessToken) {
360
+ url += `/?auth=${options.accessToken}`;
361
+ }
362
+ return url;
363
+ }
364
+ }
365
+ class InboxClient extends Client {
366
+ constructor(options) {
367
+ super(options);
368
+ __publicField(this, "socket");
369
+ this.socket = new InboxSocket(options);
370
+ }
371
+ /**
372
+ * Get paginated messages
373
+ */
374
+ async getMessages(props) {
375
+ const query = `
376
+ query GetInboxMessages(
377
+ $params: FilterParamsInput = { ${this.options.tenantId ? `accountId: "${this.options.tenantId}"` : ""} }
378
+ $limit: Int = ${(props == null ? void 0 : props.paginationLimit) ?? 24}
379
+ $after: String ${(props == null ? void 0 : props.startCursor) ? `= "${props.startCursor}"` : ""}
380
+ ) {
381
+ count(params: $params)
382
+ messages(params: $params, limit: $limit, after: $after) {
383
+ totalCount
384
+ pageInfo {
385
+ startCursor
386
+ hasNextPage
387
+ }
388
+ nodes {
389
+ messageId
390
+ read
391
+ archived
392
+ created
393
+ opened
394
+ title
395
+ preview
396
+ data
397
+ tags
398
+ trackingIds {
399
+ clickTrackingId
400
+ }
401
+ actions {
402
+ content
403
+ data
404
+ href
405
+ }
406
+ }
407
+ }
408
+ }
409
+ `;
410
+ return await graphql({
411
+ options: this.options,
412
+ query,
413
+ headers: {
414
+ "x-courier-user-id": this.options.userId,
415
+ "Authorization": `Bearer ${this.options.accessToken}`
416
+ },
417
+ url: this.options.urls.inbox.graphql
418
+ });
419
+ }
420
+ /**
421
+ * Get paginated archived messages
422
+ */
423
+ async getArchivedMessages(props) {
424
+ const query = `
425
+ query GetInboxMessages(
426
+ $params: FilterParamsInput = { ${this.options.tenantId ? `accountId: "${this.options.tenantId}"` : ""}, archived: true }
427
+ $limit: Int = ${(props == null ? void 0 : props.paginationLimit) ?? 24}
428
+ $after: String ${(props == null ? void 0 : props.startCursor) ? `= "${props.startCursor}"` : ""}
429
+ ) {
430
+ count(params: $params)
431
+ messages(params: $params, limit: $limit, after: $after) {
432
+ totalCount
433
+ pageInfo {
434
+ startCursor
435
+ hasNextPage
436
+ }
437
+ nodes {
438
+ messageId
439
+ read
440
+ archived
441
+ created
442
+ opened
443
+ title
444
+ preview
445
+ data
446
+ tags
447
+ trackingIds {
448
+ clickTrackingId
449
+ }
450
+ actions {
451
+ content
452
+ data
453
+ href
454
+ }
455
+ }
456
+ }
457
+ }
458
+ `;
459
+ return graphql({
460
+ options: this.options,
461
+ query,
462
+ headers: {
463
+ "x-courier-user-id": this.options.userId,
464
+ "Authorization": `Bearer ${this.options.accessToken}`
465
+ },
466
+ url: this.options.urls.inbox.graphql
467
+ });
468
+ }
469
+ /**
470
+ * Get unread message count
471
+ */
472
+ async getUnreadMessageCount() {
473
+ var _a;
474
+ const query = `
475
+ query GetMessages {
476
+ count(params: { status: "unread" ${this.options.tenantId ? `, accountId: "${this.options.tenantId}"` : ""} })
477
+ }
478
+ `;
479
+ const response = await graphql({
480
+ options: this.options,
481
+ query,
482
+ headers: {
483
+ "x-courier-user-id": this.options.userId,
484
+ "Authorization": `Bearer ${this.options.accessToken}`
485
+ },
486
+ url: this.options.urls.inbox.graphql
487
+ });
488
+ return ((_a = response.data) == null ? void 0 : _a.count) ?? 0;
489
+ }
490
+ /**
491
+ * Track a click event
492
+ */
493
+ async click(props) {
494
+ const query = `
495
+ mutation TrackEvent {
496
+ clicked(messageId: "${props.messageId}", trackingId: "${props.trackingId}")
497
+ }
498
+ `;
499
+ const headers = {
500
+ "x-courier-user-id": this.options.userId,
501
+ "Authorization": `Bearer ${this.options.accessToken}`
502
+ };
503
+ if (this.options.connectionId) {
504
+ headers["x-courier-client-source-id"] = this.options.connectionId;
505
+ }
506
+ await graphql({
507
+ options: this.options,
508
+ query,
509
+ headers,
510
+ url: this.options.urls.inbox.graphql
511
+ });
512
+ }
513
+ /**
514
+ * Mark a message as read
515
+ */
516
+ async read(props) {
517
+ const query = `
518
+ mutation TrackEvent {
519
+ read(messageId: "${props.messageId}")
520
+ }
521
+ `;
522
+ const headers = {
523
+ "x-courier-user-id": this.options.userId,
524
+ "Authorization": `Bearer ${this.options.accessToken}`
525
+ };
526
+ if (this.options.connectionId) {
527
+ headers["x-courier-client-source-id"] = this.options.connectionId;
528
+ }
529
+ await graphql({
530
+ options: this.options,
531
+ query,
532
+ headers,
533
+ url: this.options.urls.inbox.graphql
534
+ });
535
+ }
536
+ /**
537
+ * Mark a message as unread
538
+ */
539
+ async unread(props) {
540
+ const query = `
541
+ mutation TrackEvent {
542
+ unread(messageId: "${props.messageId}")
543
+ }
544
+ `;
545
+ const headers = {
546
+ "x-courier-user-id": this.options.userId,
547
+ "Authorization": `Bearer ${this.options.accessToken}`
548
+ };
549
+ if (this.options.connectionId) {
550
+ headers["x-courier-client-source-id"] = this.options.connectionId;
551
+ }
552
+ await graphql({
553
+ options: this.options,
554
+ query,
555
+ headers,
556
+ url: this.options.urls.inbox.graphql
557
+ });
558
+ }
559
+ /**
560
+ * Mark all messages as read
561
+ */
562
+ async readAll() {
563
+ const query = `
564
+ mutation TrackEvent {
565
+ markAllRead
566
+ }
567
+ `;
568
+ const headers = {
569
+ "x-courier-user-id": this.options.userId,
570
+ "Authorization": `Bearer ${this.options.accessToken}`
571
+ };
572
+ if (this.options.connectionId) {
573
+ headers["x-courier-client-source-id"] = this.options.connectionId;
574
+ }
575
+ await graphql({
576
+ options: this.options,
577
+ query,
578
+ headers,
579
+ url: this.options.urls.inbox.graphql
580
+ });
581
+ }
582
+ /**
583
+ * Mark a message as opened
584
+ */
585
+ async open(props) {
586
+ const query = `
587
+ mutation TrackEvent {
588
+ opened(messageId: "${props.messageId}")
589
+ }
590
+ `;
591
+ const headers = {
592
+ "x-courier-user-id": this.options.userId,
593
+ "Authorization": `Bearer ${this.options.accessToken}`
594
+ };
595
+ if (this.options.connectionId) {
596
+ headers["x-courier-client-source-id"] = this.options.connectionId;
597
+ }
598
+ await graphql({
599
+ options: this.options,
600
+ query,
601
+ headers,
602
+ url: this.options.urls.inbox.graphql
603
+ });
604
+ }
605
+ /**
606
+ * Archive a message
607
+ */
608
+ async archive(props) {
609
+ const query = `
610
+ mutation TrackEvent {
611
+ archive(messageId: "${props.messageId}")
612
+ }
613
+ `;
614
+ const headers = {
615
+ "x-courier-user-id": this.options.userId,
616
+ "Authorization": `Bearer ${this.options.accessToken}`
617
+ };
618
+ if (this.options.connectionId) {
619
+ headers["x-courier-client-source-id"] = this.options.connectionId;
620
+ }
621
+ await graphql({
622
+ options: this.options,
623
+ query,
624
+ headers,
625
+ url: this.options.urls.inbox.graphql
626
+ });
627
+ }
628
+ }
629
+ class PreferenceTransformer {
630
+ /**
631
+ * Transforms a single API response item to the CourierUserPreferencesTopic type
632
+ * @param item - The API response item
633
+ * @returns A CourierUserPreferencesTopic object
634
+ */
635
+ transformItem(item) {
636
+ return {
637
+ topicId: item.topic_id,
638
+ topicName: item.topic_name,
639
+ sectionId: item.section_id,
640
+ sectionName: item.section_name,
641
+ status: item.status,
642
+ defaultStatus: item.default_status,
643
+ hasCustomRouting: item.has_custom_routing,
644
+ customRouting: item.custom_routing || []
645
+ };
646
+ }
647
+ /**
648
+ * Transforms an array of API response items to CourierUserPreferencesTopic objects
649
+ * @param items - The API response items
650
+ * @returns A generator of CourierUserPreferencesTopic objects
651
+ */
652
+ *transform(items) {
653
+ for (const item of items) {
654
+ yield this.transformItem(item);
655
+ }
656
+ }
657
+ }
5
658
  function decode(clientKey) {
6
659
  const binaryString = atob(clientKey);
7
660
  const bytes = new Uint8Array(binaryString.length);
@@ -17,148 +670,343 @@ function encode(key) {
17
670
  }
18
671
  return btoa(String.fromCharCode(...bytes));
19
672
  }
20
-
21
- // src/helpers/client.ts
22
- async function tryCatch(fn, debug = true) {
23
- var _a, _b;
24
- const response = await fn();
25
- if (!response.ok && debug) {
26
- console.error(
27
- `Error invoking ${response.url}: ${(_a = response.status) != null ? _a : 404} because ${JSON.stringify((_b = await response.json()) == null ? void 0 : _b.message)}`
28
- );
673
+ class PreferenceClient extends Client {
674
+ constructor() {
675
+ super(...arguments);
676
+ __publicField(this, "transformer", new PreferenceTransformer());
29
677
  }
30
- }
31
- var Courier = class {
32
- constructor({
33
- authorization,
34
- baseUrl,
35
- clientKey,
36
- debug = false,
37
- userId,
38
- userSignature
39
- }) {
40
- if (!clientKey) {
41
- throw new Error("Courier client key is required");
42
- }
43
- this.authorization = authorization;
44
- this.baseUrl = `${baseUrl != null ? baseUrl : "https://api.courier.com"}`;
45
- this.clientKey = clientKey;
46
- this.debug = debug;
47
- this.userId = userId;
48
- this.userSignature = userSignature;
49
- }
50
- getHeaders() {
51
- return new Headers({
52
- "Content-Type": "application/json",
53
- "x-courier-client-version": `courier-js@${version}`,
54
- "Access-Control-Allow-Origin": "*",
55
- ...this.authorization && { Authorization: this.authorization },
56
- ...this.userId && { "x-courier-user-id": this.userId },
57
- ...this.userSignature && {
58
- "x-courier-user-signature": this.userSignature
59
- },
60
- ...this.clientKey && { "x-courier-client-key": this.clientKey }
678
+ /**
679
+ * Get all preferences for a user
680
+ * @see https://www.courier.com/docs/reference/user-preferences/list-all-user-preferences
681
+ */
682
+ async getUserPreferences(params) {
683
+ let url = `${this.options.urls.courier.rest}/users/${this.options.userId}/preferences`;
684
+ if (params == null ? void 0 : params.paginationCursor) {
685
+ url += `?cursor=${params.paginationCursor}`;
686
+ }
687
+ const json = await http({
688
+ options: this.options,
689
+ url,
690
+ method: "GET",
691
+ headers: {
692
+ "Authorization": `Bearer ${this.options.accessToken}`
693
+ }
61
694
  });
62
- }
63
- async post(path, body, useClientPath = true) {
64
- const postFn = () => {
65
- return fetch(this.getPathURL(path, useClientPath), {
66
- body: JSON.stringify(body),
67
- headers: this.getHeaders(),
68
- method: "POST"
69
- });
695
+ const data = json;
696
+ return {
697
+ items: [...this.transformer.transform(data.items)],
698
+ paging: data.paging
70
699
  };
71
- await tryCatch(postFn, this.debug);
72
- }
73
- async put(path, body, useClientPath = true) {
74
- const putFn = () => {
75
- return fetch(this.getPathURL(path, useClientPath), {
76
- ...body ? { body: JSON.stringify(body) } : {},
77
- headers: this.getHeaders(),
78
- method: "PUT"
79
- });
700
+ }
701
+ /**
702
+ * Get preferences for a specific topic
703
+ * @see https://www.courier.com/docs/reference/user-preferences/get-subscription-topic-preferences
704
+ */
705
+ async getUserPreferenceTopic(params) {
706
+ const json = await http({
707
+ options: this.options,
708
+ url: `${this.options.urls.courier.rest}/users/${this.options.userId}/preferences/${params.topicId}`,
709
+ method: "GET",
710
+ headers: {
711
+ "Authorization": `Bearer ${this.options.accessToken}`
712
+ }
713
+ });
714
+ const res = json;
715
+ return this.transformer.transformItem(res.topic);
716
+ }
717
+ /**
718
+ * Update preferences for a specific topic
719
+ * @see https://www.courier.com/docs/reference/user-preferences/update-subscription-topic-preferences
720
+ */
721
+ async putUserPreferenceTopic(params) {
722
+ const payload = {
723
+ topic: {
724
+ status: params.status,
725
+ has_custom_routing: params.hasCustomRouting,
726
+ custom_routing: params.customRouting
727
+ }
80
728
  };
81
- await tryCatch(putFn, this.debug);
82
- }
83
- async delete(path, useClientPath = true) {
84
- const deleteFn = () => {
85
- return fetch(this.getPathURL(path, useClientPath), {
86
- headers: this.getHeaders(),
87
- method: "DELETE"
88
- });
729
+ await http({
730
+ options: this.options,
731
+ url: `${this.options.urls.courier.rest}/users/${this.options.userId}/preferences/${params.topicId}`,
732
+ method: "PUT",
733
+ headers: {
734
+ "Authorization": `Bearer ${this.options.accessToken}`
735
+ },
736
+ body: payload
737
+ });
738
+ }
739
+ /**
740
+ * Get the notification center URL
741
+ * @param params
742
+ * @returns
743
+ */
744
+ getNotificationCenterUrl(params) {
745
+ const rootTenantId = decode(params.clientKey);
746
+ const url = encode(`${rootTenantId}#${this.options.userId}${this.options.tenantId ? `#${this.options.tenantId}` : ""}#${false}`);
747
+ return `https://view.notificationcenter.app/p/${url}`;
748
+ }
749
+ }
750
+ class TokenClient extends Client {
751
+ /**
752
+ * Store a push notification token for a user
753
+ * @see https://www.courier.com/docs/reference/token-management/put-token
754
+ */
755
+ async putUserToken(props) {
756
+ const payload = {
757
+ provider_key: props.provider,
758
+ ...props.device && {
759
+ device: {
760
+ app_id: props.device.appId,
761
+ ad_id: props.device.adId,
762
+ device_id: props.device.deviceId,
763
+ platform: props.device.platform,
764
+ manufacturer: props.device.manufacturer,
765
+ model: props.device.model
766
+ }
767
+ }
89
768
  };
90
- await tryCatch(deleteFn, this.debug);
769
+ await http({
770
+ options: this.options,
771
+ url: `${this.options.urls.courier.rest}/users/${this.options.userId}/tokens/${props.token}`,
772
+ method: "PUT",
773
+ headers: {
774
+ "Authorization": `Bearer ${this.options.accessToken}`
775
+ },
776
+ body: payload,
777
+ validCodes: [200, 204]
778
+ });
91
779
  }
92
- generatePreferencesUrl(userId, options) {
93
- var _a;
94
- if (!userId) {
95
- throw new Error("User ID is required to generate preferences URL");
96
- }
97
- const id = decode(this.clientKey);
98
- return `https://view.notificationcenter.app/p/${encode(
99
- `${id}#${(_a = options == null ? void 0 : options.brandId) != null ? _a : ""}#${userId}#${false}`
100
- )}`;
780
+ /**
781
+ * Delete a push notification token for a user
782
+ */
783
+ async deleteUserToken(props) {
784
+ await http({
785
+ options: this.options,
786
+ url: `${this.options.urls.courier.rest}/users/${this.options.userId}/tokens/${props.token}`,
787
+ method: "DELETE",
788
+ headers: {
789
+ "Authorization": `Bearer ${this.options.accessToken}`
790
+ },
791
+ validCodes: [200, 204]
792
+ });
101
793
  }
102
- getPathURL(path, useClientPath) {
103
- let pathUrl = this.baseUrl;
104
- if (useClientPath) {
105
- pathUrl = pathUrl.concat("/client");
106
- }
107
- return pathUrl.concat(path);
794
+ }
795
+ class ListClient extends Client {
796
+ /**
797
+ * Subscribe a user to a list
798
+ * @see https://www.courier.com/docs/reference/lists/recipient-subscribe
799
+ */
800
+ async putSubscription(props) {
801
+ return await http({
802
+ url: `${this.options.urls.courier.rest}/lists/${props.listId}/subscriptions/${this.options.userId}`,
803
+ options: this.options,
804
+ method: "PUT",
805
+ headers: {
806
+ Authorization: `Bearer ${this.options.accessToken}`
807
+ }
808
+ });
108
809
  }
109
- };
110
-
111
- // src/index.ts
112
- var client = {
113
- __instance: null,
114
- init(options) {
115
- this.__instance = new Courier(options);
116
- },
117
- get instance() {
118
- if (!this.__instance) {
119
- throw new Error("Courier instance not initialized");
120
- }
121
- return this.__instance;
122
- },
123
- async identify(userId, payload) {
124
- if (!userId) {
125
- throw new Error("userId is required");
126
- }
127
- await this.instance.post(`/identify/${userId}`, {
128
- profile: {
129
- ...payload
810
+ /**
811
+ * Unsubscribe a user from a list
812
+ * @see https://www.courier.com/docs/reference/lists/delete-subscription
813
+ */
814
+ async deleteSubscription(props) {
815
+ return await http({
816
+ url: `${this.options.urls.courier.rest}/lists/${props.listId}/subscriptions/${this.options.userId}`,
817
+ options: this.options,
818
+ method: "DELETE",
819
+ headers: {
820
+ Authorization: `Bearer ${this.options.accessToken}`
130
821
  }
131
822
  });
132
- },
133
- async subscribe(userId, listId) {
134
- if (!userId || !listId) {
135
- throw new Error("userId is required");
823
+ }
824
+ }
825
+ class TrackingClient extends Client {
826
+ /**
827
+ * Post an inbound courier event
828
+ * @see https://www.courier.com/docs/reference/inbound/courier-track-event
829
+ */
830
+ async postInboundCourier(props) {
831
+ return await http({
832
+ url: `${this.options.urls.courier.rest}/inbound/courier`,
833
+ options: this.options,
834
+ method: "POST",
835
+ headers: {
836
+ Authorization: `Bearer ${this.options.accessToken}`
837
+ },
838
+ body: {
839
+ ...props,
840
+ userId: this.options.userId
841
+ },
842
+ validCodes: [200, 202]
843
+ });
844
+ }
845
+ /**
846
+ * Post a tracking URL event
847
+ * These urls are found in messages sent from Courier
848
+ */
849
+ async postTrackingUrl(props) {
850
+ return await http({
851
+ url: props.url,
852
+ options: this.options,
853
+ method: "POST",
854
+ body: {
855
+ event: props.event
856
+ }
857
+ });
858
+ }
859
+ }
860
+ class CourierClient extends Client {
861
+ constructor(props) {
862
+ var _a, _b;
863
+ const showLogs = props.showLogs !== void 0 ? props.showLogs : process.env.NODE_ENV === "development";
864
+ const baseOptions = {
865
+ ...props,
866
+ showLogs,
867
+ apiUrls: props.apiUrls || getCourierApiUrls(),
868
+ accessToken: props.jwt ?? props.publicApiKey
869
+ };
870
+ super({
871
+ ...baseOptions,
872
+ logger: new Logger(baseOptions.showLogs),
873
+ urls: getCourierApiUrls(baseOptions.apiUrls)
874
+ });
875
+ __publicField(this, "tokens");
876
+ __publicField(this, "brands");
877
+ __publicField(this, "preferences");
878
+ __publicField(this, "inbox");
879
+ __publicField(this, "lists");
880
+ __publicField(this, "tracking");
881
+ this.tokens = new TokenClient(this.options);
882
+ this.brands = new BrandClient(this.options);
883
+ this.preferences = new PreferenceClient(this.options);
884
+ this.inbox = new InboxClient(this.options);
885
+ this.lists = new ListClient(this.options);
886
+ this.tracking = new TrackingClient(this.options);
887
+ if (!this.options.jwt && !this.options.publicApiKey) {
888
+ this.options.logger.warn("Courier Client initialized with no authentication method. Please provide a JWT or public API key.");
136
889
  }
137
- await this.instance.put(`/lists/${listId}/subscribe/${userId}`);
138
- },
139
- async track(event, properties) {
140
- if (!event) {
141
- throw new Error("event is required");
142
- }
143
- let indempotentKey = self.crypto.randomUUID();
144
- await this.instance.post(`/inbound/courier`, {
145
- messageId: indempotentKey,
146
- type: "track",
147
- event,
148
- properties: { ...properties }
149
- }, false);
150
- },
151
- async unsubscribe(userId, listId) {
152
- if (!userId || !listId) {
153
- throw new Error("userId is required");
890
+ if (this.options.publicApiKey) {
891
+ (_a = this.options.logger) == null ? void 0 : _a.warn(
892
+ "Courier Warning: Public API Keys are for testing only. Please use JWTs for production.\nYou can generate a JWT with this endpoint: https://www.courier.com/docs/reference/auth/issue-token\nThis endpoint should be called from your backend server, not the SDK."
893
+ );
154
894
  }
155
- this.instance.delete(`/lists/${listId}/unsubscribe/${userId}`);
156
- },
157
- generatePreferencesUrl(userId, options) {
158
- return this.instance.generatePreferencesUrl(userId, options);
895
+ if (this.options.jwt && this.options.publicApiKey) {
896
+ (_b = this.options.logger) == null ? void 0 : _b.warn(
897
+ "Courier Warning: Both a JWT and a Public API Key were provided. The Public API Key will be ignored."
898
+ );
899
+ }
900
+ }
901
+ }
902
+ class AuthenticationListener {
903
+ constructor(callback) {
904
+ __publicField(this, "callback");
905
+ this.callback = callback;
906
+ }
907
+ remove() {
908
+ Courier.shared.removeAuthenticationListener(this);
909
+ }
910
+ }
911
+ const _Courier = class _Courier {
912
+ constructor() {
913
+ /**
914
+ * The unique identifier for the Courier instance
915
+ */
916
+ __publicField(this, "id", UUID.generate());
917
+ /**
918
+ * The Courier client instance
919
+ */
920
+ __publicField(this, "instanceClient");
921
+ /**
922
+ * The pagination limit (min: 1, max: 100)
923
+ */
924
+ __publicField(this, "_paginationLimit", 24);
925
+ /**
926
+ * The authentication listeners
927
+ */
928
+ __publicField(this, "authenticationListeners", []);
929
+ }
930
+ get paginationLimit() {
931
+ return this._paginationLimit;
932
+ }
933
+ set paginationLimit(value) {
934
+ this._paginationLimit = Math.min(Math.max(value, 1), 100);
935
+ }
936
+ /**
937
+ * Get the Courier client instance
938
+ * @returns The Courier client instance or undefined if not signed in
939
+ */
940
+ get client() {
941
+ return this.instanceClient;
942
+ }
943
+ /**
944
+ * Get the shared Courier instance
945
+ * @returns The shared Courier instance
946
+ */
947
+ static get shared() {
948
+ if (!_Courier.instance) {
949
+ _Courier.instance = new _Courier();
950
+ }
951
+ return _Courier.instance;
952
+ }
953
+ /**
954
+ * Sign in to Courier
955
+ * @param options - The options for the Courier client
956
+ */
957
+ signIn(props) {
958
+ const connectionId = props.connectionId ?? UUID.generate();
959
+ this.instanceClient = new CourierClient({ ...props, connectionId });
960
+ this.notifyAuthenticationListeners({ userId: props.userId });
961
+ }
962
+ /**
963
+ * Sign out of Courier
964
+ */
965
+ signOut() {
966
+ this.instanceClient = void 0;
967
+ this.notifyAuthenticationListeners({ userId: void 0 });
968
+ }
969
+ /**
970
+ * Register a callback to be notified of authentication state changes
971
+ * @param callback - Function to be called when authentication state changes
972
+ * @returns AuthenticationListener instance that can be used to remove the listener
973
+ */
974
+ addAuthenticationListener(callback) {
975
+ var _a;
976
+ (_a = this.instanceClient) == null ? void 0 : _a.options.logger.info("Adding authentication listener");
977
+ const listener = new AuthenticationListener(callback);
978
+ this.authenticationListeners.push(listener);
979
+ return listener;
980
+ }
981
+ /**
982
+ * Unregister an authentication state change listener
983
+ * @param listener - The AuthenticationListener instance to remove
984
+ */
985
+ removeAuthenticationListener(listener) {
986
+ var _a;
987
+ (_a = this.instanceClient) == null ? void 0 : _a.options.logger.info("Removing authentication listener");
988
+ this.authenticationListeners = this.authenticationListeners.filter((l) => l !== listener);
989
+ }
990
+ /**
991
+ * Notify all authentication listeners
992
+ * @param props - The props to notify the listeners with
993
+ */
994
+ notifyAuthenticationListeners(props) {
995
+ this.authenticationListeners.forEach((listener) => listener.callback(props));
159
996
  }
160
997
  };
161
- var src_default = client;
998
+ /**
999
+ * The shared Courier instance
1000
+ */
1001
+ __publicField(_Courier, "instance");
1002
+ let Courier = _Courier;
162
1003
  export {
163
- src_default as default
1004
+ BrandClient,
1005
+ Courier,
1006
+ CourierClient,
1007
+ CourierSocket,
1008
+ InboxClient,
1009
+ ListClient,
1010
+ PreferenceClient,
1011
+ TokenClient
164
1012
  };