@trycourier/courier-js 2.1.3 ā 3.0.0
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/dist/client/courier-client.d.ts +2 -2
- package/dist/client/inbox-client.d.ts +34 -1
- package/dist/client/list-client.d.ts +2 -2
- package/dist/client/preference-client.d.ts +3 -3
- package/dist/client/token-client.d.ts +1 -1
- package/dist/client/tracking-client.d.ts +15 -7
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +129 -56
- package/dist/index.mjs.map +1 -1
- package/dist/types/inbox.d.ts +6 -0
- package/dist/utils/request.d.ts +1 -0
- package/package.json +1 -1
|
@@ -11,7 +11,7 @@ import { CourierUserAgent } from '../utils/courier-user-agent';
|
|
|
11
11
|
export interface CourierProps {
|
|
12
12
|
/** User ID for the client. Normally matches the UID in your system */
|
|
13
13
|
userId: string;
|
|
14
|
-
/** JWT token for authentication: More info at https://www.courier.com/docs/reference/
|
|
14
|
+
/** JWT token for authentication: More info at https://www.courier.com/docs/api-reference/authentication/create-jwt */
|
|
15
15
|
jwt?: string;
|
|
16
16
|
/** Public API key for testing (use JWTs in prod) */
|
|
17
17
|
publicApiKey?: string;
|
|
@@ -39,7 +39,7 @@ export interface CourierProps {
|
|
|
39
39
|
courierUserAgentVersion?: string;
|
|
40
40
|
}
|
|
41
41
|
export interface CourierClientOptions {
|
|
42
|
-
/** JWT token for authentication: More info at https://www.courier.com/docs/reference/
|
|
42
|
+
/** JWT token for authentication: More info at https://www.courier.com/docs/api-reference/authentication/create-jwt */
|
|
43
43
|
readonly jwt?: string;
|
|
44
44
|
/** Public API key for testing (use JWTs in prod) */
|
|
45
45
|
readonly publicApiKey?: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CourierInboxSocket } from '../socket/courier-inbox-socket';
|
|
2
|
-
import { CourierGetInboxMessagesResponse } from '../types/inbox';
|
|
2
|
+
import { CourierGetInboxMessagesQueryFilter, CourierGetInboxMessagesResponse } from '../types/inbox';
|
|
3
3
|
import { Client } from './client';
|
|
4
4
|
import { CourierClientOptions } from './courier-client';
|
|
5
5
|
export declare class InboxClient extends Client {
|
|
@@ -14,12 +14,22 @@ export declare class InboxClient extends Client {
|
|
|
14
14
|
getMessages(props?: {
|
|
15
15
|
paginationLimit?: number;
|
|
16
16
|
startCursor?: string;
|
|
17
|
+
filter?: CourierGetInboxMessagesQueryFilter;
|
|
17
18
|
}): Promise<CourierGetInboxMessagesResponse>;
|
|
19
|
+
/**
|
|
20
|
+
* Get unread counts for multiple filters in a single query.
|
|
21
|
+
*
|
|
22
|
+
* @param filtersMap - Map of dataset ID to filter
|
|
23
|
+
* @returns Promise resolving to map of dataset ID to unread count
|
|
24
|
+
*/
|
|
25
|
+
getUnreadCounts(filtersMap: Record<string, CourierGetInboxMessagesQueryFilter>): Promise<Record<string, number>>;
|
|
18
26
|
/**
|
|
19
27
|
* Get paginated archived messages
|
|
20
28
|
* @param paginationLimit - Number of messages to return per page (default: 24)
|
|
21
29
|
* @param startCursor - Cursor for pagination
|
|
22
30
|
* @returns Promise resolving to paginated archived messages response
|
|
31
|
+
*
|
|
32
|
+
* @deprecated - replace usages with {@link InboxClient.getMessages}, passing the filter `{ archived: true }`
|
|
23
33
|
*/
|
|
24
34
|
getArchivedMessages(props?: {
|
|
25
35
|
paginationLimit?: number;
|
|
@@ -93,4 +103,27 @@ export declare class InboxClient extends Client {
|
|
|
93
103
|
* Archive all read messages.
|
|
94
104
|
*/
|
|
95
105
|
archiveAll(): Promise<void>;
|
|
106
|
+
/**
|
|
107
|
+
* Create FilterParamsInput for the given filters.
|
|
108
|
+
*
|
|
109
|
+
* @param filter - the filtering options to include in the output
|
|
110
|
+
* @returns the FilterParamsInput to pass to a GraphQL query for messages
|
|
111
|
+
*/
|
|
112
|
+
private createFilterParams;
|
|
113
|
+
/**
|
|
114
|
+
* Create FilterParamsInput for the unread message count.
|
|
115
|
+
*
|
|
116
|
+
* The status: "unread" filter is only added if status is unset. This is because:
|
|
117
|
+
* - If status is "unread", the params already include the filter that would be added.
|
|
118
|
+
* - If status is "read", the unread count for the dataset would be a different set
|
|
119
|
+
* of messages rather than a count of the unread subset.
|
|
120
|
+
*/
|
|
121
|
+
private createUnreadCountFilterParams;
|
|
122
|
+
/**
|
|
123
|
+
* Sanitize dataset IDs for use as GraphQL identifiers.
|
|
124
|
+
*
|
|
125
|
+
* GraphQL identifiers must contain only alphanumerics/underscores and begin with a letter or underscore.
|
|
126
|
+
* https://spec.graphql.org/draft/#sec-Names
|
|
127
|
+
*/
|
|
128
|
+
private static sanitizeGraphQLIdentifier;
|
|
96
129
|
}
|
|
@@ -4,7 +4,7 @@ export declare class ListClient extends Client {
|
|
|
4
4
|
* Subscribe a user to a list
|
|
5
5
|
* @param listId - The ID of the list to subscribe to
|
|
6
6
|
* @returns Promise resolving when subscription is complete
|
|
7
|
-
* @see https://www.courier.com/docs/reference/lists/
|
|
7
|
+
* @see https://www.courier.com/docs/api-reference/lists/subscribe-user-profile-to-list
|
|
8
8
|
*/
|
|
9
9
|
putSubscription(props: {
|
|
10
10
|
listId: string;
|
|
@@ -13,7 +13,7 @@ export declare class ListClient extends Client {
|
|
|
13
13
|
* Unsubscribe a user from a list
|
|
14
14
|
* @param listId - The ID of the list to unsubscribe from
|
|
15
15
|
* @returns Promise resolving when unsubscription is complete
|
|
16
|
-
* @see https://www.courier.com/docs/reference/lists/
|
|
16
|
+
* @see https://www.courier.com/docs/api-reference/lists/unsubscribe-user-profile-from-list
|
|
17
17
|
*/
|
|
18
18
|
deleteSubscription(props: {
|
|
19
19
|
listId: string;
|
|
@@ -6,7 +6,7 @@ export declare class PreferenceClient extends Client {
|
|
|
6
6
|
* Get all preferences for a user
|
|
7
7
|
* @param paginationCursor - Optional cursor for pagination
|
|
8
8
|
* @returns Promise resolving to user preferences
|
|
9
|
-
* @see https://www.courier.com/docs/reference/user-preferences/
|
|
9
|
+
* @see https://www.courier.com/docs/api-reference/user-preferences/get-user-preferences
|
|
10
10
|
*/
|
|
11
11
|
getUserPreferences(props?: {
|
|
12
12
|
paginationCursor?: string;
|
|
@@ -15,7 +15,7 @@ export declare class PreferenceClient extends Client {
|
|
|
15
15
|
* Get preferences for a specific topic
|
|
16
16
|
* @param topicId - The ID of the topic to get preferences for
|
|
17
17
|
* @returns Promise resolving to topic preferences
|
|
18
|
-
* @see https://www.courier.com/docs/reference/user-preferences/get-subscription-topic
|
|
18
|
+
* @see https://www.courier.com/docs/api-reference/user-preferences/get-user-subscription-topic
|
|
19
19
|
*/
|
|
20
20
|
getUserPreferenceTopic(props: {
|
|
21
21
|
topicId: string;
|
|
@@ -27,7 +27,7 @@ export declare class PreferenceClient extends Client {
|
|
|
27
27
|
* @param hasCustomRouting - Whether the topic has custom routing
|
|
28
28
|
* @param customRouting - The custom routing channels for the topic
|
|
29
29
|
* @returns Promise resolving when update is complete
|
|
30
|
-
* @see https://www.courier.com/docs/reference/user-preferences/update-subscription-topic
|
|
30
|
+
* @see https://www.courier.com/docs/api-reference/user-preferences/update-or-create-user-preferences-for-subscription-topic
|
|
31
31
|
*/
|
|
32
32
|
putUserPreferenceTopic(props: {
|
|
33
33
|
topicId: string;
|
|
@@ -6,7 +6,7 @@ export declare class TokenClient extends Client {
|
|
|
6
6
|
* @param token - The push notification token
|
|
7
7
|
* @param provider - The provider of the token
|
|
8
8
|
* @param device - The device information
|
|
9
|
-
* @see https://www.courier.com/docs/reference/
|
|
9
|
+
* @see https://www.courier.com/docs/api-reference/device-tokens/add-single-token-to-user
|
|
10
10
|
*/
|
|
11
11
|
putUserToken(props: {
|
|
12
12
|
token: string;
|
|
@@ -2,15 +2,23 @@ import { CourierTrackingEvent } from '../types/tracking-event';
|
|
|
2
2
|
import { Client } from './client';
|
|
3
3
|
export declare class TrackingClient extends Client {
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* @
|
|
11
|
-
*
|
|
5
|
+
* @deprecated This method is deprecated and will be removed or changed significantly in a future release.
|
|
6
|
+
*
|
|
7
|
+
* Post an inbound courier event. This is typically used for tracking custom events
|
|
8
|
+
* related to a specific message in Courier.
|
|
9
|
+
*
|
|
10
|
+
* @param props - The event properties object containing:
|
|
11
|
+
* - `clientKey`: The client key associated with your Courier project.
|
|
12
|
+
* You can get your client key here: https://app.courier.com/settings/api-keys
|
|
13
|
+
* - `event`: The name of the event (e.g., "New Order Placed").
|
|
14
|
+
* - `messageId`: The unique ID of the message this event relates to.
|
|
15
|
+
* - `type`: The type of event. Only supported value: "track".
|
|
16
|
+
* - `properties`: (Optional) Additional custom properties for the event.
|
|
17
|
+
* @returns Promise resolving to an object containing the messageId.
|
|
18
|
+
* @see https://www.courier.com/docs/api-reference/inbound/courier-track-event
|
|
12
19
|
*/
|
|
13
20
|
postInboundCourier(props: {
|
|
21
|
+
clientKey: string;
|
|
14
22
|
event: string;
|
|
15
23
|
messageId: string;
|
|
16
24
|
type: 'track';
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { CourierApiUrls } 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';
|
|
5
|
-
import { CourierGetInboxMessageResponse, CourierGetInboxMessagesResponse, InboxMessage, InboxAction } from './types/inbox';
|
|
5
|
+
import { CourierGetInboxMessageResponse, CourierGetInboxMessagesResponse, InboxMessage, InboxAction, CourierGetInboxMessagesQueryFilter } from './types/inbox';
|
|
6
6
|
import { InboxMessageEvent, InboxMessageEventEnvelope } from './types/socket/protocol/messages';
|
|
7
7
|
import { CourierTrackingEvent } from './types/tracking-event';
|
|
8
8
|
import { CourierClient, CourierClientOptions, CourierProps } from './client/courier-client';
|
|
@@ -14,7 +14,7 @@ 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, CourierTrackingEvent, };
|
|
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
18
|
export { InboxMessageEvent, };
|
|
19
19
|
export { CourierClient, BrandClient, TokenClient, PreferenceClient, InboxClient, ListClient, TrackingClient, };
|
|
20
20
|
export type { AuthenticationListener };
|
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 T{constructor(t){this.options=t}}class f extends T{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 b=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(b),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!==b&&!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 A=w;class ${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 A{constructor(t){super(t),s(this,"pingIntervalId",null),s(this,"messageEventListeners",[]),s(this,"config",null),s(this,"pingTransactionManager",new $)}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(b,"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 T{constructor(t){super(t),s(this,"socket"),this.socket=new E(t)}async getMessages(t){const e=`\n query GetInboxMessages(\n $params: FilterParamsInput = { ${this.options.tenantId?`accountId: "${this.options.tenantId}"`:""} }\n $limit: Int = ${(null==t?void 0:t.paginationLimit)??24}\n $after: String ${(null==t?void 0:t.startCursor)?`= "${t.startCursor}"`:""}\n ) {\n count(params: $params)\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:e,headers:{"x-courier-user-id":this.options.userId,Authorization:`Bearer ${this.options.accessToken}`},url:this.options.apiUrls.inbox.graphql})}async getArchivedMessages(t){const e=`\n query GetInboxMessages(\n $params: FilterParamsInput = { ${this.options.tenantId?`accountId: "${this.options.tenantId}"`:""}, archived: true }\n $limit: Int = ${(null==t?void 0:t.paginationLimit)??24}\n $after: String ${(null==t?void 0:t.startCursor)?`= "${t.startCursor}"`:""}\n ) {\n count(params: $params)\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 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})}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})}}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 T{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 x extends T{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 P extends T{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 L extends T{async postInboundCourier(t){return await v({url:`${this.options.apiUrls.courier.rest}/inbound/courier`,options:this.options,method:"POST",headers:{Authorization:`Bearer ${this.options.accessToken}`},body:{...t,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 q{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 _=class t extends T{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 q(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 x(this.options),this.brands=new f(this.options),this.preferences=new U(this.options),this.inbox=new C(this.options),this.lists=new P(this.options),this.tracking=new L(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/reference/auth/issue-token\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(_,"COURIER_JS_NAME","courier-js"),s(_,"COURIER_JS_VERSION","2.1.3");let M=_;class O{constructor(t){s(this,"callback"),this.callback=t}remove(){B.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 M({...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 O(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 B=N;t.BrandClient=f,t.Courier=B,t.CourierClient=M,t.InboxClient=C,t.InboxMessageEvent=o,t.ListClient=P,t.PreferenceClient=U,t.TokenClient=x,t.TrackingClient=L,Object.defineProperty(t,Symbol.toStringTag,{value:"Module"})}));
|
|
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"})}));
|
|
2
2
|
//# sourceMappingURL=index.js.map
|