@trycourier/courier-js 3.0.0 → 3.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.
- package/README.md +106 -9
- package/dist/client/preference-client.d.ts +5 -5
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +157 -76
- package/dist/index.mjs.map +1 -1
- package/dist/types/courier-api-urls.d.ts +4 -0
- package/dist/types/inbox.d.ts +19 -0
- package/dist/types/preference.d.ts +14 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,21 +1,118 @@
|
|
|
1
|
-
|
|
1
|
+
<!-- AUTO-GENERATED-OVERVIEW:START — Do not edit this section. It is synced from mintlify-docs. -->
|
|
2
|
+
# Courier JS
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
`@trycourier/courier-js` is the API client for Courier's browser SDKs. It provides programmatic access to inbox messages, user preferences, brands, and list subscriptions for web browser-based applications.
|
|
4
5
|
|
|
5
|
-
>
|
|
6
|
-
>
|
|
7
|
-
>If you’re looking for full-featured UI components,
|
|
8
|
-
check out [Courier React](../courier-react/) or [Courier UI Web Components](../courier-ui-inbox/).
|
|
6
|
+
> **Looking for UI components?** Check out [Courier React](https://github.com/trycourier/courier-web/tree/main/%40trycourier/courier-react) for React components, or [Courier UI Inbox](https://github.com/trycourier/courier-web/tree/main/%40trycourier/courier-ui-inbox) and [Courier UI Toast](https://github.com/trycourier/courier-web/tree/main/%40trycourier/courier-ui-toast) for framework-agnostic Web Components.
|
|
9
7
|
|
|
10
8
|
## Installation
|
|
11
9
|
|
|
12
|
-
```
|
|
10
|
+
```bash
|
|
13
11
|
npm install @trycourier/courier-js
|
|
14
12
|
```
|
|
15
13
|
|
|
16
|
-
##
|
|
14
|
+
## Quick Start
|
|
17
15
|
|
|
18
|
-
|
|
16
|
+
```ts
|
|
17
|
+
import { CourierClient } from "@trycourier/courier-js";
|
|
18
|
+
|
|
19
|
+
const courierClient = new CourierClient({
|
|
20
|
+
userId: "my-user-id",
|
|
21
|
+
jwt: "eyJ.mock.jwt",
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Fetch inbox messages
|
|
25
|
+
const inboxMessages = await courierClient.inbox.getMessages();
|
|
26
|
+
|
|
27
|
+
// Mark a message as read
|
|
28
|
+
const { messageId } = inboxMessages.data.messages.nodes[0];
|
|
29
|
+
await courierClient.inbox.read({ messageId });
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## EU Endpoints
|
|
33
|
+
|
|
34
|
+
Use the built-in preset helper when your app should talk to Courier's EU endpoints:
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
import { CourierClient, getCourierApiUrlsForRegion } from "@trycourier/courier-js";
|
|
38
|
+
|
|
39
|
+
const courierClient = new CourierClient({
|
|
40
|
+
userId: "my-user-id",
|
|
41
|
+
jwt: "eyJ.mock.jwt",
|
|
42
|
+
apiUrls: getCourierApiUrlsForRegion("eu"),
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### `CourierClient` Options
|
|
47
|
+
|
|
48
|
+
| Option | Type | Required | Description |
|
|
49
|
+
|----------|---------|----------|-------------|
|
|
50
|
+
| userId | string | Yes | The user ID to authenticate and fetch messages for. |
|
|
51
|
+
| jwt | string | Yes | The JWT access token minted for the user. |
|
|
52
|
+
| tenantId | string | No | Scope messages to a specific tenant. |
|
|
53
|
+
| showLogs | boolean | No | Enable debugging console logs. Defaults to `true` in development. |
|
|
54
|
+
|
|
55
|
+
## Authentication
|
|
56
|
+
|
|
57
|
+
The SDK requires a JWT (JSON Web Token) for authentication. **Always generate JWTs on your backend server, never in client-side code.**
|
|
58
|
+
|
|
59
|
+
1. Your client calls your backend to request a token.
|
|
60
|
+
2. Your backend calls the [Courier Issue Token endpoint](https://www.courier.com/docs/api-reference/authentication/create-a-jwt) using your API key.
|
|
61
|
+
3. Your backend returns the JWT to your client and passes it to the SDK.
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
curl --request POST \
|
|
65
|
+
--url https://api.courier.com/auth/issue-token \
|
|
66
|
+
--header 'Authorization: Bearer $YOUR_API_KEY' \
|
|
67
|
+
--header 'Content-Type: application/json' \
|
|
68
|
+
--data '{
|
|
69
|
+
"scope": "user_id:$YOUR_USER_ID inbox:read:messages inbox:write:events read:preferences write:preferences read:brands",
|
|
70
|
+
"expires_in": "1 day"
|
|
71
|
+
}'
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## API Overview
|
|
75
|
+
|
|
76
|
+
**Inbox** — read and manage inbox messages:
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
await courierClient.inbox.getMessages();
|
|
80
|
+
await courierClient.inbox.getArchivedMessages();
|
|
81
|
+
await courierClient.inbox.read({ messageId });
|
|
82
|
+
await courierClient.inbox.unread({ messageId });
|
|
83
|
+
await courierClient.inbox.archive({ messageId });
|
|
84
|
+
await courierClient.inbox.readAll();
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Preferences** — read and update user notification preferences:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
await courierClient.preferences.getUserPreferences();
|
|
91
|
+
await courierClient.preferences.putUserPreferenceTopic({
|
|
92
|
+
topicId: "HVS...",
|
|
93
|
+
status: "OPTED_IN",
|
|
94
|
+
hasCustomRouting: true,
|
|
95
|
+
customRouting: ["inbox", "push"],
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Brands** — read custom brand settings:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
await courierClient.brands.getBrand({ brandId: "YF1..." });
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Lists** — subscribe and unsubscribe users:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
await courierClient.lists.putSubscription({ listId: "your_list_id" });
|
|
109
|
+
await courierClient.lists.deleteSubscription({ listId: "your_list_id" });
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Documentation
|
|
113
|
+
|
|
114
|
+
Full documentation: **[courier.com/docs/sdk-libraries/courier-js-web](https://www.courier.com/docs/sdk-libraries/courier-js-web/)**
|
|
115
|
+
<!-- AUTO-GENERATED-OVERVIEW:END -->
|
|
19
116
|
|
|
20
117
|
## Share feedback with Courier
|
|
21
118
|
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { CourierUserPreferences, CourierUserPreferencesChannel, CourierUserPreferencesStatus, CourierUserPreferencesTopic } from '../types/preference';
|
|
2
2
|
import { Client } from './client';
|
|
3
3
|
export declare class PreferenceClient extends Client {
|
|
4
|
-
private transformer;
|
|
5
4
|
/**
|
|
6
5
|
* Get all preferences for a user
|
|
7
|
-
* @param paginationCursor - Optional cursor for pagination
|
|
6
|
+
* @param paginationCursor - Optional cursor for pagination (not used in GraphQL implementation)
|
|
8
7
|
* @returns Promise resolving to user preferences
|
|
9
|
-
* @see https://www.courier.com/docs/api-reference/user-preferences/get-user-preferences
|
|
10
8
|
*/
|
|
11
9
|
getUserPreferences(props?: {
|
|
12
10
|
paginationCursor?: string;
|
|
@@ -15,7 +13,6 @@ export declare class PreferenceClient extends Client {
|
|
|
15
13
|
* Get preferences for a specific topic
|
|
16
14
|
* @param topicId - The ID of the topic to get preferences for
|
|
17
15
|
* @returns Promise resolving to topic preferences
|
|
18
|
-
* @see https://www.courier.com/docs/api-reference/user-preferences/get-user-subscription-topic
|
|
19
16
|
*/
|
|
20
17
|
getUserPreferenceTopic(props: {
|
|
21
18
|
topicId: string;
|
|
@@ -27,7 +24,6 @@ export declare class PreferenceClient extends Client {
|
|
|
27
24
|
* @param hasCustomRouting - Whether the topic has custom routing
|
|
28
25
|
* @param customRouting - The custom routing channels for the topic
|
|
29
26
|
* @returns Promise resolving when update is complete
|
|
30
|
-
* @see https://www.courier.com/docs/api-reference/user-preferences/update-or-create-user-preferences-for-subscription-topic
|
|
31
27
|
*/
|
|
32
28
|
putUserPreferenceTopic(props: {
|
|
33
29
|
topicId: string;
|
|
@@ -43,4 +39,8 @@ export declare class PreferenceClient extends Client {
|
|
|
43
39
|
getNotificationCenterUrl(props: {
|
|
44
40
|
clientKey: string;
|
|
45
41
|
}): string;
|
|
42
|
+
/**
|
|
43
|
+
* Transform a GraphQL RecipientPreference node to CourierUserPreferencesTopic
|
|
44
|
+
*/
|
|
45
|
+
private transformToTopic;
|
|
46
46
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CourierApiUrls } from './types/courier-api-urls';
|
|
1
|
+
import { CourierApiRegion, CourierApiUrls, DEFAULT_COURIER_API_URLS, EU_COURIER_API_URLS, getCourierApiUrls, getCourierApiUrlsForRegion } from './types/courier-api-urls';
|
|
2
2
|
import { CourierBrand, CourierBrandSettings, CourierBrandColors, CourierBrandEmail, CourierBrandInApp } from './types/brands';
|
|
3
3
|
import { CourierUserPreferences, CourierUserPreferencesStatus, CourierUserPreferencesChannel, CourierUserPreferencesPaging, CourierUserPreferencesTopic, CourierUserPreferencesTopicResponse } from './types/preference';
|
|
4
4
|
import { CourierDevice, CourierToken } from './types/token';
|
|
@@ -14,8 +14,8 @@ import { ListClient } from './client/list-client';
|
|
|
14
14
|
import { TrackingClient } from './client/tracking-client';
|
|
15
15
|
import { AuthenticationListener } from './shared/authentication-listener';
|
|
16
16
|
import { Courier } from './shared/courier';
|
|
17
|
-
export type { CourierProps, CourierClientOptions, CourierBrand, CourierBrandSettings, CourierBrandColors, CourierBrandEmail, CourierBrandInApp, CourierApiUrls, CourierUserPreferences, CourierUserPreferencesStatus, CourierUserPreferencesChannel, CourierUserPreferencesPaging, CourierUserPreferencesTopic, CourierUserPreferencesTopicResponse, CourierDevice, CourierToken, CourierGetInboxMessageResponse, CourierGetInboxMessagesResponse, InboxMessage, InboxAction, InboxMessageEventEnvelope, CourierGetInboxMessagesQueryFilter, CourierTrackingEvent, };
|
|
18
|
-
export { InboxMessageEvent, };
|
|
17
|
+
export type { CourierProps, CourierClientOptions, CourierBrand, CourierBrandSettings, CourierBrandColors, CourierBrandEmail, CourierBrandInApp, CourierApiRegion, CourierApiUrls, CourierUserPreferences, CourierUserPreferencesStatus, CourierUserPreferencesChannel, CourierUserPreferencesPaging, CourierUserPreferencesTopic, CourierUserPreferencesTopicResponse, CourierDevice, CourierToken, CourierGetInboxMessageResponse, CourierGetInboxMessagesResponse, InboxMessage, InboxAction, InboxMessageEventEnvelope, CourierGetInboxMessagesQueryFilter, CourierTrackingEvent, };
|
|
18
|
+
export { DEFAULT_COURIER_API_URLS, EU_COURIER_API_URLS, getCourierApiUrls, getCourierApiUrlsForRegion, InboxMessageEvent, };
|
|
19
19
|
export { CourierClient, BrandClient, TokenClient, PreferenceClient, InboxClient, ListClient, TrackingClient, };
|
|
20
20
|
export type { AuthenticationListener };
|
|
21
21
|
export { Courier };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).CourierJS={})}(this,(function(t){"use strict";var e=Object.defineProperty,s=(t,s,n)=>((t,s,n)=>s in t?e(t,s,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[s]=n)(t,"symbol"!=typeof s?s+"":s,n),n=(t=>(t.Subscribe="subscribe",t.Unsubscribe="unsubscribe",t.Pong="pong",t.Ping="ping",t.GetConfig="get-config",t))(n||{}),i=(t=>(t.Ping="ping",t))(i||{}),o=(t=>(t.NewMessage="message",t.Archive="archive",t.ArchiveAll="archive-all",t.ArchiveRead="archive-read",t.Clicked="clicked",t.MarkAllRead="mark-all-read",t.Opened="opened",t.Read="read",t.Unarchive="unarchive",t.Unopened="unopened",t.Unread="unread",t))(o||{});const r=t=>({courier:{rest:(null==t?void 0:t.courier.rest)||"https://api.courier.com",graphql:(null==t?void 0:t.courier.graphql)||"https://api.courier.com/client/q"},inbox:{graphql:(null==t?void 0:t.inbox.graphql)||"https://inbox.courier.com/q",webSocket:(null==t?void 0:t.inbox.webSocket)||"wss://realtime.courier.io"}});class a{constructor(t){s(this,"PREFIX","[COURIER]"),this.showLogs=t}warn(t,...e){this.showLogs&&console.warn(`${this.PREFIX} ${t}`,...e)}log(t,...e){this.showLogs&&console.log(`${this.PREFIX} ${t}`,...e)}error(t,...e){this.showLogs&&console.error(`${this.PREFIX} ${t}`,...e)}debug(t,...e){this.showLogs&&console.debug(`${this.PREFIX} ${t}`,...e)}info(t,...e){this.showLogs&&console.info(`${this.PREFIX} ${t}`,...e)}}const c=class t{static nanoid(e=21){let s="",n=crypto.getRandomValues(new Uint8Array(e|=0));for(;e--;)s+=t.ALPHABET[63&n[e]];return s}};s(c,"ALPHABET","useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict");let u=c;const h="x-courier-ua",d="sdk",l="sdkv",p="cid";class g extends Error{constructor(t,e,s){super(e),this.code=t,this.type=s,this.name="CourierRequestError"}}function m(t,e,s,n){t.log(`\n📡 New Courier ${s} Request: ${e}\nURL: ${n.url}\n${n.method?`Method: ${n.method}`:""}\n${n.query?`Query: ${n.query}`:""}\n${n.variables?`Variables: ${JSON.stringify(n.variables,null,2)}`:""}\nHeaders: ${JSON.stringify(n.headers,null,2)}\nBody: ${n.body?JSON.stringify(n.body,null,2):"Empty"}\n `)}function I(t,e,s,n){t.log(`\n📡 New Courier ${s} Response: ${e}\nStatus Code: ${n.status}\nResponse JSON: ${JSON.stringify(n.response,null,2)}\n `)}async function v(t){const e=t.validCodes??[200],s=t.options.showLogs?u.nanoid():void 0,n=t.options.courierUserAgent.toHttpHeaderValue(),i=new Request(t.url,{method:t.method,headers:{"Content-Type":"application/json",[h]:n,...t.headers},body:t.body?JSON.stringify(t.body):void 0});s&&m(t.options.logger,s,"HTTP",{url:i.url,method:i.method,headers:Object.fromEntries(i.headers.entries()),body:t.body});const o=await fetch(i);if(204===o.status)return;let r;try{r=await o.json()}catch(a){if(200===o.status)return;throw new g(o.status,"Failed to parse response as JSON","PARSE_ERROR")}if(s&&I(t.options.logger,s,"HTTP",{status:o.status,response:r}),!e.includes(o.status))throw new g(o.status,(null==r?void 0:r.message)||"Unknown Error",null==r?void 0:r.type);return r}async function y(t){const e=t.options.showLogs?u.nanoid():void 0,s=t.options.courierUserAgent.toHttpHeaderValue();e&&m(t.options.logger,e,"GraphQL",{url:t.url,headers:t.headers,query:t.query,variables:t.variables});const n=await fetch(t.url,{method:"POST",headers:{"Content-Type":"application/json",[h]:s,...t.headers},body:JSON.stringify({query:t.query,variables:t.variables})});let i;try{i=await n.json()}catch(o){throw new g(n.status,"Failed to parse response as JSON","PARSE_ERROR")}if(e&&I(t.options.logger,e,"GraphQL",{status:n.status,response:i}),!n.ok)throw new g(n.status,(null==i?void 0:i.message)||"Unknown Error",null==i?void 0:i.type);return i}class f{constructor(t){this.options=t}}class b extends f{async getBrand(t){const e=`\n query GetBrand {\n brand(brandId: "${t.brandId}") {\n settings {\n colors {\n primary\n secondary\n tertiary\n }\n inapp {\n borderRadius\n disableCourierFooter\n }\n }\n }\n }\n `;return(await y({options:this.options,url:this.options.apiUrls.courier.graphql,headers:{"x-courier-user-id":this.options.userId,"x-courier-client-key":"empty",Authorization:`Bearer ${this.options.accessToken}`},query:e,variables:{brandId:t.brandId}})).data.brand}}const T=1e3,w=class t{constructor(t){s(this,"webSocket",null),s(this,"retryAttempt",0),s(this,"retryTimeoutId",null),s(this,"closeRequested",!1),s(this,"url"),s(this,"options"),this.url=t.apiUrls.inbox.webSocket,this.options=t}async connect(){var e,s;return this.isConnecting||this.isOpen?(null==(s=this.options.logger)||s.info(`Attempted to open a WebSocket connection, but one already exists in state '${null==(e=this.webSocket)?void 0:e.readyState}'.`),Promise.resolve()):(this.clearRetryTimeout(),this.closeRequested=!1,new Promise(((e,s)=>{this.webSocket=new WebSocket(this.getWebSocketUrl()),this.webSocket.addEventListener("open",(t=>{this.retryAttempt=0,this.onOpen(t),e()})),this.webSocket.addEventListener("message",(async t=>{var e;try{const e=JSON.parse(t.data);if("event"in e&&"reconnect"===e.event)return this.close(T),void(await this.retryConnection(1e3*e.retryAfter));this.onMessageReceived(e)}catch(s){null==(e=this.options.logger)||e.error("Error parsing socket message",s)}})),this.webSocket.addEventListener("close",(e=>{if(e.code!==T&&!this.closeRequested){const s=t.parseCloseEvent(e);s.retryAfterSeconds?this.retryConnection(1e3*s.retryAfterSeconds):this.retryConnection()}this.onClose(e)})),this.webSocket.addEventListener("error",(t=>{this.closeRequested||this.retryConnection(),this.onError(t),s(t)}))})))}close(t=1e3,e){null!==this.webSocket&&(this.closeRequested=!0,this.clearRetryTimeout(),this.retryAttempt=0,this.webSocket.close(t,e))}send(t){var e;if(null===this.webSocket||this.isConnecting)return void(null==(e=this.options.logger)||e.info("Attempted to send a message, but the WebSocket is not yet open."));const s=JSON.stringify(t);this.webSocket.send(s)}get userId(){return this.options.userId}get subTenantId(){return this.options.tenantId}get logger(){return this.options.logger}get courierUserAgent(){return this.options.courierUserAgent}get isConnecting(){return null!==this.webSocket&&this.webSocket.readyState===WebSocket.CONNECTING}get isOpen(){return null!==this.webSocket&&this.webSocket.readyState===WebSocket.OPEN}getWebSocketUrl(){const t=this.options.accessToken,e=this.options.connectionId,s=this.userId,n=this.courierUserAgent.getUserAgentInfo()[d],i=this.courierUserAgent.getUserAgentInfo()[l];return`${this.url}?auth=${t}&cid=${e}&iwpv=v1&userId=${s}&${d}=${n}&${l}=${i}`}static parseCloseEvent(e){if(null===e.reason||""===e.reason)return e;try{const s=JSON.parse(e.reason);if(!s[t.RETRY_AFTER_KEY])return e;const n=parseInt(s[t.RETRY_AFTER_KEY]);return Number.isNaN(n)||n<0?e:{...e,retryAfterSeconds:n}}catch(s){return e}}getBackoffTimeInMillis(){const e=t.BACKOFF_INTERVALS_IN_MILLIS[this.retryAttempt],s=e-e*t.BACKOFF_JITTER_FACTOR,n=e+e*t.BACKOFF_JITTER_FACTOR;return Math.floor(Math.random()*(n-s)+s)}async retryConnection(e){var s,n,i;if(null!==this.retryTimeoutId)return void(null==(s=this.logger)||s.debug("Skipping retry attempt because a previous retry is already scheduled."));if(this.retryAttempt>=t.MAX_RETRY_ATTEMPTS)return void(null==(n=this.logger)||n.error(`Max retry attempts (${t.MAX_RETRY_ATTEMPTS}) reached.`));const o=e??this.getBackoffTimeInMillis();this.retryTimeoutId=window.setTimeout((async()=>{try{await this.connect()}catch(t){}}),o),null==(i=this.logger)||i.debug(`Retrying connection in ${Math.floor(o/1e3)}s. Retry attempt ${this.retryAttempt+1} of ${t.MAX_RETRY_ATTEMPTS}.`),this.retryAttempt++}clearRetryTimeout(){null!==this.retryTimeoutId&&(window.clearTimeout(this.retryTimeoutId),this.retryTimeoutId=null)}};s(w,"BACKOFF_JITTER_FACTOR",.5),s(w,"MAX_RETRY_ATTEMPTS",5),s(w,"BACKOFF_INTERVALS_IN_MILLIS",[3e4,6e4,12e4,24e4,48e4]),s(w,"RETRY_AFTER_KEY","Retry-After");let $=w;class A{constructor(t=10){s(this,"outstandingRequestsMap",new Map),s(this,"completedTransactionsQueue",[]),s(this,"completedTransactionsToKeep"),this.completedTransactionsToKeep=t}addOutstandingRequest(t,e){if(this.outstandingRequestsMap.has(t))throw new Error(`Transaction [${t}] already has an outstanding request`);const s={transactionId:t,request:e,response:null,start:new Date,end:null};this.outstandingRequestsMap.set(t,s)}addResponse(t,e){const s=this.outstandingRequestsMap.get(t);if(void 0===s)throw new Error(`Transaction [${t}] does not have an outstanding request`);s.response=e,s.end=new Date,this.outstandingRequestsMap.delete(t),this.addCompletedTransaction(s)}get outstandingRequests(){return Array.from(this.outstandingRequestsMap.values())}get completedTransactions(){return this.completedTransactionsQueue}clearOutstandingRequests(){this.outstandingRequestsMap.clear()}addCompletedTransaction(t){this.completedTransactionsQueue.push(t),this.completedTransactionsQueue.length>this.completedTransactionsToKeep&&this.completedTransactionsQueue.shift()}}function k(t){return function(t){if(t.event===o.NewMessage){const e=t.data;return e.created||(e.created=(new Date).toISOString()),{...t,data:e}}return t}(t)}const R=class t extends ${constructor(t){super(t),s(this,"pingIntervalId",null),s(this,"messageEventListeners",[]),s(this,"config",null),s(this,"pingTransactionManager",new A)}onOpen(t){return this.pingTransactionManager.clearOutstandingRequests(),this.restartPingInterval(),this.sendGetConfig(),this.sendSubscribe(),Promise.resolve()}onMessageReceived(e){if("action"in e&&e.action===i.Ping){const t=e;this.sendPong(t)}if("response"in e&&"pong"===e.response){const t=e;this.pingTransactionManager.addResponse(t.tid,t),this.pingTransactionManager.clearOutstandingRequests()}if("response"in e&&"config"===e.response){const t=e;this.setConfig(t.data)}if("event"in e&&t.isInboxMessageEvent(e.event)){const t=k(e);for(const e of this.messageEventListeners)e(t)}return this.restartPingInterval(),Promise.resolve()}onClose(t){return this.clearPingInterval(),this.clearMessageEventListeners(),this.pingTransactionManager.clearOutstandingRequests(),Promise.resolve()}onError(t){return Promise.resolve()}sendSubscribe(){const t={channel:this.userId,event:"*"};this.subTenantId&&(t.accountId=this.subTenantId);const e={tid:u.nanoid(),action:n.Subscribe,data:t};this.send(e)}sendUnsubscribe(){const t={tid:u.nanoid(),action:n.Unsubscribe,data:{channel:this.userId}};this.send(t)}addMessageEventListener(t){return this.messageEventListeners.push(t),()=>{this.removeMessageEventListener(t)}}sendPing(){var t;if(this.pingTransactionManager.outstandingRequests.length>=this.maxOutstandingPings)return null==(t=this.logger)||t.debug("Max outstanding pings reached, retrying connection."),this.close(T,"Max outstanding pings reached, retrying connection."),void this.retryConnection();const e={tid:u.nanoid(),action:n.Ping};this.send(e),this.pingTransactionManager.addOutstandingRequest(e.tid,e)}sendPong(t){const e={tid:t.tid,action:n.Pong};this.send(e)}sendGetConfig(){const t={tid:u.nanoid(),action:n.GetConfig};this.send(t)}restartPingInterval(){this.clearPingInterval(),this.pingIntervalId=window.setInterval((()=>{this.sendPing()}),this.pingInterval)}clearPingInterval(){this.pingIntervalId&&window.clearInterval(this.pingIntervalId)}get pingInterval(){return this.config?1e3*this.config.pingInterval:t.DEFAULT_PING_INTERVAL_MILLIS}get maxOutstandingPings(){return this.config?this.config.maxOutstandingPings:t.DEFAULT_MAX_OUTSTANDING_PINGS}setConfig(t){this.config=t}clearMessageEventListeners(){for(;this.messageEventListeners.length>0;)this.messageEventListeners.pop()}removeMessageEventListener(t){const e=this.messageEventListeners.indexOf(t);e>-1&&this.messageEventListeners.splice(e,1)}static isInboxMessageEvent(t){return Object.values(o).includes(t)}};s(R,"DEFAULT_PING_INTERVAL_MILLIS",6e4),s(R,"DEFAULT_MAX_OUTSTANDING_PINGS",3);let E=R;class C extends f{constructor(t){super(t),s(this,"socket"),this.socket=new E(t)}async getMessages(t){const e=(null==t?void 0:t.filter)||{},s=`\n query GetInboxMessages(\n $params: FilterParamsInput = ${this.createFilterParams(e)}\n $unreadCountParams: FilterParamsInput = ${this.createUnreadCountFilterParams(e)}\n $limit: Int = ${(null==t?void 0:t.paginationLimit)??24}\n $after: String ${(null==t?void 0:t.startCursor)?`= "${t.startCursor}"`:""}\n ) {\n count: count(params: $params)\n unreadCount: count(params: $unreadCountParams)\n messages(params: $params, limit: $limit, after: $after) {\n totalCount\n pageInfo {\n startCursor\n hasNextPage\n }\n nodes {\n messageId\n read\n archived\n created\n opened\n title\n preview\n data\n tags\n trackingIds {\n clickTrackingId\n }\n actions {\n content\n data\n href\n }\n }\n }\n }\n `;return await y({options:this.options,query:s,headers:{"x-courier-user-id":this.options.userId,Authorization:`Bearer ${this.options.accessToken}`},url:this.options.apiUrls.inbox.graphql})}async getUnreadCounts(t){const e={},s={};for(const[c,u]of Object.entries(t))"read"===u.status?e[c]=0:s[c]=u;if(0===Object.keys(s).length)return e;const n=[],i=[],o={};for(const[c,u]of Object.entries(s)){const t=C.sanitizeGraphQLIdentifier(c);o[t]=c;const e=this.createUnreadCountFilterParams(u);n.push(`$${t}: FilterParamsInput = ${e}`),i.push(`${t}: count(params: $${t})`)}const r=`\n query GetUnreadCounts(\n ${n.join("\n")}\n ) {\n ${i.join("\n")}\n }\n `,a=await y({options:this.options,query:r,headers:{"x-courier-user-id":this.options.userId,Authorization:`Bearer ${this.options.accessToken}`},url:this.options.apiUrls.inbox.graphql});if(a.data)for(const[c,u]of Object.entries(o))e[u]=a.data[c]??0;return e}async getArchivedMessages(t){return this.getMessages({paginationLimit:null==t?void 0:t.paginationLimit,startCursor:null==t?void 0:t.startCursor,filter:{archived:!0}})}async getUnreadMessageCount(){var t;const e=`\n query GetMessages {\n count(params: { status: "unread" ${this.options.tenantId?`, accountId: "${this.options.tenantId}"`:""} })\n }\n `;return(null==(t=(await y({options:this.options,query:e,headers:{"x-courier-user-id":this.options.userId,Authorization:`Bearer ${this.options.accessToken}`},url:this.options.apiUrls.inbox.graphql})).data)?void 0:t.count)??0}async click(t){const e=`\n mutation TrackEvent {\n clicked(messageId: "${t.messageId}", trackingId: "${t.trackingId}")\n }\n `,s={"x-courier-user-id":this.options.userId,Authorization:`Bearer ${this.options.accessToken}`};this.options.connectionId&&(s["x-courier-client-source-id"]=this.options.connectionId),await y({options:this.options,query:e,headers:s,url:this.options.apiUrls.inbox.graphql})}async read(t){const e=`\n mutation TrackEvent {\n read(messageId: "${t.messageId}")\n }\n `,s={"x-courier-user-id":this.options.userId,Authorization:`Bearer ${this.options.accessToken}`};this.options.connectionId&&(s["x-courier-client-source-id"]=this.options.connectionId),await y({options:this.options,query:e,headers:s,url:this.options.apiUrls.inbox.graphql})}async unread(t){const e=`\n mutation TrackEvent {\n unread(messageId: "${t.messageId}")\n }\n `,s={"x-courier-user-id":this.options.userId,Authorization:`Bearer ${this.options.accessToken}`};this.options.connectionId&&(s["x-courier-client-source-id"]=this.options.connectionId),await y({options:this.options,query:e,headers:s,url:this.options.apiUrls.inbox.graphql})}async open(t){const e=`\n mutation TrackEvent {\n opened(messageId: "${t.messageId}")\n }\n `,s={"x-courier-user-id":this.options.userId,Authorization:`Bearer ${this.options.accessToken}`};this.options.connectionId&&(s["x-courier-client-source-id"]=this.options.connectionId),await y({options:this.options,query:e,headers:s,url:this.options.apiUrls.inbox.graphql})}async archive(t){const e=`\n mutation TrackEvent {\n archive(messageId: "${t.messageId}")\n }\n `,s={"x-courier-user-id":this.options.userId,Authorization:`Bearer ${this.options.accessToken}`};this.options.connectionId&&(s["x-courier-client-source-id"]=this.options.connectionId),await y({options:this.options,query:e,headers:s,url:this.options.apiUrls.inbox.graphql})}async unarchive(t){const e=`\n mutation TrackEvent {\n unarchive(messageId: "${t.messageId}")\n }\n `,s={"x-courier-user-id":this.options.userId,Authorization:`Bearer ${this.options.accessToken}`};this.options.connectionId&&(s["x-courier-client-source-id"]=this.options.connectionId),await y({options:this.options,query:e,headers:s,url:this.options.apiUrls.inbox.graphql})}async readAll(){const t={"x-courier-user-id":this.options.userId,Authorization:`Bearer ${this.options.accessToken}`};this.options.connectionId&&(t["x-courier-client-source-id"]=this.options.connectionId),await y({options:this.options,query:"\n mutation TrackEvent {\n markAllRead\n }\n ",headers:t,url:this.options.apiUrls.inbox.graphql})}async archiveRead(){const t={"x-courier-user-id":this.options.userId,Authorization:`Bearer ${this.options.accessToken}`};this.options.connectionId&&(t["x-courier-client-source-id"]=this.options.connectionId),await y({options:this.options,query:"\n mutation TrackEvent {\n archiveRead\n }\n ",headers:t,url:this.options.apiUrls.inbox.graphql})}async archiveAll(){const t={"x-courier-user-id":this.options.userId,Authorization:`Bearer ${this.options.accessToken}`};this.options.connectionId&&(t["x-courier-client-source-id"]=this.options.connectionId),await y({options:this.options,query:"\n mutation TrackEvent {\n archiveAll\n }\n ",headers:t,url:this.options.apiUrls.inbox.graphql})}createFilterParams(t){const e=[];return this.options.tenantId&&e.push(`accountId: "${this.options.tenantId}"`),t.tags&&e.push(`tags: [${t.tags.map((t=>`"${t}"`)).join(",")}]`),t.status&&e.push(`status: "${t.status}"`),t.archived&&e.push(`archived: ${t.archived}`),`{ ${e.join(",")} }`}createUnreadCountFilterParams(t){return t.status?this.createFilterParams(t):this.createFilterParams({...t,status:"unread"})}static sanitizeGraphQLIdentifier(t){return`id_${t.replace(/_/g,"__").replace(/-/g,"_")}`}}class S{transformItem(t){return{topicId:t.topic_id,topicName:t.topic_name,sectionId:t.section_id,sectionName:t.section_name,status:t.status,defaultStatus:t.default_status,hasCustomRouting:t.has_custom_routing,customRouting:t.custom_routing||[]}}*transform(t){for(const e of t)yield this.transformItem(e)}}class U extends f{constructor(){super(...arguments),s(this,"transformer",new S)}async getUserPreferences(t){let e=`${this.options.apiUrls.courier.rest}/users/${this.options.userId}/preferences`;(null==t?void 0:t.paginationCursor)&&(e+=`?cursor=${t.paginationCursor}`);const s=await v({options:this.options,url:e,method:"GET",headers:{Authorization:`Bearer ${this.options.accessToken}`}});return{items:[...this.transformer.transform(s.items)],paging:s.paging}}async getUserPreferenceTopic(t){const e=await v({options:this.options,url:`${this.options.apiUrls.courier.rest}/users/${this.options.userId}/preferences/${t.topicId}`,method:"GET",headers:{Authorization:`Bearer ${this.options.accessToken}`}});return this.transformer.transformItem(e.topic)}async putUserPreferenceTopic(t){const e={topic:{status:t.status,has_custom_routing:t.hasCustomRouting,custom_routing:t.customRouting}};await v({options:this.options,url:`${this.options.apiUrls.courier.rest}/users/${this.options.userId}/preferences/${t.topicId}`,method:"PUT",headers:{Authorization:`Bearer ${this.options.accessToken}`},body:e})}getNotificationCenterUrl(t){return`https://view.notificationcenter.app/p/${function(t){const e=new Uint8Array(t.length);for(let s=0;s<t.length;s++)e[s]=t.charCodeAt(s);return btoa(String.fromCharCode(...e))}(`${function(t){const e=atob(t),s=new Uint8Array(e.length);for(let n=0;n<e.length;n++)s[n]=e.charCodeAt(n);return String.fromCharCode(...s)}(t.clientKey)}#${this.options.userId}${this.options.tenantId?`#${this.options.tenantId}`:""}#false`)}`}}class P extends f{async putUserToken(t){const e={provider_key:t.provider,...t.device&&{device:{app_id:t.device.appId,ad_id:t.device.adId,device_id:t.device.deviceId,platform:t.device.platform,manufacturer:t.device.manufacturer,model:t.device.model}}};await v({options:this.options,url:`${this.options.apiUrls.courier.rest}/users/${this.options.userId}/tokens/${t.token}`,method:"PUT",headers:{Authorization:`Bearer ${this.options.accessToken}`},body:e,validCodes:[200,204]})}async deleteUserToken(t){await v({options:this.options,url:`${this.options.apiUrls.courier.rest}/users/${this.options.userId}/tokens/${t.token}`,method:"DELETE",headers:{Authorization:`Bearer ${this.options.accessToken}`},validCodes:[200,204]})}}class L extends f{async putSubscription(t){return await v({url:`${this.options.apiUrls.courier.rest}/lists/${t.listId}/subscriptions/${this.options.userId}`,options:this.options,method:"PUT",headers:{Authorization:`Bearer ${this.options.accessToken}`}})}async deleteSubscription(t){return await v({url:`${this.options.apiUrls.courier.rest}/lists/${t.listId}/subscriptions/${this.options.userId}`,options:this.options,method:"DELETE",headers:{Authorization:`Bearer ${this.options.accessToken}`}})}}class x extends f{async postInboundCourier(t){const{clientKey:e,...s}=t;return await v({url:`${this.options.apiUrls.courier.rest}/inbound/courier`,options:this.options,method:"POST",headers:{"x-courier-client-key":e},body:{...s,userId:this.options.userId},validCodes:[200,202]})}async postTrackingUrl(t){return await v({url:t.url,options:this.options,method:"POST",body:{event:t.event}})}}class _{constructor(t,e,s){this.clientId=t,this.sdkName=e,this.sdkVersion=s}getUserAgentInfo(){return{[d]:this.sdkName,[l]:this.sdkVersion,[p]:this.clientId}}toHttpHeaderValue(){return Object.entries(this.getUserAgentInfo()).map((([t,e])=>`${t}=${e}`)).join(",")}}const q=class t extends f{constructor(e){var n,i;const o=void 0!==e.showLogs&&e.showLogs,c=u.nanoid(),h={...e,showLogs:o,connectionId:c,apiUrls:e.apiUrls||r(),accessToken:e.jwt??e.publicApiKey},d=new _(c,e.courierUserAgentName||t.COURIER_JS_NAME,e.courierUserAgentVersion||t.COURIER_JS_VERSION);super({...h,logger:new a(h.showLogs),courierUserAgent:d,apiUrls:r(h.apiUrls)}),s(this,"tokens"),s(this,"brands"),s(this,"preferences"),s(this,"inbox"),s(this,"lists"),s(this,"tracking"),this.tokens=new P(this.options),this.brands=new b(this.options),this.preferences=new U(this.options),this.inbox=new C(this.options),this.lists=new L(this.options),this.tracking=new x(this.options),this.options.jwt||this.options.publicApiKey||this.options.logger.warn("Courier Client initialized with no authentication method. Please provide a JWT or public API key."),this.options.publicApiKey&&(null==(n=this.options.logger)||n.warn("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/api-reference/authentication/create-jwt\nThis endpoint should be called from your backend server, not the SDK.")),this.options.jwt&&this.options.publicApiKey&&(null==(i=this.options.logger)||i.warn("Courier Warning: Both a JWT and a Public API Key were provided. The Public API Key will be ignored."))}};s(q,"COURIER_JS_NAME","courier-js"),s(q,"COURIER_JS_VERSION","3.0.0");let O=q;class M{constructor(t){s(this,"callback"),this.callback=t}remove(){F.shared.removeAuthenticationListener(this)}}const N=class t{constructor(){s(this,"id",u.nanoid()),s(this,"instanceClient"),s(this,"courierUserAgentName"),s(this,"courierUserAgentVersion"),s(this,"_paginationLimit",24),s(this,"authenticationListeners",[])}get paginationLimit(){return this._paginationLimit}set paginationLimit(t){this._paginationLimit=Math.min(Math.max(t,1),100)}get client(){return this.instanceClient}static get shared(){return t.instance||(t.instance=new t),t.instance}signIn(t){this.instanceClient&&(this.instanceClient.options.logger.warn("Sign in called but there is already a user signed in. Signing out the current user."),this.signOut()),this.instanceClient=new O({...t,courierUserAgentName:this.courierUserAgentName,courierUserAgentVersion:this.courierUserAgentVersion}),this.notifyAuthenticationListeners({userId:t.userId})}signOut(){var t,e;null==(e=null==(t=this.instanceClient)?void 0:t.inbox.socket)||e.close(),this.instanceClient=void 0,this.notifyAuthenticationListeners({userId:void 0})}addAuthenticationListener(t){var e;null==(e=this.instanceClient)||e.options.logger.info("Adding authentication listener");const s=new M(t);return this.authenticationListeners.push(s),s}removeAuthenticationListener(t){var e;null==(e=this.instanceClient)||e.options.logger.info("Removing authentication listener"),this.authenticationListeners=this.authenticationListeners.filter((e=>e!==t))}notifyAuthenticationListeners(t){this.authenticationListeners.forEach((e=>e.callback(t)))}};s(N,"instance");let F=N;t.BrandClient=b,t.Courier=F,t.CourierClient=O,t.InboxClient=C,t.InboxMessageEvent=o,t.ListClient=L,t.PreferenceClient=U,t.TokenClient=P,t.TrackingClient=x,Object.defineProperty(t,Symbol.toStringTag,{value:"Module"})}));
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).CourierJS={})}(this,(function(e){"use strict";var t=Object.defineProperty,n=(e,n,s)=>((e,n,s)=>n in e?t(e,n,{enumerable:!0,configurable:!0,writable:!0,value:s}):e[n]=s)(e,"symbol"!=typeof n?n+"":n,s);const s=e=>Object.freeze({courier:Object.freeze({...e.courier}),inbox:Object.freeze({...e.inbox})}),i={us:s({courier:{rest:"https://api.courier.com",graphql:"https://api.courier.com/client/q"},inbox:{graphql:"https://inbox.courier.com/q",webSocket:"wss://realtime.courier.io"}}),eu:s({courier:{rest:"https://api.eu.courier.com",graphql:"https://api.eu.courier.com/client/q"},inbox:{graphql:"https://inbox.eu.courier.io/q",webSocket:"wss://realtime.eu.courier.io"}})},o=i.us,r=i.eu,a=(e="us")=>{return{courier:{...(t=i[e]).courier},inbox:{...t.inbox}};var t},c=e=>{const t=o;return{courier:{rest:(null==e?void 0:e.courier.rest)||t.courier.rest,graphql:(null==e?void 0:e.courier.graphql)||t.courier.graphql},inbox:{graphql:(null==e?void 0:e.inbox.graphql)||t.inbox.graphql,webSocket:(null==e?void 0:e.inbox.webSocket)||t.inbox.webSocket}}};var u=(e=>(e.Subscribe="subscribe",e.Unsubscribe="unsubscribe",e.Pong="pong",e.Ping="ping",e.GetConfig="get-config",e))(u||{}),h=(e=>(e.Ping="ping",e))(h||{}),d=(e=>(e.NewMessage="message",e.Archive="archive",e.ArchiveAll="archive-all",e.ArchiveRead="archive-read",e.Clicked="clicked",e.MarkAllRead="mark-all-read",e.Opened="opened",e.Read="read",e.Unarchive="unarchive",e.Unopened="unopened",e.Unread="unread",e))(d||{});class l{constructor(e){n(this,"PREFIX","[COURIER]"),this.showLogs=e}warn(e,...t){this.showLogs&&console.warn(`${this.PREFIX} ${e}`,...t)}log(e,...t){this.showLogs&&console.log(`${this.PREFIX} ${e}`,...t)}error(e,...t){this.showLogs&&console.error(`${this.PREFIX} ${e}`,...t)}debug(e,...t){this.showLogs&&console.debug(`${this.PREFIX} ${e}`,...t)}info(e,...t){this.showLogs&&console.info(`${this.PREFIX} ${e}`,...t)}}const p=class e{static nanoid(t=21){let n="",s=crypto.getRandomValues(new Uint8Array(t|=0));for(;t--;)n+=e.ALPHABET[63&s[t]];return n}};n(p,"ALPHABET","useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict");let g=p;const m="x-courier-ua",I="sdk",y="sdkv",v="cid";class f extends Error{constructor(e,t,n){super(t),this.code=e,this.type=n,this.name="CourierRequestError"}}function b(e,t,n,s){e.log(`\n📡 New Courier ${n} Request: ${t}\nURL: ${s.url}\n${s.method?`Method: ${s.method}`:""}\n${s.query?`Query: ${s.query}`:""}\n${s.variables?`Variables: ${JSON.stringify(s.variables,null,2)}`:""}\nHeaders: ${JSON.stringify(s.headers,null,2)}\nBody: ${s.body?JSON.stringify(s.body,null,2):"Empty"}\n `)}function T(e,t,n,s){e.log(`\n📡 New Courier ${n} Response: ${t}\nStatus Code: ${s.status}\nResponse JSON: ${JSON.stringify(s.response,null,2)}\n `)}async function w(e){const t=e.validCodes??[200],n=e.options.showLogs?g.nanoid():void 0,s=e.options.courierUserAgent.toHttpHeaderValue(),i=new Request(e.url,{method:e.method,headers:{"Content-Type":"application/json",[m]:s,...e.headers},body:e.body?JSON.stringify(e.body):void 0});n&&b(e.options.logger,n,"HTTP",{url:i.url,method:i.method,headers:Object.fromEntries(i.headers.entries()),body:e.body});const o=await fetch(i);if(204===o.status)return;let r;try{r=await o.json()}catch(a){if(200===o.status)return;throw new f(o.status,"Failed to parse response as JSON","PARSE_ERROR")}if(n&&T(e.options.logger,n,"HTTP",{status:o.status,response:r}),!t.includes(o.status))throw new f(o.status,(null==r?void 0:r.message)||"Unknown Error",null==r?void 0:r.type);return r}async function $(e){const t=e.options.showLogs?g.nanoid():void 0,n=e.options.courierUserAgent.toHttpHeaderValue();t&&b(e.options.logger,t,"GraphQL",{url:e.url,headers:e.headers,query:e.query,variables:e.variables});const s=await fetch(e.url,{method:"POST",headers:{"Content-Type":"application/json",[m]:n,...e.headers},body:JSON.stringify({query:e.query,variables:e.variables})});let i;try{i=await s.json()}catch(o){throw new f(s.status,"Failed to parse response as JSON","PARSE_ERROR")}if(t&&T(e.options.logger,t,"GraphQL",{status:s.status,response:i}),!s.ok)throw new f(s.status,(null==i?void 0:i.message)||"Unknown Error",null==i?void 0:i.type);return i}class A{constructor(e){this.options=e}}class R extends A{async getBrand(e){const t=`\n query GetBrand {\n brand(brandId: "${e.brandId}") {\n settings {\n colors {\n primary\n secondary\n tertiary\n }\n inapp {\n borderRadius\n disableCourierFooter\n }\n }\n }\n }\n `;return(await $({options:this.options,url:this.options.apiUrls.courier.graphql,headers:{"x-courier-user-id":this.options.userId,"x-courier-client-key":"empty",Authorization:`Bearer ${this.options.accessToken}`},query:t,variables:{brandId:e.brandId}})).data.brand}}const k=1e3,C=class e{constructor(e){n(this,"webSocket",null),n(this,"retryAttempt",0),n(this,"retryTimeoutId",null),n(this,"closeRequested",!1),n(this,"url"),n(this,"options"),this.url=e.apiUrls.inbox.webSocket,this.options=e}async connect(){var t,n;return this.isConnecting||this.isOpen?(null==(n=this.options.logger)||n.info(`Attempted to open a WebSocket connection, but one already exists in state '${null==(t=this.webSocket)?void 0:t.readyState}'.`),Promise.resolve()):(this.clearRetryTimeout(),this.closeRequested=!1,new Promise(((t,n)=>{this.webSocket=new WebSocket(this.getWebSocketUrl()),this.webSocket.addEventListener("open",(e=>{this.retryAttempt=0,this.onOpen(e),t()})),this.webSocket.addEventListener("message",(async e=>{var t;try{const t=JSON.parse(e.data);if("event"in t&&"reconnect"===t.event)return this.close(k),void(await this.retryConnection(1e3*t.retryAfter));this.onMessageReceived(t)}catch(n){null==(t=this.options.logger)||t.error("Error parsing socket message",n)}})),this.webSocket.addEventListener("close",(t=>{if(t.code!==k&&!this.closeRequested){const n=e.parseCloseEvent(t);n.retryAfterSeconds?this.retryConnection(1e3*n.retryAfterSeconds):this.retryConnection()}this.onClose(t)})),this.webSocket.addEventListener("error",(e=>{this.closeRequested||this.retryConnection(),this.onError(e),n(e)}))})))}close(e=1e3,t){null!==this.webSocket&&(this.closeRequested=!0,this.clearRetryTimeout(),this.retryAttempt=0,this.webSocket.close(e,t))}send(e){var t;if(null===this.webSocket||this.isConnecting)return void(null==(t=this.options.logger)||t.info("Attempted to send a message, but the WebSocket is not yet open."));const n=JSON.stringify(e);this.webSocket.send(n)}get userId(){return this.options.userId}get subTenantId(){return this.options.tenantId}get logger(){return this.options.logger}get courierUserAgent(){return this.options.courierUserAgent}get isConnecting(){return null!==this.webSocket&&this.webSocket.readyState===WebSocket.CONNECTING}get isOpen(){return null!==this.webSocket&&this.webSocket.readyState===WebSocket.OPEN}getWebSocketUrl(){const e=this.options.accessToken,t=this.options.connectionId,n=this.userId,s=this.courierUserAgent.getUserAgentInfo()[I],i=this.courierUserAgent.getUserAgentInfo()[y];return`${this.url}?auth=${e}&cid=${t}&iwpv=v1&userId=${n}&${I}=${s}&${y}=${i}`}static parseCloseEvent(t){if(null===t.reason||""===t.reason)return t;try{const n=JSON.parse(t.reason);if(!n[e.RETRY_AFTER_KEY])return t;const s=parseInt(n[e.RETRY_AFTER_KEY]);return Number.isNaN(s)||s<0?t:{...t,retryAfterSeconds:s}}catch(n){return t}}getBackoffTimeInMillis(){const t=e.BACKOFF_INTERVALS_IN_MILLIS[this.retryAttempt],n=t-t*e.BACKOFF_JITTER_FACTOR,s=t+t*e.BACKOFF_JITTER_FACTOR;return Math.floor(Math.random()*(s-n)+n)}async retryConnection(t){var n,s,i;if(null!==this.retryTimeoutId)return void(null==(n=this.logger)||n.debug("Skipping retry attempt because a previous retry is already scheduled."));if(this.retryAttempt>=e.MAX_RETRY_ATTEMPTS)return void(null==(s=this.logger)||s.error(`Max retry attempts (${e.MAX_RETRY_ATTEMPTS}) reached.`));const o=t??this.getBackoffTimeInMillis();this.retryTimeoutId=window.setTimeout((async()=>{try{await this.connect()}catch(e){}}),o),null==(i=this.logger)||i.debug(`Retrying connection in ${Math.floor(o/1e3)}s. Retry attempt ${this.retryAttempt+1} of ${e.MAX_RETRY_ATTEMPTS}.`),this.retryAttempt++}clearRetryTimeout(){null!==this.retryTimeoutId&&(window.clearTimeout(this.retryTimeoutId),this.retryTimeoutId=null)}};n(C,"BACKOFF_JITTER_FACTOR",.5),n(C,"MAX_RETRY_ATTEMPTS",5),n(C,"BACKOFF_INTERVALS_IN_MILLIS",[3e4,6e4,12e4,24e4,48e4]),n(C,"RETRY_AFTER_KEY","Retry-After");let E=C;class S{constructor(e=10){n(this,"outstandingRequestsMap",new Map),n(this,"completedTransactionsQueue",[]),n(this,"completedTransactionsToKeep"),this.completedTransactionsToKeep=e}addOutstandingRequest(e,t){if(this.outstandingRequestsMap.has(e))throw new Error(`Transaction [${e}] already has an outstanding request`);const n={transactionId:e,request:t,response:null,start:new Date,end:null};this.outstandingRequestsMap.set(e,n)}addResponse(e,t){const n=this.outstandingRequestsMap.get(e);if(void 0===n)throw new Error(`Transaction [${e}] does not have an outstanding request`);n.response=t,n.end=new Date,this.outstandingRequestsMap.delete(e),this.addCompletedTransaction(n)}get outstandingRequests(){return Array.from(this.outstandingRequestsMap.values())}get completedTransactions(){return this.completedTransactionsQueue}clearOutstandingRequests(){this.outstandingRequestsMap.clear()}addCompletedTransaction(e){this.completedTransactionsQueue.push(e),this.completedTransactionsQueue.length>this.completedTransactionsToKeep&&this.completedTransactionsQueue.shift()}}function U(e){return function(e){if(e.event===d.NewMessage){const t=e.data;return t.created||(t.created=(new Date).toISOString()),{...e,data:t}}return e}(e)}const P=class e extends E{constructor(e){super(e),n(this,"pingIntervalId",null),n(this,"messageEventListeners",[]),n(this,"config",null),n(this,"pingTransactionManager",new S)}onOpen(e){return this.pingTransactionManager.clearOutstandingRequests(),this.restartPingInterval(),this.sendGetConfig(),this.sendSubscribe(),Promise.resolve()}onMessageReceived(t){if("action"in t&&t.action===h.Ping){const e=t;this.sendPong(e)}if("response"in t&&"pong"===t.response){const e=t;this.pingTransactionManager.addResponse(e.tid,e),this.pingTransactionManager.clearOutstandingRequests()}if("response"in t&&"config"===t.response){const e=t;this.setConfig(e.data)}if("event"in t&&e.isInboxMessageEvent(t.event)){const e=U(t);for(const t of this.messageEventListeners)t(e)}return this.restartPingInterval(),Promise.resolve()}onClose(e){return this.clearPingInterval(),this.clearMessageEventListeners(),this.pingTransactionManager.clearOutstandingRequests(),Promise.resolve()}onError(e){return Promise.resolve()}sendSubscribe(){const e={channel:this.userId,event:"*"};this.subTenantId&&(e.accountId=this.subTenantId);const t={tid:g.nanoid(),action:u.Subscribe,data:e};this.send(t)}sendUnsubscribe(){const e={tid:g.nanoid(),action:u.Unsubscribe,data:{channel:this.userId}};this.send(e)}addMessageEventListener(e){return this.messageEventListeners.push(e),()=>{this.removeMessageEventListener(e)}}sendPing(){var e;if(this.pingTransactionManager.outstandingRequests.length>=this.maxOutstandingPings)return null==(e=this.logger)||e.debug("Max outstanding pings reached, retrying connection."),this.close(k,"Max outstanding pings reached, retrying connection."),void this.retryConnection();const t={tid:g.nanoid(),action:u.Ping};this.send(t),this.pingTransactionManager.addOutstandingRequest(t.tid,t)}sendPong(e){const t={tid:e.tid,action:u.Pong};this.send(t)}sendGetConfig(){const e={tid:g.nanoid(),action:u.GetConfig};this.send(e)}restartPingInterval(){this.clearPingInterval(),this.pingIntervalId=window.setInterval((()=>{this.sendPing()}),this.pingInterval)}clearPingInterval(){this.pingIntervalId&&window.clearInterval(this.pingIntervalId)}get pingInterval(){return this.config?1e3*this.config.pingInterval:e.DEFAULT_PING_INTERVAL_MILLIS}get maxOutstandingPings(){return this.config?this.config.maxOutstandingPings:e.DEFAULT_MAX_OUTSTANDING_PINGS}setConfig(e){this.config=e}clearMessageEventListeners(){for(;this.messageEventListeners.length>0;)this.messageEventListeners.pop()}removeMessageEventListener(e){const t=this.messageEventListeners.indexOf(e);t>-1&&this.messageEventListeners.splice(t,1)}static isInboxMessageEvent(e){return Object.values(d).includes(e)}};n(P,"DEFAULT_PING_INTERVAL_MILLIS",6e4),n(P,"DEFAULT_MAX_OUTSTANDING_PINGS",3);let x=P;class q extends A{constructor(e){super(e),n(this,"socket"),this.socket=new x(e)}async getMessages(e){const t=(null==e?void 0:e.filter)||{},n=`\n query GetInboxMessages(\n $params: FilterParamsInput = ${this.createFilterParams(t)}\n $unreadCountParams: FilterParamsInput = ${this.createUnreadCountFilterParams(t)}\n $limit: Int = ${(null==e?void 0:e.paginationLimit)??24}\n $after: String ${(null==e?void 0:e.startCursor)?`= "${e.startCursor}"`:""}\n ) {\n count: count(params: $params)\n unreadCount: count(params: $unreadCountParams)\n messages(params: $params, limit: $limit, after: $after) {\n totalCount\n pageInfo {\n startCursor\n hasNextPage\n }\n nodes {\n messageId\n read\n archived\n created\n opened\n title\n preview\n data\n tags\n trackingIds {\n clickTrackingId\n }\n actions {\n content\n data\n href\n }\n }\n }\n }\n `;return await $({options:this.options,query:n,headers:{"x-courier-user-id":this.options.userId,Authorization:`Bearer ${this.options.accessToken}`},url:this.options.apiUrls.inbox.graphql})}async getUnreadCounts(e){const t={},n={};for(const[c,u]of Object.entries(e))"read"===u.status?t[c]=0:n[c]=u;if(0===Object.keys(n).length)return t;const s=[],i=[],o={};for(const[c,u]of Object.entries(n)){const e=q.sanitizeGraphQLIdentifier(c);o[e]=c;const t=this.createUnreadCountFilterParams(u);s.push(`$${e}: FilterParamsInput = ${t}`),i.push(`${e}: count(params: $${e})`)}const r=`\n query GetUnreadCounts(\n ${s.join("\n")}\n ) {\n ${i.join("\n")}\n }\n `,a=await $({options:this.options,query:r,headers:{"x-courier-user-id":this.options.userId,Authorization:`Bearer ${this.options.accessToken}`},url:this.options.apiUrls.inbox.graphql});if(a.data)for(const[c,u]of Object.entries(o))t[u]=a.data[c]??0;return t}async getArchivedMessages(e){return this.getMessages({paginationLimit:null==e?void 0:e.paginationLimit,startCursor:null==e?void 0:e.startCursor,filter:{archived:!0}})}async getUnreadMessageCount(){var e;const t=`\n query GetMessages {\n count(params: { status: "unread" ${this.options.tenantId?`, accountId: "${this.options.tenantId}"`:""} })\n }\n `;return(null==(e=(await $({options:this.options,query:t,headers:{"x-courier-user-id":this.options.userId,Authorization:`Bearer ${this.options.accessToken}`},url:this.options.apiUrls.inbox.graphql})).data)?void 0:e.count)??0}async click(e){const t=`\n mutation TrackEvent {\n clicked(messageId: "${e.messageId}", trackingId: "${e.trackingId}")\n }\n `,n={"x-courier-user-id":this.options.userId,Authorization:`Bearer ${this.options.accessToken}`};this.options.connectionId&&(n["x-courier-client-source-id"]=this.options.connectionId),await $({options:this.options,query:t,headers:n,url:this.options.apiUrls.inbox.graphql})}async read(e){const t=`\n mutation TrackEvent {\n read(messageId: "${e.messageId}")\n }\n `,n={"x-courier-user-id":this.options.userId,Authorization:`Bearer ${this.options.accessToken}`};this.options.connectionId&&(n["x-courier-client-source-id"]=this.options.connectionId),await $({options:this.options,query:t,headers:n,url:this.options.apiUrls.inbox.graphql})}async unread(e){const t=`\n mutation TrackEvent {\n unread(messageId: "${e.messageId}")\n }\n `,n={"x-courier-user-id":this.options.userId,Authorization:`Bearer ${this.options.accessToken}`};this.options.connectionId&&(n["x-courier-client-source-id"]=this.options.connectionId),await $({options:this.options,query:t,headers:n,url:this.options.apiUrls.inbox.graphql})}async open(e){const t=`\n mutation TrackEvent {\n opened(messageId: "${e.messageId}")\n }\n `,n={"x-courier-user-id":this.options.userId,Authorization:`Bearer ${this.options.accessToken}`};this.options.connectionId&&(n["x-courier-client-source-id"]=this.options.connectionId),await $({options:this.options,query:t,headers:n,url:this.options.apiUrls.inbox.graphql})}async archive(e){const t=`\n mutation TrackEvent {\n archive(messageId: "${e.messageId}")\n }\n `,n={"x-courier-user-id":this.options.userId,Authorization:`Bearer ${this.options.accessToken}`};this.options.connectionId&&(n["x-courier-client-source-id"]=this.options.connectionId),await $({options:this.options,query:t,headers:n,url:this.options.apiUrls.inbox.graphql})}async unarchive(e){const t=`\n mutation TrackEvent {\n unarchive(messageId: "${e.messageId}")\n }\n `,n={"x-courier-user-id":this.options.userId,Authorization:`Bearer ${this.options.accessToken}`};this.options.connectionId&&(n["x-courier-client-source-id"]=this.options.connectionId),await $({options:this.options,query:t,headers:n,url:this.options.apiUrls.inbox.graphql})}async readAll(){const e={"x-courier-user-id":this.options.userId,Authorization:`Bearer ${this.options.accessToken}`};this.options.connectionId&&(e["x-courier-client-source-id"]=this.options.connectionId),await $({options:this.options,query:"\n mutation TrackEvent {\n markAllRead\n }\n ",headers:e,url:this.options.apiUrls.inbox.graphql})}async archiveRead(){const e={"x-courier-user-id":this.options.userId,Authorization:`Bearer ${this.options.accessToken}`};this.options.connectionId&&(e["x-courier-client-source-id"]=this.options.connectionId),await $({options:this.options,query:"\n mutation TrackEvent {\n archiveRead\n }\n ",headers:e,url:this.options.apiUrls.inbox.graphql})}async archiveAll(){const e={"x-courier-user-id":this.options.userId,Authorization:`Bearer ${this.options.accessToken}`};this.options.connectionId&&(e["x-courier-client-source-id"]=this.options.connectionId),await $({options:this.options,query:"\n mutation TrackEvent {\n archiveAll\n }\n ",headers:e,url:this.options.apiUrls.inbox.graphql})}createFilterParams(e){const t=[];return this.options.tenantId&&t.push(`accountId: "${this.options.tenantId}"`),e.tags&&t.push(`tags: [${e.tags.map((e=>`"${e}"`)).join(",")}]`),e.status&&t.push(`status: "${e.status}"`),e.archived&&t.push(`archived: ${e.archived}`),e.from&&t.push(`from: "${e.from}"`),`{ ${t.join(",")} }`}createUnreadCountFilterParams(e){return e.status?this.createFilterParams(e):this.createFilterParams({...e,status:"unread"})}static sanitizeGraphQLIdentifier(e){return`id_${e.replace(/_/g,"__").replace(/-/g,"_")}`}}class L extends A{async getUserPreferences(e){var t,n;const s=`\n query GetRecipientPreferences {\n recipientPreferences${this.options.tenantId?`(accountId: "${this.options.tenantId}")`:""} {\n nodes {\n templateId\n templateName\n sectionId\n sectionName\n defaultStatus\n status\n hasCustomRouting\n routingPreferences\n digestSchedule\n }\n }\n }\n `;return{items:((null==(n=null==(t=(await $({options:this.options,url:this.options.apiUrls.courier.graphql,query:s,headers:{"x-courier-user-id":this.options.userId,"x-courier-client-key":"empty",Authorization:`Bearer ${this.options.accessToken}`}})).data)?void 0:t.recipientPreferences)?void 0:n.nodes)||[]).map((e=>this.transformToTopic(e))),paging:{cursor:null==e?void 0:e.paginationCursor,more:!1}}}async getUserPreferenceTopic(e){var t;const n=`\n query GetRecipientPreferenceTopic {\n recipientPreference(templateId: "${e.topicId}"${this.options.tenantId?`, accountId: "${this.options.tenantId}"`:""}) {\n templateId\n templateName\n status\n hasCustomRouting\n routingPreferences\n digestSchedule\n sectionId\n sectionName\n defaultStatus\n }\n }\n `,s=null==(t=(await $({options:this.options,url:this.options.apiUrls.courier.graphql,query:n,headers:{"x-courier-user-id":this.options.userId,"x-courier-client-key":"empty",Authorization:`Bearer ${this.options.accessToken}`}})).data)?void 0:t.recipientPreference;if(!s)throw new Error(`Preference topic not found: ${e.topicId}`);return this.transformToTopic(s)}async putUserPreferenceTopic(e){const t=e.customRouting.length>0?`[${e.customRouting.join(", ")}]`:"[]",n=`\n mutation UpdateRecipientPreferences {\n updatePreferences(\n templateId: "${e.topicId}",\n preferences: {\n status: ${e.status},\n hasCustomRouting: ${e.hasCustomRouting},\n routingPreferences: ${t}\n }${this.options.tenantId?`, accountId: "${this.options.tenantId}"`:""}\n )\n }\n `;await $({options:this.options,url:this.options.apiUrls.courier.graphql,query:n,headers:{"x-courier-user-id":this.options.userId,"x-courier-client-key":"empty",Authorization:`Bearer ${this.options.accessToken}`}})}getNotificationCenterUrl(e){return`https://view.notificationcenter.app/p/${function(e){const t=new Uint8Array(e.length);for(let n=0;n<e.length;n++)t[n]=e.charCodeAt(n);return btoa(String.fromCharCode(...t))}(`${function(e){const t=atob(e),n=new Uint8Array(t.length);for(let s=0;s<t.length;s++)n[s]=t.charCodeAt(s);return String.fromCharCode(...n)}(e.clientKey)}#${this.options.userId}${this.options.tenantId?`#${this.options.tenantId}`:""}#false`)}`}transformToTopic(e){return{topicId:e.templateId,topicName:e.templateName||"",sectionId:e.sectionId||"",sectionName:e.sectionName||"",status:e.status||"UNKNOWN",defaultStatus:e.defaultStatus||"UNKNOWN",hasCustomRouting:e.hasCustomRouting||!1,customRouting:e.routingPreferences||[]}}}class O extends A{async putUserToken(e){const t={provider_key:e.provider,...e.device&&{device:{app_id:e.device.appId,ad_id:e.device.adId,device_id:e.device.deviceId,platform:e.device.platform,manufacturer:e.device.manufacturer,model:e.device.model}}};await w({options:this.options,url:`${this.options.apiUrls.courier.rest}/users/${this.options.userId}/tokens/${e.token}`,method:"PUT",headers:{Authorization:`Bearer ${this.options.accessToken}`},body:t,validCodes:[200,204]})}async deleteUserToken(e){await w({options:this.options,url:`${this.options.apiUrls.courier.rest}/users/${this.options.userId}/tokens/${e.token}`,method:"DELETE",headers:{Authorization:`Bearer ${this.options.accessToken}`},validCodes:[200,204]})}}class _ extends A{async putSubscription(e){return await w({url:`${this.options.apiUrls.courier.rest}/lists/${e.listId}/subscriptions/${this.options.userId}`,options:this.options,method:"PUT",headers:{Authorization:`Bearer ${this.options.accessToken}`}})}async deleteSubscription(e){return await w({url:`${this.options.apiUrls.courier.rest}/lists/${e.listId}/subscriptions/${this.options.userId}`,options:this.options,method:"DELETE",headers:{Authorization:`Bearer ${this.options.accessToken}`}})}}class N extends A{async postInboundCourier(e){const{clientKey:t,...n}=e;return await w({url:`${this.options.apiUrls.courier.rest}/inbound/courier`,options:this.options,method:"POST",headers:{"x-courier-client-key":t},body:{...n,userId:this.options.userId},validCodes:[200,202]})}async postTrackingUrl(e){return await w({url:e.url,options:this.options,method:"POST",body:{event:e.event}})}}class M{constructor(e,t,n){this.clientId=e,this.sdkName=t,this.sdkVersion=n}getUserAgentInfo(){return{[I]:this.sdkName,[y]:this.sdkVersion,[v]:this.clientId}}toHttpHeaderValue(){return Object.entries(this.getUserAgentInfo()).map((([e,t])=>`${e}=${t}`)).join(",")}}const F=class e extends A{constructor(t){var s,i;const o=void 0!==t.showLogs&&t.showLogs,r=g.nanoid(),u={...t,showLogs:o,connectionId:r,apiUrls:t.apiUrls||a("us"),accessToken:t.jwt??t.publicApiKey},h=new M(r,t.courierUserAgentName||e.COURIER_JS_NAME,t.courierUserAgentVersion||e.COURIER_JS_VERSION);super({...u,logger:new l(u.showLogs),courierUserAgent:h,apiUrls:c(u.apiUrls)}),n(this,"tokens"),n(this,"brands"),n(this,"preferences"),n(this,"inbox"),n(this,"lists"),n(this,"tracking"),this.tokens=new O(this.options),this.brands=new R(this.options),this.preferences=new L(this.options),this.inbox=new q(this.options),this.lists=new _(this.options),this.tracking=new N(this.options),this.options.jwt||this.options.publicApiKey||this.options.logger.warn("Courier Client initialized with no authentication method. Please provide a JWT or public API key."),this.options.publicApiKey&&(null==(s=this.options.logger)||s.warn("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/api-reference/authentication/create-jwt\nThis endpoint should be called from your backend server, not the SDK.")),this.options.jwt&&this.options.publicApiKey&&(null==(i=this.options.logger)||i.warn("Courier Warning: Both a JWT and a Public API Key were provided. The Public API Key will be ignored."))}};n(F,"COURIER_JS_NAME","courier-js"),n(F,"COURIER_JS_VERSION","3.1.1");let B=F;class j{constructor(e){n(this,"callback"),this.callback=e}remove(){J.shared.removeAuthenticationListener(this)}}const z=class e{constructor(){n(this,"id",g.nanoid()),n(this,"instanceClient"),n(this,"courierUserAgentName"),n(this,"courierUserAgentVersion"),n(this,"_paginationLimit",24),n(this,"authenticationListeners",[])}get paginationLimit(){return this._paginationLimit}set paginationLimit(e){this._paginationLimit=Math.min(Math.max(e,1),100)}get client(){return this.instanceClient}static get shared(){return e.instance||(e.instance=new e),e.instance}signIn(e){this.instanceClient&&(this.instanceClient.options.logger.warn("Sign in called but there is already a user signed in. Signing out the current user."),this.signOut()),this.instanceClient=new B({...e,courierUserAgentName:this.courierUserAgentName,courierUserAgentVersion:this.courierUserAgentVersion}),this.notifyAuthenticationListeners({userId:e.userId})}signOut(){var e,t;null==(t=null==(e=this.instanceClient)?void 0:e.inbox.socket)||t.close(),this.instanceClient=void 0,this.notifyAuthenticationListeners({userId:void 0})}addAuthenticationListener(e){var t;null==(t=this.instanceClient)||t.options.logger.info("Adding authentication listener");const n=new j(e);return this.authenticationListeners.push(n),n}removeAuthenticationListener(e){var t;null==(t=this.instanceClient)||t.options.logger.info("Removing authentication listener"),this.authenticationListeners=this.authenticationListeners.filter((t=>t!==e))}notifyAuthenticationListeners(e){this.authenticationListeners.forEach((t=>t.callback(e)))}};n(z,"instance");let J=z;e.BrandClient=R,e.Courier=J,e.CourierClient=B,e.DEFAULT_COURIER_API_URLS=o,e.EU_COURIER_API_URLS=r,e.InboxClient=q,e.InboxMessageEvent=d,e.ListClient=_,e.PreferenceClient=L,e.TokenClient=O,e.TrackingClient=N,e.getCourierApiUrls=c,e.getCourierApiUrlsForRegion=a,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
|
|
2
2
|
//# sourceMappingURL=index.js.map
|