@trycourier/courier-js 2.0.10 → 2.0.12

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 CHANGED
@@ -5,7 +5,7 @@ The base API client and shared instance singleton for Courier's JavaScript Brows
5
5
  ## Installation
6
6
 
7
7
  ```sh
8
- npm install @trycourier/courier-js@beta
8
+ npm install @trycourier/courier-js
9
9
  ```
10
10
 
11
11
  ## Usage
@@ -21,7 +21,7 @@ const courierClient = new CourierClient({
21
21
  userId: 'some_user_id', // The user id for your user. This is usually the user id you maintain in your system for a user.
22
22
  jwt: 'ey...n0', // The access token associated with the user.
23
23
  tenantId: 'asdf', // [OPTIONAL] Allows you to scope a client to a specific tenant. If you didn't configure multi-tenant routing, you probably don't need this.
24
- showLogs: true, // [OPTIONAL] Shows debugging logs from the client. Defaults to process.env.NODE_ENV === 'development'.
24
+ showLogs: true, // [OPTIONAL] Shows debugging logs from the client. Defaults to `false`.
25
25
  });
26
26
  ```
27
27
 
@@ -34,7 +34,7 @@ Courier.shared.signIn({
34
34
  userId: 'some_user_id', // The user id for your user. This is usually the user id you maintain in your system for a user.
35
35
  jwt: 'ey...n0', // The access token associated with the user.
36
36
  tenantId: 'asdf', // [OPTIONAL] Allows you to scope a client to a specific tenant. If you didn't configure multi-tenant routing, you probably don't need this.
37
- showLogs: true, // [OPTIONAL] Shows debugging logs from the client. Defaults to process.env.NODE_ENV === 'development'.
37
+ showLogs: true, // [OPTIONAL] Shows debugging logs from the client. Defaults to `false`.
38
38
  });
39
39
 
40
40
  Courier.shared.signOut();
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,n=(t,n,s)=>((t,n,s)=>n in t?e(t,n,{enumerable:!0,configurable:!0,writable:!0,value:s}):t[n]=s)(t,"symbol"!=typeof n?n+"":n,s),s=(t=>(t.Subscribe="subscribe",t.Unsubscribe="unsubscribe",t.Pong="pong",t.Ping="ping",t.GetConfig="get-config",t))(s||{}),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){n(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 n="",s=crypto.getRandomValues(new Uint8Array(e|=0));for(;e--;)n+=t.ALPHABET[63&s[e]];return n}};n(c,"ALPHABET","useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict");let u=c;class h extends Error{constructor(t,e,n){super(e),this.code=t,this.type=n,this.name="CourierRequestError"}}function d(t,e,n,s){t.log(`\nšŸ“” New Courier ${n} Request: ${e}\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 l(t,e,n,s){t.log(`\nšŸ“” New Courier ${n} Response: ${e}\nStatus Code: ${s.status}\nResponse JSON: ${JSON.stringify(s.response,null,2)}\n `)}async function p(t){const e=t.validCodes??[200],n=t.options.showLogs?u.nanoid():void 0,s=new Request(t.url,{method:t.method,headers:{"Content-Type":"application/json",...t.headers},body:t.body?JSON.stringify(t.body):void 0});n&&d(t.options.logger,n,"HTTP",{url:s.url,method:s.method,headers:Object.fromEntries(s.headers.entries()),body:t.body});const i=await fetch(s);if(204===i.status)return;let o;try{o=await i.json()}catch(r){if(200===i.status)return;throw new h(i.status,"Failed to parse response as JSON","PARSE_ERROR")}if(n&&l(t.options.logger,n,"HTTP",{status:i.status,response:o}),!e.includes(i.status))throw new h(i.status,(null==o?void 0:o.message)||"Unknown Error",null==o?void 0:o.type);return o}async function g(t){const e=t.options.showLogs?u.nanoid():void 0;e&&d(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",...t.headers},body:JSON.stringify({query:t.query,variables:t.variables})});let s;try{s=await n.json()}catch(i){throw new h(n.status,"Failed to parse response as JSON","PARSE_ERROR")}if(e&&l(t.options.logger,e,"GraphQL",{status:n.status,response:s}),!n.ok)throw new h(n.status,(null==s?void 0:s.message)||"Unknown Error",null==s?void 0:s.type);return s}class m{constructor(t){this.options=t}}class I extends m{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 g({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 y=1e3,v=class t{constructor(t){n(this,"webSocket",null),n(this,"retryAttempt",0),n(this,"retryTimeoutId",null),n(this,"closeRequested",!1),n(this,"url"),n(this,"options"),this.url=t.apiUrls.inbox.webSocket,this.options=t}async connect(){var e,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==(e=this.webSocket)?void 0:e.readyState}'.`),Promise.resolve()):(this.clearRetryTimeout(),this.closeRequested=!1,new Promise(((e,n)=>{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(y),void(await this.retryConnection(1e3*e.retryAfter));this.onMessageReceived(e)}catch(n){null==(e=this.options.logger)||e.error("Error parsing socket message",n)}})),this.webSocket.addEventListener("close",(e=>{if(e.code!==y&&!this.closeRequested){const n=t.parseCloseEvent(e);n.retryAfterSeconds?this.retryConnection(1e3*n.retryAfterSeconds):this.retryConnection()}this.onClose(e)})),this.webSocket.addEventListener("error",(t=>{this.closeRequested||this.retryConnection(),this.onError(t),n(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 n=JSON.stringify(t);this.webSocket.send(n)}get userId(){return this.options.userId}get subTenantId(){return this.options.tenantId}get logger(){return this.options.logger}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,n=this.userId;return`${this.url}?auth=${t}&cid=${e}&iwpv=v1&userId=${n}`}static parseCloseEvent(e){if(null===e.reason||""===e.reason)return e;try{const n=JSON.parse(e.reason);if(!n[t.RETRY_AFTER_KEY])return e;const s=parseInt(n[t.RETRY_AFTER_KEY]);return Number.isNaN(s)||s<0?e:{...e,retryAfterSeconds:s}}catch(n){return e}}getBackoffTimeInMillis(){const e=t.BACKOFF_INTERVALS_IN_MILLIS[this.retryAttempt],n=e-e*t.BACKOFF_JITTER_FACTOR,s=e+e*t.BACKOFF_JITTER_FACTOR;return Math.floor(Math.random()*(s-n)+n)}async retryConnection(e){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>=t.MAX_RETRY_ATTEMPTS)return void(null==(s=this.logger)||s.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)}};n(v,"BACKOFF_JITTER_FACTOR",.5),n(v,"MAX_RETRY_ATTEMPTS",5),n(v,"BACKOFF_INTERVALS_IN_MILLIS",[3e4,6e4,12e4,24e4,48e4]),n(v,"RETRY_AFTER_KEY","Retry-After");let T=v;class b{constructor(t=10){n(this,"outstandingRequestsMap",new Map),n(this,"completedTransactionsQueue",[]),n(this,"completedTransactionsToKeep"),this.completedTransactionsToKeep=t}addOutstandingRequest(t,e){if(this.outstandingRequestsMap.has(t))throw new Error(`Transaction [${t}] already has an outstanding request`);const n={transactionId:t,request:e,response:null,start:new Date,end:null};this.outstandingRequestsMap.set(t,n)}addResponse(t,e){const n=this.outstandingRequestsMap.get(t);if(void 0===n)throw new Error(`Transaction [${t}] does not have an outstanding request`);n.response=e,n.end=new Date,this.outstandingRequestsMap.delete(t),this.addCompletedTransaction(n)}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 f(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 w=class t extends T{constructor(t){super(t),n(this,"pingIntervalId",null),n(this,"messageEventListeners",[]),n(this,"config",null),n(this,"pingTransactionManager",new b)}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=f(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:s.Subscribe,data:t};this.send(e)}sendUnsubscribe(){const t={tid:u.nanoid(),action:s.Unsubscribe,data:{channel:this.userId}};this.send(t)}addMessageEventListener(t){this.messageEventListeners.push(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(y,"Max outstanding pings reached, retrying connection."),void this.retryConnection();const e={tid:u.nanoid(),action:s.Ping};this.send(e),this.pingTransactionManager.addOutstandingRequest(e.tid,e)}sendPong(t){const e={tid:t.tid,action:s.Pong};this.send(e)}sendGetConfig(){const t={tid:u.nanoid(),action:s.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(){this.messageEventListeners=[]}static isInboxMessageEvent(t){return Object.values(o).includes(t)}};n(w,"DEFAULT_PING_INTERVAL_MILLIS",6e4),n(w,"DEFAULT_MAX_OUTSTANDING_PINGS",3);let $=w;class A extends m{constructor(t){super(t),n(this,"socket"),this.socket=new $(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 g({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 g({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 g({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 `,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 g({options:this.options,query:e,headers:n,url:this.options.apiUrls.inbox.graphql})}async read(t){const e=`\n mutation TrackEvent {\n read(messageId: "${t.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 g({options:this.options,query:e,headers:n,url:this.options.apiUrls.inbox.graphql})}async unread(t){const e=`\n mutation TrackEvent {\n unread(messageId: "${t.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 g({options:this.options,query:e,headers:n,url:this.options.apiUrls.inbox.graphql})}async open(t){const e=`\n mutation TrackEvent {\n opened(messageId: "${t.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 g({options:this.options,query:e,headers:n,url:this.options.apiUrls.inbox.graphql})}async archive(t){const e=`\n mutation TrackEvent {\n archive(messageId: "${t.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 g({options:this.options,query:e,headers:n,url:this.options.apiUrls.inbox.graphql})}async unarchive(t){const e=`\n mutation TrackEvent {\n unarchive(messageId: "${t.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 g({options:this.options,query:e,headers:n,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 g({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 g({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 g({options:this.options,query:"\n mutation TrackEvent {\n archiveAll\n }\n ",headers:t,url:this.options.apiUrls.inbox.graphql})}}class k{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 R extends m{constructor(){super(...arguments),n(this,"transformer",new k)}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 n=await p({options:this.options,url:e,method:"GET",headers:{Authorization:`Bearer ${this.options.accessToken}`}});return{items:[...this.transformer.transform(n.items)],paging:n.paging}}async getUserPreferenceTopic(t){const e=await p({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 p({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 n=0;n<t.length;n++)e[n]=t.charCodeAt(n);return btoa(String.fromCharCode(...e))}(`${function(t){const e=atob(t),n=new Uint8Array(e.length);for(let s=0;s<e.length;s++)n[s]=e.charCodeAt(s);return String.fromCharCode(...n)}(t.clientKey)}#${this.options.userId}${this.options.tenantId?`#${this.options.tenantId}`:""}#false`)}`}}class E extends m{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 p({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 p({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 C extends m{async putSubscription(t){return await p({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 p({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 S extends m{async postInboundCourier(t){return await p({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 p({url:t.url,options:this.options,method:"POST",body:{event:t.event}})}}class x extends m{constructor(t){var e,s;const i=void 0!==t.showLogs?t.showLogs:"development"===process.env.NODE_ENV,o={...t,showLogs:i,apiUrls:t.apiUrls||r(),accessToken:t.jwt??t.publicApiKey};super({...o,logger:new a(o.showLogs),apiUrls:r(o.apiUrls)}),n(this,"tokens"),n(this,"brands"),n(this,"preferences"),n(this,"inbox"),n(this,"lists"),n(this,"tracking"),this.tokens=new E(this.options),this.brands=new I(this.options),this.preferences=new R(this.options),this.inbox=new A(this.options),this.lists=new C(this.options),this.tracking=new S(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==(e=this.options.logger)||e.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==(s=this.options.logger)||s.warn("Courier Warning: Both a JWT and a Public API Key were provided. The Public API Key will be ignored."))}}class P{constructor(t){n(this,"callback"),this.callback=t}remove(){L.shared.removeAuthenticationListener(this)}}const q=class t{constructor(){n(this,"id",u.nanoid()),n(this,"instanceClient"),n(this,"_paginationLimit",24),n(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());const e=t.connectionId??u.nanoid();this.instanceClient=new x({...t,connectionId:e}),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 n=new P(t);return this.authenticationListeners.push(n),n}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)))}};n(q,"instance");let L=q;t.BrandClient=I,t.Courier=L,t.CourierClient=x,t.InboxClient=A,t.InboxMessageEvent=o,t.ListClient=C,t.PreferenceClient=R,t.TokenClient=E,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,n=(t,n,s)=>((t,n,s)=>n in t?e(t,n,{enumerable:!0,configurable:!0,writable:!0,value:s}):t[n]=s)(t,"symbol"!=typeof n?n+"":n,s),s=(t=>(t.Subscribe="subscribe",t.Unsubscribe="unsubscribe",t.Pong="pong",t.Ping="ping",t.GetConfig="get-config",t))(s||{}),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){n(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 n="",s=crypto.getRandomValues(new Uint8Array(e|=0));for(;e--;)n+=t.ALPHABET[63&s[e]];return n}};n(c,"ALPHABET","useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict");let u=c;class h extends Error{constructor(t,e,n){super(e),this.code=t,this.type=n,this.name="CourierRequestError"}}function d(t,e,n,s){t.log(`\nšŸ“” New Courier ${n} Request: ${e}\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 l(t,e,n,s){t.log(`\nšŸ“” New Courier ${n} Response: ${e}\nStatus Code: ${s.status}\nResponse JSON: ${JSON.stringify(s.response,null,2)}\n `)}async function p(t){const e=t.validCodes??[200],n=t.options.showLogs?u.nanoid():void 0,s=new Request(t.url,{method:t.method,headers:{"Content-Type":"application/json",...t.headers},body:t.body?JSON.stringify(t.body):void 0});n&&d(t.options.logger,n,"HTTP",{url:s.url,method:s.method,headers:Object.fromEntries(s.headers.entries()),body:t.body});const i=await fetch(s);if(204===i.status)return;let o;try{o=await i.json()}catch(r){if(200===i.status)return;throw new h(i.status,"Failed to parse response as JSON","PARSE_ERROR")}if(n&&l(t.options.logger,n,"HTTP",{status:i.status,response:o}),!e.includes(i.status))throw new h(i.status,(null==o?void 0:o.message)||"Unknown Error",null==o?void 0:o.type);return o}async function g(t){const e=t.options.showLogs?u.nanoid():void 0;e&&d(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",...t.headers},body:JSON.stringify({query:t.query,variables:t.variables})});let s;try{s=await n.json()}catch(i){throw new h(n.status,"Failed to parse response as JSON","PARSE_ERROR")}if(e&&l(t.options.logger,e,"GraphQL",{status:n.status,response:s}),!n.ok)throw new h(n.status,(null==s?void 0:s.message)||"Unknown Error",null==s?void 0:s.type);return s}class m{constructor(t){this.options=t}}class I extends m{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 g({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 y=1e3,v=class t{constructor(t){n(this,"webSocket",null),n(this,"retryAttempt",0),n(this,"retryTimeoutId",null),n(this,"closeRequested",!1),n(this,"url"),n(this,"options"),this.url=t.apiUrls.inbox.webSocket,this.options=t}async connect(){var e,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==(e=this.webSocket)?void 0:e.readyState}'.`),Promise.resolve()):(this.clearRetryTimeout(),this.closeRequested=!1,new Promise(((e,n)=>{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(y),void(await this.retryConnection(1e3*e.retryAfter));this.onMessageReceived(e)}catch(n){null==(e=this.options.logger)||e.error("Error parsing socket message",n)}})),this.webSocket.addEventListener("close",(e=>{if(e.code!==y&&!this.closeRequested){const n=t.parseCloseEvent(e);n.retryAfterSeconds?this.retryConnection(1e3*n.retryAfterSeconds):this.retryConnection()}this.onClose(e)})),this.webSocket.addEventListener("error",(t=>{this.closeRequested||this.retryConnection(),this.onError(t),n(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 n=JSON.stringify(t);this.webSocket.send(n)}get userId(){return this.options.userId}get subTenantId(){return this.options.tenantId}get logger(){return this.options.logger}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,n=this.userId;return`${this.url}?auth=${t}&cid=${e}&iwpv=v1&userId=${n}`}static parseCloseEvent(e){if(null===e.reason||""===e.reason)return e;try{const n=JSON.parse(e.reason);if(!n[t.RETRY_AFTER_KEY])return e;const s=parseInt(n[t.RETRY_AFTER_KEY]);return Number.isNaN(s)||s<0?e:{...e,retryAfterSeconds:s}}catch(n){return e}}getBackoffTimeInMillis(){const e=t.BACKOFF_INTERVALS_IN_MILLIS[this.retryAttempt],n=e-e*t.BACKOFF_JITTER_FACTOR,s=e+e*t.BACKOFF_JITTER_FACTOR;return Math.floor(Math.random()*(s-n)+n)}async retryConnection(e){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>=t.MAX_RETRY_ATTEMPTS)return void(null==(s=this.logger)||s.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)}};n(v,"BACKOFF_JITTER_FACTOR",.5),n(v,"MAX_RETRY_ATTEMPTS",5),n(v,"BACKOFF_INTERVALS_IN_MILLIS",[3e4,6e4,12e4,24e4,48e4]),n(v,"RETRY_AFTER_KEY","Retry-After");let T=v;class b{constructor(t=10){n(this,"outstandingRequestsMap",new Map),n(this,"completedTransactionsQueue",[]),n(this,"completedTransactionsToKeep"),this.completedTransactionsToKeep=t}addOutstandingRequest(t,e){if(this.outstandingRequestsMap.has(t))throw new Error(`Transaction [${t}] already has an outstanding request`);const n={transactionId:t,request:e,response:null,start:new Date,end:null};this.outstandingRequestsMap.set(t,n)}addResponse(t,e){const n=this.outstandingRequestsMap.get(t);if(void 0===n)throw new Error(`Transaction [${t}] does not have an outstanding request`);n.response=e,n.end=new Date,this.outstandingRequestsMap.delete(t),this.addCompletedTransaction(n)}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 f(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 w=class t extends T{constructor(t){super(t),n(this,"pingIntervalId",null),n(this,"messageEventListeners",[]),n(this,"config",null),n(this,"pingTransactionManager",new b)}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=f(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:s.Subscribe,data:t};this.send(e)}sendUnsubscribe(){const t={tid:u.nanoid(),action:s.Unsubscribe,data:{channel:this.userId}};this.send(t)}addMessageEventListener(t){this.messageEventListeners.push(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(y,"Max outstanding pings reached, retrying connection."),void this.retryConnection();const e={tid:u.nanoid(),action:s.Ping};this.send(e),this.pingTransactionManager.addOutstandingRequest(e.tid,e)}sendPong(t){const e={tid:t.tid,action:s.Pong};this.send(e)}sendGetConfig(){const t={tid:u.nanoid(),action:s.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(){this.messageEventListeners=[]}static isInboxMessageEvent(t){return Object.values(o).includes(t)}};n(w,"DEFAULT_PING_INTERVAL_MILLIS",6e4),n(w,"DEFAULT_MAX_OUTSTANDING_PINGS",3);let $=w;class A extends m{constructor(t){super(t),n(this,"socket"),this.socket=new $(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 g({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 g({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 g({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 `,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 g({options:this.options,query:e,headers:n,url:this.options.apiUrls.inbox.graphql})}async read(t){const e=`\n mutation TrackEvent {\n read(messageId: "${t.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 g({options:this.options,query:e,headers:n,url:this.options.apiUrls.inbox.graphql})}async unread(t){const e=`\n mutation TrackEvent {\n unread(messageId: "${t.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 g({options:this.options,query:e,headers:n,url:this.options.apiUrls.inbox.graphql})}async open(t){const e=`\n mutation TrackEvent {\n opened(messageId: "${t.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 g({options:this.options,query:e,headers:n,url:this.options.apiUrls.inbox.graphql})}async archive(t){const e=`\n mutation TrackEvent {\n archive(messageId: "${t.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 g({options:this.options,query:e,headers:n,url:this.options.apiUrls.inbox.graphql})}async unarchive(t){const e=`\n mutation TrackEvent {\n unarchive(messageId: "${t.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 g({options:this.options,query:e,headers:n,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 g({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 g({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 g({options:this.options,query:"\n mutation TrackEvent {\n archiveAll\n }\n ",headers:t,url:this.options.apiUrls.inbox.graphql})}}class k{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 R extends m{constructor(){super(...arguments),n(this,"transformer",new k)}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 n=await p({options:this.options,url:e,method:"GET",headers:{Authorization:`Bearer ${this.options.accessToken}`}});return{items:[...this.transformer.transform(n.items)],paging:n.paging}}async getUserPreferenceTopic(t){const e=await p({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 p({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 n=0;n<t.length;n++)e[n]=t.charCodeAt(n);return btoa(String.fromCharCode(...e))}(`${function(t){const e=atob(t),n=new Uint8Array(e.length);for(let s=0;s<e.length;s++)n[s]=e.charCodeAt(s);return String.fromCharCode(...n)}(t.clientKey)}#${this.options.userId}${this.options.tenantId?`#${this.options.tenantId}`:""}#false`)}`}}class E extends m{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 p({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 p({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 C extends m{async putSubscription(t){return await p({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 p({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 S extends m{async postInboundCourier(t){return await p({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 p({url:t.url,options:this.options,method:"POST",body:{event:t.event}})}}class x extends m{constructor(t){var e,s;const i=void 0!==t.showLogs&&t.showLogs,o={...t,showLogs:i,apiUrls:t.apiUrls||r(),accessToken:t.jwt??t.publicApiKey};super({...o,logger:new a(o.showLogs),apiUrls:r(o.apiUrls)}),n(this,"tokens"),n(this,"brands"),n(this,"preferences"),n(this,"inbox"),n(this,"lists"),n(this,"tracking"),this.tokens=new E(this.options),this.brands=new I(this.options),this.preferences=new R(this.options),this.inbox=new A(this.options),this.lists=new C(this.options),this.tracking=new S(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==(e=this.options.logger)||e.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==(s=this.options.logger)||s.warn("Courier Warning: Both a JWT and a Public API Key were provided. The Public API Key will be ignored."))}}class P{constructor(t){n(this,"callback"),this.callback=t}remove(){L.shared.removeAuthenticationListener(this)}}const q=class t{constructor(){n(this,"id",u.nanoid()),n(this,"instanceClient"),n(this,"_paginationLimit",24),n(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());const e=t.connectionId??u.nanoid();this.instanceClient=new x({...t,connectionId:e}),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 n=new P(t);return this.authenticationListeners.push(n),n}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)))}};n(q,"instance");let L=q;t.BrandClient=I,t.Courier=L,t.CourierClient=x,t.InboxClient=A,t.InboxMessageEvent=o,t.ListClient=C,t.PreferenceClient=R,t.TokenClient=E,Object.defineProperty(t,Symbol.toStringTag,{value:"Module"})}));
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/types/socket/protocol/messages.ts","../src/types/courier-api-urls.ts","../src/utils/logger.ts","../src/utils/uuid.ts","../src/utils/request.ts","../src/client/client.ts","../src/client/brand-client.ts","../src/types/socket/protocol/errors.ts","../src/socket/courier-socket.ts","../src/socket/courier-inbox-transaction-manager.ts","../src/socket/inbox-message-utils.ts","../src/socket/courier-inbox-socket.ts","../src/client/inbox-client.ts","../src/types/preference.ts","../src/client/preference-client.ts","../src/utils/coding.ts","../src/client/token-client.ts","../src/client/list-client.ts","../src/client/tracking-client.ts","../src/client/courier-client.ts","../src/shared/authentication-listener.ts","../src/shared/courier.ts"],"sourcesContent":["import { InboxMessage } from \"../../inbox\";\n\n/** Client actions. */\nexport enum ClientAction {\n /** Subscribe to various events for a particular channel. */\n Subscribe = 'subscribe',\n\n /** Unsubscribe from a channel. */\n Unsubscribe = 'unsubscribe',\n\n /** Pong response to a ping message from the server. */\n Pong = 'pong',\n\n /** Ping the server to keep the connection alive. */\n Ping = 'ping',\n\n /** Get the current configuration. */\n GetConfig = 'get-config',\n}\n\n/** Client request envelope. */\nexport interface ClientMessageEnvelope {\n /**\n * Transaction ID.\n *\n * This is a UUID generated per-socket message.\n *\n * The server response should include the same transaction ID.\n */\n tid: string;\n\n /** Requested action for the server to perform. */\n action: ClientAction;\n\n /** Optional: Statistics describing past requests and/or client state. */\n stats?: Record<string, any>;\n\n /** Optional: Payload for the request, varying by action. */\n data?: Record<string, any>;\n}\n\nexport enum ServerAction {\n /** Ping message from the server. */\n Ping = 'ping',\n}\n\n/**\n * Server action envelope.\n *\n * This is a request for the client to perform an action and respond to the server.\n */\nexport interface ServerActionEnvelope {\n /** Transaction ID. */\n tid: string;\n\n /** Action from the server. */\n action: ServerAction;\n}\n\n/** Server response types. */\nexport enum ServerResponse {\n /** Response to an action request. */\n Ack = 'ack',\n\n /** Response to a ping request. */\n Pong = 'pong',\n}\n\n/**\n * Server response envelope.\n *\n * This is a response from the server to a {@link ClientAction} (ping, subscribe, get-config, etc.).\n */\nexport interface ServerResponseEnvelope {\n /** Transaction ID. */\n tid: string;\n\n /** Response from the server. */\n response: ServerResponse;\n\n /** Optional: Payload for the response, varying by response. */\n data?: Record<string, any>;\n}\n\n/** Message event types broadcast by the server. */\nexport enum InboxMessageEvent {\n NewMessage = 'message',\n Archive = 'archive',\n ArchiveAll = 'archive-all',\n ArchiveRead = 'archive-read',\n Clicked = 'clicked',\n MarkAllRead = 'mark-all-read',\n Opened = 'opened',\n Read = 'read',\n Unarchive = 'unarchive',\n Unopened = 'unopened',\n Unread = 'unread',\n}\n\n/** Envelope for an inbox message event. */\nexport interface InboxMessageEventEnvelope {\n /** Event type indicating a new message, or a mutation to one or more existing messages. */\n event: InboxMessageEvent;\n\n /**\n * Optional:Message ID.\n *\n * messageId is present for events that mutate a single message (e.g. read, unread, archive, etc.).\n */\n messageId?: string;\n\n /** Optional: Message data, varying by event.\n *\n * For {@link InboxMessageEvent.NewMessage}, this is an {@link InboxMessage}.\n * For other events this is undefined.\n */\n data?: InboxMessage;\n}\n\n/** Message sent by the server to indicate that the client should reconnect. */\nexport interface ReconnectMessage {\n /** Event type indicating a reconnection. */\n event: 'reconnect';\n\n /** Message describing the reason for the reconnection. */\n message: string;\n\n /** Seconds after which the client should retry the connection. */\n retryAfter: number;\n\n /** https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent/code */\n code: number;\n}\n\n/** Configuration for the client. */\nexport interface Config {\n /** The time interval in milliseconds between client ping messages to the server. */\n pingInterval: number;\n\n /**\n * Maximum number of outstanding pings before the client should\n * close the connection and retry connecting.\n */\n maxOutstandingPings: number;\n}\n\n/** Envelope for a config response. */\nexport interface ConfigResponseEnvelope {\n /** Transaction ID. */\n tid: string;\n\n response: 'config';\n\n /** Configuration data for the client. */\n data: Config;\n}\n\nexport type ServerMessage =\n | ConfigResponseEnvelope\n | InboxMessageEventEnvelope\n | ReconnectMessage\n | ServerActionEnvelope\n | ServerResponseEnvelope;\n","export interface CourierApiUrls {\n courier: {\n rest: string;\n graphql: string;\n },\n inbox: {\n graphql: string;\n webSocket: string;\n }\n}\n\nexport const getCourierApiUrls = (urls?: CourierApiUrls): CourierApiUrls => ({\n courier: {\n rest: urls?.courier.rest || 'https://api.courier.com',\n graphql: urls?.courier.graphql || 'https://api.courier.com/client/q',\n },\n inbox: {\n graphql: urls?.inbox.graphql || 'https://inbox.courier.com/q',\n webSocket: urls?.inbox.webSocket || 'wss://realtime.courier.io'\n }\n});","export class Logger {\n\n private readonly PREFIX = '[COURIER]';\n\n constructor(private readonly showLogs: boolean) { }\n\n public warn(message: string, ...args: any[]): void {\n if (this.showLogs) {\n console.warn(`${this.PREFIX} ${message}`, ...args);\n }\n }\n\n public log(message: string, ...args: any[]): void {\n if (this.showLogs) {\n console.log(`${this.PREFIX} ${message}`, ...args);\n }\n }\n\n public error(message: string, ...args: any[]): void {\n if (this.showLogs) {\n console.error(`${this.PREFIX} ${message}`, ...args);\n }\n }\n\n public debug(message: string, ...args: any[]): void {\n if (this.showLogs) {\n console.debug(`${this.PREFIX} ${message}`, ...args);\n }\n }\n\n public info(message: string, ...args: any[]): void {\n if (this.showLogs) {\n console.info(`${this.PREFIX} ${message}`, ...args);\n }\n }\n}\n","export class UUID {\n\n private static readonly ALPHABET = 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict';\n\n /**\n * nanoid\n * Copyright 2017 Andrey Sitnik <andrey@sitnik.ru>\n *\n * https://github.com/ai/nanoid/blob/main/LICENSE\n *\n * @param size - The size of the UUID to generate.\n * @returns A string representing the UUID.\n */\n static nanoid(size: number = 21): string {\n let id = '';\n let bytes = crypto.getRandomValues(new Uint8Array((size |= 0)));\n\n while (size--) {\n // Using the bitwise AND operator to \"cap\" the value of\n // the random byte from 255 to 63, in that way we can make sure\n // that the value will be a valid index for the \"chars\" string.\n id += UUID.ALPHABET[bytes[size] & 63]\n }\n return id;\n }\n\n}","import { CourierClientOptions } from \"../client/courier-client\";\nimport { Logger } from \"./logger\";\nimport { UUID } from \"./uuid\";\n\nexport class CourierRequestError extends Error {\n constructor(\n public code: number,\n message: string,\n public type?: string\n ) {\n super(message);\n this.name = 'CourierRequestError';\n }\n}\n\nfunction logRequest(logger: Logger, uid: string, type: 'HTTP' | 'GraphQL', data: {\n url: string;\n method?: string;\n headers: Record<string, string>;\n body?: any;\n query?: string;\n variables?: Record<string, any>;\n}) {\n logger.log(`\nšŸ“” New Courier ${type} Request: ${uid}\nURL: ${data.url}\n${data.method ? `Method: ${data.method}` : ''}\n${data.query ? `Query: ${data.query}` : ''}\n${data.variables ? `Variables: ${JSON.stringify(data.variables, null, 2)}` : ''}\nHeaders: ${JSON.stringify(data.headers, null, 2)}\nBody: ${data.body ? JSON.stringify(data.body, null, 2) : 'Empty'}\n `);\n}\n\nfunction logResponse(logger: Logger, uid: string, type: 'HTTP' | 'GraphQL', data: {\n status: number;\n response: any;\n}) {\n logger.log(`\nšŸ“” New Courier ${type} Response: ${uid}\nStatus Code: ${data.status}\nResponse JSON: ${JSON.stringify(data.response, null, 2)}\n `);\n}\n\nexport async function http(props: {\n url: string,\n options: CourierClientOptions,\n method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH',\n headers?: Record<string, string>,\n body?: any,\n validCodes?: number[]\n}): Promise<any> {\n const validCodes = props.validCodes ?? [200];\n const uid = props.options.showLogs ? UUID.nanoid() : undefined;\n\n // Create request\n const request = new Request(props.url, {\n method: props.method,\n headers: {\n 'Content-Type': 'application/json',\n ...props.headers\n },\n body: props.body ? JSON.stringify(props.body) : undefined\n });\n\n // Log request if enabled\n if (uid) {\n logRequest(props.options.logger, uid, 'HTTP', {\n url: request.url,\n method: request.method,\n headers: Object.fromEntries(request.headers.entries()),\n body: props.body\n });\n }\n\n // Perform request\n const response = await fetch(request);\n\n // Handle empty responses (like 204 No Content)\n if (response.status === 204) {\n return;\n }\n\n // Try to parse JSON response\n let data;\n try {\n data = await response.json();\n } catch (error) {\n\n // Weird fallback for only tracking url events :facepalm:\n if (response.status === 200) {\n return;\n }\n\n throw new CourierRequestError(\n response.status,\n 'Failed to parse response as JSON',\n 'PARSE_ERROR'\n );\n }\n\n // Log response if enabled\n if (uid) {\n logResponse(props.options.logger, uid, 'HTTP', {\n status: response.status,\n response: data\n });\n }\n\n // Handle invalid status codes\n if (!validCodes.includes(response.status)) {\n throw new CourierRequestError(\n response.status,\n data?.message || 'Unknown Error',\n data?.type\n );\n }\n\n return data;\n}\n\nexport async function graphql(props: {\n url: string,\n options: CourierClientOptions,\n headers: Record<string, string>,\n query: string,\n variables?: Record<string, any>\n}): Promise<any> {\n const uid = props.options.showLogs ? UUID.nanoid() : undefined;\n\n // Log request if enabled\n if (uid) {\n logRequest(props.options.logger, uid, 'GraphQL', {\n url: props.url,\n headers: props.headers,\n query: props.query,\n variables: props.variables\n });\n }\n\n const response = await fetch(props.url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...props.headers\n },\n body: JSON.stringify({\n query: props.query,\n variables: props.variables\n })\n });\n\n // Try to parse JSON response\n let data;\n try {\n data = await response.json();\n } catch (error) {\n throw new CourierRequestError(\n response.status,\n 'Failed to parse response as JSON',\n 'PARSE_ERROR'\n );\n }\n\n // Log response if enabled\n if (uid) {\n logResponse(props.options.logger, uid, 'GraphQL', {\n status: response.status,\n response: data\n });\n }\n\n if (!response.ok) {\n throw new CourierRequestError(\n response.status,\n data?.message || 'Unknown Error',\n data?.type\n );\n }\n\n return data;\n}\n","import { CourierClientOptions } from \"./courier-client\";\n\nexport class Client {\n\n constructor(public readonly options: CourierClientOptions) { }\n\n}\n","import { CourierBrand } from '../types/brands';\nimport { graphql } from '../utils/request';\nimport { Client } from './client';\n\nexport class BrandClient extends Client {\n\n /**\n * Get a brand by ID using GraphQL\n * @param brandId - The ID of the brand to retrieve\n * @returns Promise resolving to the requested brand\n */\n public async getBrand(props: { brandId: string }): Promise<CourierBrand> {\n const query = `\n query GetBrand {\n brand(brandId: \"${props.brandId}\") {\n settings {\n colors {\n primary\n secondary\n tertiary\n }\n inapp {\n borderRadius\n disableCourierFooter\n }\n }\n }\n }\n `;\n\n const json = await graphql({\n options: this.options,\n url: this.options.apiUrls.courier.graphql,\n headers: {\n 'x-courier-user-id': this.options.userId,\n 'x-courier-client-key': 'empty', // Empty for now. Will be removed in future.\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n query,\n variables: { brandId: props.brandId }\n });\n\n return json.data.brand as CourierBrand;\n }\n\n}\n","/**\n * Connection close code for non-error conditions.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent/code\n */\nexport const CLOSE_CODE_NORMAL_CLOSURE = 1000;\n\n/**\n * Courier-specific close event.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent\n */\nexport interface CourierCloseEvent extends CloseEvent {\n /** The number of seconds to wait before retrying the connection. */\n retryAfterSeconds?: number;\n}\n","import { CourierClientOptions } from \"../client/courier-client\";\nimport { CLOSE_CODE_NORMAL_CLOSURE, CourierCloseEvent } from \"../types/socket/protocol/errors\";\nimport { ServerMessage } from \"../types/socket/protocol/messages\";\nimport { Logger } from \"../utils/logger\";\nimport { INBOX_WIRE_PROTOCOL_VERSION } from \"./version\";\n\n/**\n * Abstract base class for Courier WebSocket implementations.\n *\n * The base class handles the connection and close events, as well as retry logic.\n * Application-specific logic should be implemented in the concrete classes.\n */\nexport abstract class CourierSocket {\n /**\n * The jitter factor for the backoff intervals.\n *\n * Backoff with jitter is calculated as a random value in the range:\n * [BACKOFF_INTERVAL - BACKOFF_JITTER_FACTOR * BACKOFF_INTERVAL,\n * BACKOFF_INTERVAL + BACKOFF_JITTER_FACTOR * BACKOFF_INTERVAL).\n */\n private static readonly BACKOFF_JITTER_FACTOR = 0.5;\n\n /**\n * The maximum number of retry attempts.\n */\n private static readonly MAX_RETRY_ATTEMPTS = 5;\n\n /**\n * Backoff intervals in milliseconds.\n *\n * Each represents an offset from the previous interval, rather than a\n * absolute offset from the initial request time.\n */\n private static readonly BACKOFF_INTERVALS_IN_MILLIS = [\n 30_000, // 30 seconds\n 60_000, // 1 minute\n 120_000, // 2 minutes\n 240_000, // 4 minutes\n 480_000, // 8 minutes\n ];\n\n /**\n * The key of the retry after time in the WebSocket close event reason.\n *\n * The Courier WebSocket server may send the close event reason in the following format:\n *\n * ```json\n * {\n * \"Retry-After\": \"10\" // The retry after time in seconds\n * }\n * ```\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent/reason\n */\n private static readonly RETRY_AFTER_KEY = 'Retry-After';\n\n /** The WebSocket instance, which may be null if the connection is not established. */\n private webSocket: WebSocket | null = null;\n\n /** The number of connection retry attempts so far, reset after a successful connection. */\n private retryAttempt: number = 0;\n\n /** The timeout ID for the current connectionretry attempt, reset when we attempt to connect. */\n private retryTimeoutId: number | null = null;\n\n /**\n * Flag indicating the application initiated a {@link CourierSocket#close} call.\n *\n * An application-initiated close may look like an abnormal closure (code 1006)\n * if it occurs before the connection is established. We differentiate to\n * prevent retrying the connection when the socket is closed intentionally.\n */\n private closeRequested: boolean = false;\n\n private readonly url: string;\n private readonly options: CourierClientOptions;\n\n constructor(\n options: CourierClientOptions\n ) {\n this.url = options.apiUrls.inbox.webSocket;\n this.options = options;\n }\n\n /**\n * Connects to the Courier WebSocket server.\n *\n * If the connection is already established, this is a no-op.\n *\n * @returns A promise that resolves when the connection is established or rejects if the connection could not be established.\n */\n public async connect(): Promise<void> {\n if (this.isConnecting || this.isOpen) {\n this.options.logger?.info(`Attempted to open a WebSocket connection, but one already exists in state '${this.webSocket?.readyState}'.`);\n\n // This isn't necessarily an error (the result is a no-op), so we resolve the promise.\n return Promise.resolve();\n }\n\n // If we're in the process of retrying, clear the timeout to prevent further retries.\n this.clearRetryTimeout();\n\n // Reset the close requested flag when we attempt to connect.\n this.closeRequested = false;\n\n return new Promise((resolve, reject) => {\n this.webSocket = new WebSocket(this.getWebSocketUrl());\n\n this.webSocket.addEventListener('open', (event: Event) => {\n // Reset the retry attempt counter when the connection is established.\n this.retryAttempt = 0;\n\n this.onOpen(event);\n\n // Resolve the promise when the WebSocket is opened (i.e. the connection is established)\n resolve();\n });\n\n this.webSocket.addEventListener('message', async (event: MessageEvent) => {\n try {\n const json = JSON.parse(event.data) as ServerMessage;\n if ('event' in json && json.event === 'reconnect') {\n this.close(CLOSE_CODE_NORMAL_CLOSURE);\n await this.retryConnection(json.retryAfter * 1000);\n return;\n }\n\n this.onMessageReceived(json)\n } catch (error) {\n this.options.logger?.error('Error parsing socket message', error);\n }\n });\n\n this.webSocket.addEventListener('close', (event: CloseEvent) => {\n // Close events are fired when the connection is closed either normally or abnormally.\n //\n // The 'close' event triggers a retry if the 'close' is:\n // 1) not a normal closure and,\n // 2) the application did not request the close (see CourierSocket#closeRequested)\n if (event.code !== CLOSE_CODE_NORMAL_CLOSURE && !this.closeRequested) {\n const courierCloseEvent = CourierSocket.parseCloseEvent(event);\n\n if (courierCloseEvent.retryAfterSeconds) {\n this.retryConnection(courierCloseEvent.retryAfterSeconds * 1000);\n } else {\n this.retryConnection();\n }\n }\n\n this.onClose(event);\n });\n\n this.webSocket.addEventListener('error', (event: Event) => {\n // If the closure was requested by the application, don't retry the connection.\n // The error event may be fired for a normal closure if it occurs before the connection is established.\n if (!this.closeRequested) {\n this.retryConnection();\n }\n\n this.onError(event);\n\n // If the HTTP Upgrade request fails, the WebSocket API fires an error event,\n // so we reject the promise to indicate that the connection could not be established.\n reject(event);\n });\n });\n }\n\n /**\n * Closes the WebSocket connection.\n *\n * See {@link https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close} for more details.\n *\n * @param code The WebSocket close code. Defaults to {@link CLOSE_CODE_NORMAL_CLOSURE}.\n * @param reason The WebSocket close reason.\n */\n public close(code = CLOSE_CODE_NORMAL_CLOSURE, reason?: string): void {\n if (this.webSocket === null) {\n return;\n }\n\n this.closeRequested = true;\n\n // Cancel any pending retries and reset the retry attempt counter.\n this.clearRetryTimeout();\n this.retryAttempt = 0;\n\n this.webSocket.close(code, reason);\n\n }\n\n /**\n * Sends a message to the Courier WebSocket server.\n *\n * @param message The message to send. The message will be serialized to a JSON string.\n */\n public send(message: Record<string, any>): void {\n if (this.webSocket === null || this.isConnecting) {\n this.options.logger?.info('Attempted to send a message, but the WebSocket is not yet open.');\n return;\n }\n\n const json = JSON.stringify(message);\n this.webSocket.send(json);\n }\n\n protected get userId(): string {\n return this.options.userId;\n }\n\n /** The sub-tenant ID, if specified by the user. */\n protected get subTenantId(): string | undefined {\n return this.options.tenantId;\n }\n\n protected get logger(): Logger | undefined {\n return this.options.logger;\n }\n\n /**\n * Called when the WebSocket connection is established with the Courier WebSocket server.\n *\n * @param event The WebSocket open event.\n */\n public abstract onOpen(event: Event): Promise<void>;\n\n /**\n * Called when a message is received from the Courier WebSocket server.\n *\n * @param data The message received.\n */\n public abstract onMessageReceived(data: ServerMessage): Promise<void>;\n\n /**\n * Called when the WebSocket connection is closed.\n *\n * @param event The WebSocket close event.\n */\n public abstract onClose(event: CloseEvent): Promise<void>;\n\n /**\n * Called when an error occurs on the WebSocket connection.\n *\n * @param event The WebSocket error event.\n */\n public abstract onError(event: Event): Promise<void>;\n\n /**\n * Whether the WebSocket connection is currently being established.\n */\n public get isConnecting(): boolean {\n return this.webSocket !== null && this.webSocket.readyState === WebSocket.CONNECTING;\n }\n\n /**\n * Whether the WebSocket connection is currently open.\n */\n public get isOpen(): boolean {\n return this.webSocket !== null && this.webSocket.readyState === WebSocket.OPEN;\n }\n\n /**\n * Constructs the WebSocket URL for the Courier WebSocket server using context\n * from the {@link CourierClientOptions} passed to the constructor.\n *\n * @returns The WebSocket URL\n */\n private getWebSocketUrl(): string {\n const accessToken = this.options.accessToken;\n const connectionId = this.options.connectionId;\n const userId = this.userId;\n\n return `${this.url}?auth=${accessToken}&cid=${connectionId}&iwpv=${INBOX_WIRE_PROTOCOL_VERSION}&userId=${userId}`;\n }\n\n /**\n * Parses the Retry-After time from the WebSocket close event reason,\n * and returns a new {@link CourierCloseEvent} with the retry after time in seconds\n * if present.\n *\n * The Courier WebSocket server may send the close event reason in the following format:\n *\n * ```json\n * {\n * \"Retry-After\": \"10\" // The retry after time in seconds\n * }\n * ```\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent/reason\n *\n * @param closeEvent The WebSocket close event.\n * @returns The WebSocket close event with the retry after time in seconds.\n */\n private static parseCloseEvent(closeEvent: CloseEvent): CourierCloseEvent {\n if (closeEvent.reason === null || closeEvent.reason === '') {\n return closeEvent;\n }\n\n try {\n const jsonReason = JSON.parse(closeEvent.reason);\n if (!jsonReason[CourierSocket.RETRY_AFTER_KEY]) {\n return closeEvent;\n }\n\n const retryAfterSeconds = parseInt(jsonReason[CourierSocket.RETRY_AFTER_KEY]);\n if (Number.isNaN(retryAfterSeconds) || retryAfterSeconds < 0) {\n return closeEvent;\n }\n\n return {\n ...closeEvent,\n retryAfterSeconds,\n };\n } catch (error) {\n return closeEvent;\n }\n }\n\n /**\n * Calculates the retry backoff time in milliseconds based on the current retry attempt.\n */\n private getBackoffTimeInMillis(): number {\n const backoffIntervalInMillis = CourierSocket.BACKOFF_INTERVALS_IN_MILLIS[this.retryAttempt];\n const lowerBound = backoffIntervalInMillis - (backoffIntervalInMillis * CourierSocket.BACKOFF_JITTER_FACTOR);\n const upperBound = backoffIntervalInMillis + (backoffIntervalInMillis * CourierSocket.BACKOFF_JITTER_FACTOR);\n\n return Math.floor(Math.random() * (upperBound - lowerBound) + lowerBound);\n }\n\n /**\n * Retries the connection to the Courier WebSocket server after\n * either {@param suggestedBackoffTimeInMillis} or a random backoff time\n * calculated using {@link getBackoffTimeInMillis}.\n *\n * @param suggestedBackoffTimeInMillis The suggested backoff time in milliseconds.\n * @returns A promise that resolves when the connection is established or rejects if the connection could not be established.\n */\n protected async retryConnection(suggestedBackoffTimeInMillis?: number): Promise<void> {\n if (this.retryTimeoutId !== null) {\n this.logger?.debug('Skipping retry attempt because a previous retry is already scheduled.');\n return;\n }\n\n if (this.retryAttempt >= CourierSocket.MAX_RETRY_ATTEMPTS) {\n this.logger?.error(`Max retry attempts (${CourierSocket.MAX_RETRY_ATTEMPTS}) reached.`);\n return;\n }\n\n const backoffTimeInMillis = suggestedBackoffTimeInMillis ?? this.getBackoffTimeInMillis();\n this.retryTimeoutId = window.setTimeout(async () => {\n try {\n await this.connect();\n } catch (error) {\n // connect() will retry if applicable\n }\n }, backoffTimeInMillis);\n this.logger?.debug(`Retrying connection in ${Math.floor(backoffTimeInMillis / 1000)}s. Retry attempt ${this.retryAttempt + 1} of ${CourierSocket.MAX_RETRY_ATTEMPTS}.`);\n\n this.retryAttempt++;\n }\n\n /**\n * Clears the retry timeout if it exists.\n */\n private clearRetryTimeout(): void {\n if (this.retryTimeoutId !== null) {\n window.clearTimeout(this.retryTimeoutId);\n this.retryTimeoutId = null;\n }\n }\n}\n","import { ClientMessageEnvelope, ServerMessage } from \"../types/socket/protocol/messages\";\n\nexport class TransactionManager {\n /**\n * The map of <transactionId, Transaction> representing outstanding requests.\n */\n private readonly outstandingRequestsMap: Map<string, Transaction> = new Map();\n\n /**\n * The queue of completed requests. This is a FIFO queue of the last N completed requests,\n * where N is {@link completedTransactionsToKeep}.\n */\n private readonly completedTransactionsQueue: Transaction[] = [];\n\n /**\n * Number of completed requests to keep in memory.\n */\n private readonly completedTransactionsToKeep: number;\n\n constructor(completedTransactionsToKeep: number = 10) {\n this.completedTransactionsToKeep = completedTransactionsToKeep;\n }\n\n public addOutstandingRequest(transactionId: string, request: ClientMessageEnvelope): void {\n const isOutstanding = this.outstandingRequestsMap.has(transactionId);\n if (isOutstanding) {\n throw new Error(`Transaction [${transactionId}] already has an outstanding request`);\n }\n\n const transaction: Transaction = {\n transactionId,\n request,\n response: null,\n start: new Date(),\n end: null,\n };\n\n this.outstandingRequestsMap.set(transactionId, transaction);\n }\n\n public addResponse(transactionId: string, response: ServerMessage): void {\n const transaction = this.outstandingRequestsMap.get(transactionId);\n if (transaction === undefined) {\n throw new Error(`Transaction [${transactionId}] does not have an outstanding request`);\n }\n\n transaction.response = response;\n transaction.end = new Date();\n\n // Move the transaction from the outstanding requests to the completed requests.\n this.outstandingRequestsMap.delete(transactionId);\n this.addCompletedTransaction(transaction);\n }\n\n public get outstandingRequests(): Transaction[] {\n return Array.from(this.outstandingRequestsMap.values());\n }\n\n public get completedTransactions(): Transaction[] {\n return this.completedTransactionsQueue;\n }\n\n public clearOutstandingRequests(): void {\n this.outstandingRequestsMap.clear();\n }\n\n /**\n * Adds a completed request to the queue.\n *\n * If the number of completed requests exceeds the maximum number of completed requests to keep,\n * remove the oldest completed request.\n */\n private addCompletedTransaction(transaction: Transaction): void {\n this.completedTransactionsQueue.push(transaction);\n\n if (this.completedTransactionsQueue.length > this.completedTransactionsToKeep) {\n this.completedTransactionsQueue.shift();\n }\n }\n}\n\ninterface Transaction {\n /**\n * The transaction ID.\n */\n transactionId: string;\n\n /**\n * The request to the server.\n */\n request: ClientMessageEnvelope;\n\n /**\n * The response to the request.\n *\n * The response is null until the request is completed.\n */\n response: ServerMessage | null;\n\n /**\n * The start time of the transaction.\n */\n start: Date;\n\n /**\n * The end time of the transaction.\n */\n end: Date | null;\n}\n","import { InboxMessage } from \"../types/inbox\";\nimport { InboxMessageEvent, InboxMessageEventEnvelope } from \"../types/socket/protocol/messages\";\n\n/**\n * Ensure the `created` timestamp is set for a new message.\n *\n * New messages received from the WebSocket may not have a created time,\n * until they are retrieved from the GraphQL API.\n *\n * @param envelope - The envelope containing the message event.\n * @returns The envelope with the created time set.\n */\nfunction ensureCreatedTime(envelope: InboxMessageEventEnvelope): InboxMessageEventEnvelope {\n if (envelope.event === InboxMessageEvent.NewMessage) {\n const message = envelope.data as InboxMessage;\n\n if (!message.created) {\n message.created = new Date().toISOString();\n }\n\n return {\n ...envelope,\n data: message,\n };\n }\n\n return envelope;\n}\n\n/**\n * Apply fixes to a message event envelope.\n *\n * @param envelope - The envelope containing the message event.\n * @returns The envelope with the fixes applied.\n */\nexport function fixMessageEventEnvelope(envelope: InboxMessageEventEnvelope): InboxMessageEventEnvelope {\n // Apply any fixes to the message event envelope.\n return ensureCreatedTime(envelope);\n}\n","import { CourierClientOptions } from '../client/courier-client';\nimport { ClientAction, ClientMessageEnvelope, Config, ConfigResponseEnvelope, InboxMessageEvent, InboxMessageEventEnvelope, ServerAction, ServerActionEnvelope, ServerMessage, ServerResponseEnvelope } from '../types/socket/protocol/messages';\nimport { CLOSE_CODE_NORMAL_CLOSURE } from '../types/socket/protocol/errors';\nimport { UUID } from '../utils/uuid';\nimport { CourierSocket } from './courier-socket';\nimport { TransactionManager } from './courier-inbox-transaction-manager';\nimport { fixMessageEventEnvelope } from './inbox-message-utils';\n\n/** Application-layer implementation of the Courier WebSocket API for Inbox messages. */\nexport class CourierInboxSocket extends CourierSocket {\n /**\n * The default interval in milliseconds at which to send a ping message to the server\n * if no other message has been received from the server.\n *\n * Fallback when the server does not provide a config.\n */\n private static readonly DEFAULT_PING_INTERVAL_MILLIS = 60_000; // 1 minute\n\n /**\n * The default maximum number of outstanding pings before the client should\n * close the connection and retry connecting.\n *\n * Fallback when the server does not provide a config.\n */\n private static readonly DEFAULT_MAX_OUTSTANDING_PINGS = 3;\n\n /**\n * The interval ID for the ping interval.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/setInterval\n */\n private pingIntervalId: number | null = null;\n\n /**\n * The list of message event listeners, called when a message event is received\n * from the Courier WebSocket server.\n */\n private messageEventListeners: ((message: InboxMessageEventEnvelope) => void)[] = [];\n\n /** Server-provided configuration for the client. */\n private config: Config | null = null;\n\n /**\n * The transaction manager, used to track outstanding requests and responses.\n */\n private readonly pingTransactionManager: TransactionManager = new TransactionManager();\n\n constructor(options: CourierClientOptions) {\n super(options);\n }\n\n public onOpen(_: Event): Promise<void> {\n // Clear any outstanding pings from the previous connection before starting to ping.\n this.pingTransactionManager.clearOutstandingRequests();\n this.restartPingInterval();\n\n // Send a request for the client's configuration.\n this.sendGetConfig();\n\n // Subscribe to all events for the user.\n this.sendSubscribe();\n\n return Promise.resolve();\n }\n\n public onMessageReceived(data: ServerMessage): Promise<void> {\n // ServerActionEnvelope\n // Respond to pings.\n if ('action' in data && data.action === ServerAction.Ping) {\n const envelope: ServerActionEnvelope = data as ServerActionEnvelope;\n this.sendPong(envelope);\n }\n\n // ServerResponseEnvelope\n // Track pongs.\n if ('response' in data && data.response === 'pong') {\n const envelope: ServerResponseEnvelope = data as ServerResponseEnvelope;\n\n // Keep track of the pong response and clear out any outstanding pings.\n // We only need to keep track of the most recent missed pings.\n this.pingTransactionManager.addResponse(envelope.tid, envelope);\n this.pingTransactionManager.clearOutstandingRequests();\n }\n\n // ConfigResponseEnvelope\n // Update the client's config.\n if ('response' in data && data.response === 'config') {\n const envelope: ConfigResponseEnvelope = data as ConfigResponseEnvelope;\n this.setConfig(envelope.data);\n }\n\n // InboxMessageEventEnvelope\n // Handle message events, calling all registered listeners.\n if ('event' in data && CourierInboxSocket.isInboxMessageEvent(data.event)) {\n const envelope: InboxMessageEventEnvelope = data as InboxMessageEventEnvelope;\n const fixedEnvelope = fixMessageEventEnvelope(envelope);\n for (const listener of this.messageEventListeners) {\n listener(fixedEnvelope);\n }\n }\n\n // Restart the ping interval if a message is received from the server.\n this.restartPingInterval();\n\n return Promise.resolve();\n }\n\n public onClose(_: CloseEvent): Promise<void> {\n // Cancel scheduled pings.\n this.clearPingInterval();\n\n // Remove any message event listeners.\n this.clearMessageEventListeners();\n\n // Clear any outstanding pings.\n this.pingTransactionManager.clearOutstandingRequests();\n\n return Promise.resolve();\n }\n\n public onError(_: Event): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Sends a subscribe message to the server.\n *\n * Subscribes to all events for the user.\n */\n public sendSubscribe(): void {\n const data: Record<string, any> = {\n channel: this.userId,\n event: '*',\n };\n\n // Set accountId to the sub-tenant ID if it is specified.\n if (this.subTenantId) {\n data.accountId = this.subTenantId;\n }\n\n const envelope: ClientMessageEnvelope = {\n tid: UUID.nanoid(),\n action: ClientAction.Subscribe,\n data,\n };\n\n this.send(envelope);\n }\n\n /**\n * Sends an unsubscribe message to the server.\n *\n * Unsubscribes from all events for the user.\n */\n public sendUnsubscribe(): void {\n const envelope: ClientMessageEnvelope = {\n tid: UUID.nanoid(),\n action: ClientAction.Unsubscribe,\n data: {\n channel: this.userId,\n },\n };\n\n this.send(envelope);\n }\n\n /**\n * Adds a message event listener, called when a message event is received\n * from the Courier WebSocket server.\n *\n * @param listener The listener function\n */\n public addMessageEventListener(listener: (message: InboxMessageEventEnvelope) => void): void {\n this.messageEventListeners.push(listener);\n }\n\n /**\n * Send a ping message to the server.\n *\n * ping/pong is implemented at the application layer since the browser's\n * WebSocket implementation does not support control-level ping/pong.\n */\n private sendPing(): void {\n if (this.pingTransactionManager.outstandingRequests.length >= this.maxOutstandingPings) {\n this.logger?.debug('Max outstanding pings reached, retrying connection.');\n this.close(CLOSE_CODE_NORMAL_CLOSURE, 'Max outstanding pings reached, retrying connection.');\n this.retryConnection();\n\n return;\n }\n\n const envelope: ClientMessageEnvelope = {\n tid: UUID.nanoid(),\n action: ClientAction.Ping,\n };\n\n this.send(envelope);\n this.pingTransactionManager.addOutstandingRequest(envelope.tid, envelope);\n }\n\n /**\n * Send a pong response to the server.\n *\n * ping/pong is implemented at the application layer since the browser's\n * WebSocket implementation does not support control-level ping/pong.\n */\n private sendPong(incomingMessage: ServerActionEnvelope): void {\n const response: ClientMessageEnvelope = {\n tid: incomingMessage.tid,\n action: ClientAction.Pong,\n };\n\n this.send(response);\n }\n\n /**\n * Send a request for the client's configuration.\n */\n private sendGetConfig(): void {\n const envelope: ClientMessageEnvelope = {\n tid: UUID.nanoid(),\n action: ClientAction.GetConfig,\n };\n\n this.send(envelope);\n }\n\n /**\n * Restart the ping interval, clearing the previous interval if it exists.\n */\n private restartPingInterval(): void {\n this.clearPingInterval();\n\n this.pingIntervalId = window.setInterval(() => {\n this.sendPing();\n }, this.pingInterval);\n }\n\n private clearPingInterval(): void {\n if (this.pingIntervalId) {\n window.clearInterval(this.pingIntervalId);\n }\n }\n\n private get pingInterval(): number {\n if (this.config) {\n // Server-provided ping interval is in seconds.\n return this.config.pingInterval * 1000;\n }\n\n return CourierInboxSocket.DEFAULT_PING_INTERVAL_MILLIS;\n }\n\n private get maxOutstandingPings(): number {\n if (this.config) {\n return this.config.maxOutstandingPings;\n }\n\n return CourierInboxSocket.DEFAULT_MAX_OUTSTANDING_PINGS;\n }\n\n private setConfig(config: Config): void {\n this.config = config;\n }\n\n /**\n * Removes all message event listeners.\n */\n private clearMessageEventListeners(): void {\n this.messageEventListeners = [];\n }\n\n private static isInboxMessageEvent(event: string): event is InboxMessageEvent {\n return Object.values(InboxMessageEvent).includes(event as InboxMessageEvent);\n }\n}\n","import { CourierInboxSocket } from '../socket/courier-inbox-socket';\nimport { CourierGetInboxMessagesResponse } from '../types/inbox';\nimport { graphql } from '../utils/request';\nimport { Client } from './client';\nimport { CourierClientOptions } from './courier-client';\n\nexport class InboxClient extends Client {\n\n readonly socket: CourierInboxSocket;\n\n constructor(options: CourierClientOptions) {\n super(options);\n this.socket = new CourierInboxSocket(options);\n }\n\n /**\n * Get paginated messages\n * @param paginationLimit - Number of messages to return per page (default: 24)\n * @param startCursor - Cursor for pagination\n * @returns Promise resolving to paginated messages response\n */\n public async getMessages(props?: { paginationLimit?: number; startCursor?: string; }): Promise<CourierGetInboxMessagesResponse> {\n const query = `\n query GetInboxMessages(\n $params: FilterParamsInput = { ${this.options.tenantId ? `accountId: \"${this.options.tenantId}\"` : ''} }\n $limit: Int = ${props?.paginationLimit ?? 24}\n $after: String ${props?.startCursor ? `= \"${props.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 `;\n\n return await graphql({\n options: this.options,\n query,\n headers: {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Get paginated archived messages\n * @param paginationLimit - Number of messages to return per page (default: 24)\n * @param startCursor - Cursor for pagination\n * @returns Promise resolving to paginated archived messages response\n */\n public async getArchivedMessages(props?: { paginationLimit?: number; startCursor?: string; }): Promise<CourierGetInboxMessagesResponse> {\n const query = `\n query GetInboxMessages(\n $params: FilterParamsInput = { ${this.options.tenantId ? `accountId: \"${this.options.tenantId}\"` : ''}, archived: true }\n $limit: Int = ${props?.paginationLimit ?? 24}\n $after: String ${props?.startCursor ? `= \"${props.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 `;\n\n return graphql({\n options: this.options,\n query,\n headers: {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Get unread message count\n * @returns Promise resolving to number of unread messages\n */\n public async getUnreadMessageCount(): Promise<number> {\n const query = `\n query GetMessages {\n count(params: { status: \"unread\" ${this.options.tenantId ? `, accountId: \"${this.options.tenantId}\"` : ''} })\n }\n `;\n\n const response = await graphql({\n options: this.options,\n query,\n headers: {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n url: this.options.apiUrls.inbox.graphql,\n });\n\n return response.data?.count ?? 0;\n }\n\n /**\n * Track a click event\n * @param messageId - ID of the message\n * @param trackingId - ID for tracking the click\n * @returns Promise resolving when click is tracked\n */\n public async click(props: { messageId: string, trackingId: string }): Promise<void> {\n const query = `\n mutation TrackEvent {\n clicked(messageId: \"${props.messageId}\", trackingId: \"${props.trackingId}\")\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Mark a message as read\n * @param messageId - ID of the message to mark as read\n * @returns Promise resolving when message is marked as read\n */\n public async read(props: { messageId: string }): Promise<void> {\n const query = `\n mutation TrackEvent {\n read(messageId: \"${props.messageId}\")\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Mark a message as unread\n * @param messageId - ID of the message to mark as unread\n * @returns Promise resolving when message is marked as unread\n */\n public async unread(props: { messageId: string }): Promise<void> {\n const query = `\n mutation TrackEvent {\n unread(messageId: \"${props.messageId}\")\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Mark a message as opened\n * @param messageId - ID of the message to mark as opened\n * @returns Promise resolving when message is marked as opened\n */\n public async open(props: { messageId: string }): Promise<void> {\n const query = `\n mutation TrackEvent {\n opened(messageId: \"${props.messageId}\")\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Archive a message\n * @param messageId - ID of the message to archive\n * @returns Promise resolving when message is archived\n */\n public async archive(props: { messageId: string }): Promise<void> {\n const query = `\n mutation TrackEvent {\n archive(messageId: \"${props.messageId}\")\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Unarchive a message\n * @param messageId - ID of the message to unarchive\n * @returns Promise resolving when message is unarchived\n */\n public async unarchive(props: { messageId: string }): Promise<void> {\n const query = `\n mutation TrackEvent {\n unarchive(messageId: \"${props.messageId}\")\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Mark all messages as read\n * @returns Promise resolving when all messages are marked as read\n */\n public async readAll(): Promise<void> {\n const query = `\n mutation TrackEvent {\n markAllRead\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Archive all read messages.\n */\n public async archiveRead(): Promise<void> {\n const query = `\n mutation TrackEvent {\n archiveRead\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Archive all read messages.\n */\n public async archiveAll(): Promise<void> {\n const query = `\n mutation TrackEvent {\n archiveAll\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n}\n","export type CourierUserPreferencesStatus = 'OPTED_IN' | 'OPTED_OUT' | 'REQUIRED' | 'UNKNOWN';\n\nexport type CourierUserPreferencesChannel = 'direct_message' | 'inbox' | 'email' | 'push' | 'sms' | 'webhook' | 'unknown';\n\nexport interface CourierUserPreferencesPaging {\n cursor?: string;\n more: boolean;\n}\n\nexport interface CourierUserPreferencesTopic {\n topicId: string;\n topicName: string;\n sectionId: string;\n sectionName: string;\n status: CourierUserPreferencesStatus;\n defaultStatus: CourierUserPreferencesStatus;\n hasCustomRouting: boolean;\n customRouting: CourierUserPreferencesChannel[];\n}\n\nexport interface CourierUserPreferences {\n items: CourierUserPreferencesTopic[];\n paging: CourierUserPreferencesPaging;\n}\n\nexport interface CourierUserPreferencesTopicResponse {\n topic: CourierUserPreferencesTopic;\n}\n\nexport class PreferenceTransformer {\n /**\n * Transforms a single API response item to the CourierUserPreferencesTopic type\n * @param item - The API response item\n * @returns A CourierUserPreferencesTopic object\n */\n transformItem(item: any): CourierUserPreferencesTopic {\n return {\n topicId: item.topic_id,\n topicName: item.topic_name,\n sectionId: item.section_id,\n sectionName: item.section_name,\n status: item.status,\n defaultStatus: item.default_status,\n hasCustomRouting: item.has_custom_routing,\n customRouting: item.custom_routing || []\n };\n }\n\n /**\n * Transforms an array of API response items to CourierUserPreferencesTopic objects\n * @param items - The API response items\n * @returns A generator of CourierUserPreferencesTopic objects\n */\n *transform(items: any[]): Generator<CourierUserPreferencesTopic> {\n for (const item of items) {\n yield this.transformItem(item);\n }\n }\n}","import { CourierUserPreferences, CourierUserPreferencesChannel, CourierUserPreferencesStatus, CourierUserPreferencesTopic, CourierUserPreferencesTopicResponse, PreferenceTransformer } from '../types/preference';\nimport { decode, encode } from '../utils/coding';\nimport { http } from '../utils/request';\nimport { Client } from './client';\n\nexport class PreferenceClient extends Client {\n private transformer = new PreferenceTransformer();\n\n /**\n * Get all preferences for a user\n * @param paginationCursor - Optional cursor for pagination\n * @returns Promise resolving to user preferences\n * @see https://www.courier.com/docs/reference/user-preferences/list-all-user-preferences\n */\n public async getUserPreferences(props?: { paginationCursor?: string; }): Promise<CourierUserPreferences> {\n let url = `${this.options.apiUrls.courier.rest}/users/${this.options.userId}/preferences`;\n\n if (props?.paginationCursor) {\n url += `?cursor=${props.paginationCursor}`;\n }\n\n const json = await http({\n options: this.options,\n url,\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n });\n\n const data = json as CourierUserPreferences;\n\n return {\n items: [...this.transformer.transform(data.items)],\n paging: data.paging\n };\n }\n\n /**\n * Get preferences for a specific topic\n * @param topicId - The ID of the topic to get preferences for\n * @returns Promise resolving to topic preferences\n * @see https://www.courier.com/docs/reference/user-preferences/get-subscription-topic-preferences\n */\n public async getUserPreferenceTopic(props: { topicId: string; }): Promise<CourierUserPreferencesTopic> {\n\n const json = await http({\n options: this.options,\n url: `${this.options.apiUrls.courier.rest}/users/${this.options.userId}/preferences/${props.topicId}`,\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n });\n\n const res = json as CourierUserPreferencesTopicResponse;\n return this.transformer.transformItem(res.topic);\n }\n\n /**\n * Update preferences for a specific topic\n * @param topicId - The ID of the topic to update preferences for\n * @param status - The new status for the topic\n * @param hasCustomRouting - Whether the topic has custom routing\n * @param customRouting - The custom routing channels for the topic\n * @returns Promise resolving when update is complete\n * @see https://www.courier.com/docs/reference/user-preferences/update-subscription-topic-preferences\n */\n public async putUserPreferenceTopic(props: { topicId: string; status: CourierUserPreferencesStatus; hasCustomRouting: boolean; customRouting: CourierUserPreferencesChannel[]; }): Promise<void> {\n\n const payload = {\n topic: {\n status: props.status,\n has_custom_routing: props.hasCustomRouting,\n custom_routing: props.customRouting,\n },\n };\n\n await http({\n options: this.options,\n url: `${this.options.apiUrls.courier.rest}/users/${this.options.userId}/preferences/${props.topicId}`,\n method: 'PUT',\n headers: {\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n body: payload,\n });\n }\n\n /**\n * Get the notification center URL\n * @param clientKey - The client key to use for the URL\n * @returns The notification center URL\n */\n public getNotificationCenterUrl(props: {\n clientKey: string;\n }): string {\n const rootTenantId = decode(props.clientKey);\n const url = encode(`${rootTenantId}#${this.options.userId}${this.options.tenantId ? `#${this.options.tenantId}` : \"\"}#${false}`);\n return `https://view.notificationcenter.app/p/${url}`;\n }\n\n}\n","export function decode(clientKey: string): string {\n const binaryString = atob(clientKey);\n const bytes = new Uint8Array(binaryString.length);\n\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n\n return String.fromCharCode(...bytes);\n}\n\nexport function encode(key: string): string {\n const bytes = new Uint8Array(key.length);\n\n for (let i = 0; i < key.length; i++) {\n bytes[i] = key.charCodeAt(i);\n }\n\n return btoa(String.fromCharCode(...bytes));\n}","import { CourierDevice } from '../types/token';\nimport { http } from '../utils/request';\nimport { Client } from './client';\n\nexport class TokenClient extends Client {\n\n /**\n * Store a push notification token for a user\n * @param token - The push notification token\n * @param provider - The provider of the token\n * @param device - The device information\n * @see https://www.courier.com/docs/reference/token-management/put-token\n */\n public async putUserToken(props: {\n token: string;\n provider: string;\n device?: CourierDevice;\n }): Promise<void> {\n const payload = {\n provider_key: props.provider,\n ...(props.device && {\n device: {\n app_id: props.device.appId,\n ad_id: props.device.adId,\n device_id: props.device.deviceId,\n platform: props.device.platform,\n manufacturer: props.device.manufacturer,\n model: props.device.model\n }\n })\n };\n\n await http({\n options: this.options,\n url: `${this.options.apiUrls.courier.rest}/users/${this.options.userId}/tokens/${props.token}`,\n method: 'PUT',\n headers: {\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n body: payload,\n validCodes: [200, 204]\n });\n }\n\n /**\n * Delete a push notification token for a user\n * @param token - The push notification token\n * @returns Promise resolving when token is deleted\n */\n public async deleteUserToken(props: {\n token: string;\n }): Promise<void> {\n await http({\n options: this.options,\n url: `${this.options.apiUrls.courier.rest}/users/${this.options.userId}/tokens/${props.token}`,\n method: 'DELETE',\n headers: {\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n validCodes: [200, 204]\n });\n }\n}\n","import { http } from '../utils/request';\nimport { Client } from './client';\n\nexport class ListClient extends Client {\n\n /**\n * Subscribe a user to a list\n * @param listId - The ID of the list to subscribe to\n * @returns Promise resolving when subscription is complete\n * @see https://www.courier.com/docs/reference/lists/recipient-subscribe\n */\n public async putSubscription(props: { listId: string; }): Promise<void> {\n return await http({\n url: `${this.options.apiUrls.courier.rest}/lists/${props.listId}/subscriptions/${this.options.userId}`,\n options: this.options,\n method: 'PUT',\n headers: {\n Authorization: `Bearer ${this.options.accessToken}`,\n },\n });\n }\n\n /**\n * Unsubscribe a user from a list\n * @param listId - The ID of the list to unsubscribe from\n * @returns Promise resolving when unsubscription is complete\n * @see https://www.courier.com/docs/reference/lists/delete-subscription\n */\n public async deleteSubscription(props: { listId: string; }): Promise<void> {\n return await http({\n url: `${this.options.apiUrls.courier.rest}/lists/${props.listId}/subscriptions/${this.options.userId}`,\n options: this.options,\n method: 'DELETE',\n headers: {\n Authorization: `Bearer ${this.options.accessToken}`,\n },\n });\n }\n\n}\n","import { CourierTrackingEvent } from '../types/tracking-event';\nimport { http } from '../utils/request';\nimport { Client } from './client';\n\nexport class TrackingClient extends Client {\n\n /**\n * Post an inbound courier event\n * @param event - The event type: Example: \"New Order Placed\"\n * @param messageId - The message ID\n * @param type - The type of event: Available options: \"track\"\n * @param properties - The properties of the event\n * @returns Promise resolving to the message ID\n * @see https://www.courier.com/docs/reference/inbound/courier-track-event\n */\n public async postInboundCourier(props: {\n event: string;\n messageId: string;\n type: 'track';\n properties?: Record<string, any>;\n }): Promise<{ messageId: string }> {\n return await http({\n url: `${this.options.apiUrls.courier.rest}/inbound/courier`,\n options: this.options,\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.options.accessToken}`,\n },\n body: {\n ...props,\n userId: this.options.userId\n },\n validCodes: [200, 202]\n });\n }\n\n /**\n * Post a tracking URL event\n * These urls are found in messages sent from Courier\n * @param url - The URL to post the event to\n * @param event - The event type: Available options: \"click\", \"open\", \"unsubscribe\"\n * @returns Promise resolving when the event is posted\n */\n public async postTrackingUrl(props: {\n url: string;\n event: CourierTrackingEvent;\n }): Promise<void> {\n return await http({\n url: props.url,\n options: this.options,\n method: 'POST',\n body: {\n event: props.event\n }\n });\n }\n\n}\n","\nimport { CourierApiUrls, getCourierApiUrls } from '../types/courier-api-urls';\nimport { Logger } from '../utils/logger';\nimport { BrandClient } from './brand-client';\nimport { InboxClient } from './inbox-client';\nimport { PreferenceClient } from './preference-client';\nimport { TokenClient } from './token-client';\nimport { Client } from './client';\nimport { ListClient } from './list-client';\nimport { TrackingClient } from './tracking-client';\n\nexport interface CourierProps {\n /** User ID for the client. Normally matches the UID in your system */\n userId: string;\n\n /** JWT token for authentication: More info at https://www.courier.com/docs/reference/auth/issue-token */\n jwt?: string;\n\n /** Public API key for testing (use JWTs in prod) */\n publicApiKey?: string;\n\n /** Inbox Websocket connection ID */\n connectionId?: string;\n\n /** Tenant ID. Used for multi-tenant apps */\n tenantId?: string;\n\n /** Flag to control logging. Logs are prefixed with [COURIER]. */\n showLogs?: boolean;\n\n /** Custom API URLs */\n apiUrls?: CourierApiUrls;\n}\n\nexport interface CourierClientOptions {\n /** JWT token for authentication: More info at https://www.courier.com/docs/reference/auth/issue-token */\n readonly jwt?: string;\n\n /** Public API key for testing (use JWTs in prod) */\n readonly publicApiKey?: string;\n\n /** User ID for the client. Normally matches the UID in your system */\n readonly userId: string;\n\n /** Inbox Websocket connection ID */\n readonly connectionId?: string;\n\n /** Tenant ID. Used for multi-tenant apps */\n readonly tenantId?: string;\n\n /** Flag to control logging. Logs are prefixed with [COURIER]. */\n readonly showLogs?: boolean;\n\n /** Combined authentication token (jwt or publicApiKey) */\n readonly accessToken?: string;\n\n /** Logger instance */\n readonly logger: Logger;\n\n /** Final API URLs configuration */\n readonly apiUrls: CourierApiUrls;\n}\n\nexport class CourierClient extends Client {\n public readonly tokens: TokenClient;\n public readonly brands: BrandClient;\n public readonly preferences: PreferenceClient;\n public readonly inbox: InboxClient;\n public readonly lists: ListClient;\n public readonly tracking: TrackingClient;\n\n constructor(props: CourierProps) {\n // Determine if we should show logs based on props or environment\n const showLogs = props.showLogs !== undefined ? props.showLogs : process.env.NODE_ENV === 'development';\n\n // Setup base options with default values\n const baseOptions = {\n ...props,\n showLogs,\n apiUrls: props.apiUrls || getCourierApiUrls(),\n accessToken: props.jwt ?? props.publicApiKey\n };\n\n // Initialize base client with logger and URLs\n super({\n ...baseOptions,\n logger: new Logger(baseOptions.showLogs),\n apiUrls: getCourierApiUrls(baseOptions.apiUrls)\n });\n\n // Initialize all subclients with the configured options\n this.tokens = new TokenClient(this.options);\n this.brands = new BrandClient(this.options);\n this.preferences = new PreferenceClient(this.options);\n this.inbox = new InboxClient(this.options);\n this.lists = new ListClient(this.options);\n this.tracking = new TrackingClient(this.options);\n\n // Warn if no authentication method is provided\n if (!this.options.jwt && !this.options.publicApiKey) {\n this.options.logger.warn('Courier Client initialized with no authentication method. Please provide a JWT or public API key.');\n }\n\n // Warn about using public API key in production\n if (this.options.publicApiKey) {\n this.options.logger?.warn(\n 'Courier Warning: Public API Keys are for testing only. Please use JWTs for production.\\n' +\n 'You can generate a JWT with this endpoint: https://www.courier.com/docs/reference/auth/issue-token\\n' +\n 'This endpoint should be called from your backend server, not the SDK.'\n );\n }\n\n // Warn if both authentication methods are provided\n if (this.options.jwt && this.options.publicApiKey) {\n this.options.logger?.warn(\n 'Courier Warning: Both a JWT and a Public API Key were provided. The Public API Key will be ignored.'\n );\n }\n }\n}\n","import { Courier } from \"./courier\";\n\nexport class AuthenticationListener {\n readonly callback: (props: { userId?: string }) => void;\n\n constructor(callback: (props: { userId?: string }) => void) {\n this.callback = callback;\n }\n\n public remove(): void {\n Courier.shared.removeAuthenticationListener(this);\n }\n\n}","import { CourierClient, CourierProps } from \"../client/courier-client\";\nimport { AuthenticationListener } from '../shared/authentication-listener';\nimport { UUID } from \"../utils/uuid\";\n\n/**\n * Courier is a singleton class that manages a shared Courier client instance and other resources.\n * UI components will automatically syncronize with this instance.\n * If you only need to call the Courier api, you should consider using the CourierClient directly.\n */\nexport class Courier {\n\n /**\n * The unique identifier for the Courier instance\n */\n public readonly id = UUID.nanoid();\n\n /**\n * The shared Courier instance\n */\n private static instance: Courier;\n\n /**\n * The Courier client instance\n */\n private instanceClient?: CourierClient;\n\n /**\n * The pagination limit (min: 1, max: 100)\n */\n private _paginationLimit = 24;\n\n public get paginationLimit(): number {\n return this._paginationLimit;\n }\n\n public set paginationLimit(value: number) {\n this._paginationLimit = Math.min(Math.max(value, 1), 100);\n }\n\n /**\n * Get the Courier client instance\n * @returns The Courier client instance or undefined if not signed in\n */\n public get client(): CourierClient | undefined {\n return this.instanceClient;\n }\n\n /**\n * The authentication listeners\n */\n private authenticationListeners: AuthenticationListener[] = [];\n\n /**\n * Get the shared Courier instance\n * @returns The shared Courier instance\n */\n public static get shared(): Courier {\n if (!Courier.instance) {\n Courier.instance = new Courier();\n }\n return Courier.instance;\n }\n\n /**\n * Sign in to Courier\n * @param options - The options for the Courier client\n */\n public signIn(props: CourierProps) {\n // Sign out any existing user.\n if (this.instanceClient) {\n this.instanceClient.options.logger.warn('Sign in called but there is already a user signed in. Signing out the current user.');\n this.signOut();\n }\n\n // Create a new client.\n const connectionId = props.connectionId ?? UUID.nanoid();\n this.instanceClient = new CourierClient({ ...props, connectionId });\n this.notifyAuthenticationListeners({ userId: props.userId });\n }\n\n /**\n * Sign out of Courier\n */\n public signOut() {\n // Close the socket client.\n this.instanceClient?.inbox.socket?.close();\n\n // Clear the client.\n this.instanceClient = undefined;\n this.notifyAuthenticationListeners({ userId: undefined });\n }\n\n /**\n * Register a callback to be notified of authentication state changes\n * @param callback - Function to be called when authentication state changes\n * @returns AuthenticationListener instance that can be used to remove the listener\n */\n public addAuthenticationListener(callback: (props: { userId?: string }) => void): AuthenticationListener {\n this.instanceClient?.options.logger.info('Adding authentication listener');\n const listener = new AuthenticationListener(callback);\n this.authenticationListeners.push(listener);\n return listener;\n }\n\n /**\n * Unregister an authentication state change listener\n * @param listener - The AuthenticationListener instance to remove\n */\n public removeAuthenticationListener(listener: AuthenticationListener) {\n this.instanceClient?.options.logger.info('Removing authentication listener');\n this.authenticationListeners = this.authenticationListeners.filter(l => l !== listener);\n }\n\n /**\n * Notify all authentication listeners\n * @param props - The props to notify the listeners with\n */\n private notifyAuthenticationListeners(props: { userId?: string }) {\n this.authenticationListeners.forEach(listener => listener.callback(props));\n }\n\n}\n"],"names":["ClientAction","ServerAction","InboxMessageEvent","getCourierApiUrls","urls","courier","rest","graphql","inbox","webSocket","Logger","constructor","showLogs","__publicField","this","warn","message","args","console","PREFIX","log","error","debug","info","_UUID","nanoid","size","id","bytes","crypto","getRandomValues","Uint8Array","ALPHABET","UUID","CourierRequestError","Error","code","type","super","name","logRequest","logger","uid","data","url","method","query","variables","JSON","stringify","headers","body","logResponse","status","response","async","http","props","validCodes","options","request","Request","Object","fromEntries","entries","fetch","json","includes","ok","Client","BrandClient","getBrand","brandId","apiUrls","userId","Authorization","accessToken","brand","CLOSE_CODE_NORMAL_CLOSURE","_CourierSocket","connect","isConnecting","isOpen","_b","_a","readyState","Promise","resolve","clearRetryTimeout","closeRequested","reject","WebSocket","getWebSocketUrl","addEventListener","event","retryAttempt","onOpen","parse","close","retryConnection","retryAfter","onMessageReceived","courierCloseEvent","parseCloseEvent","retryAfterSeconds","onClose","onError","reason","send","subTenantId","tenantId","CONNECTING","OPEN","connectionId","closeEvent","jsonReason","RETRY_AFTER_KEY","parseInt","Number","isNaN","getBackoffTimeInMillis","backoffIntervalInMillis","BACKOFF_INTERVALS_IN_MILLIS","lowerBound","BACKOFF_JITTER_FACTOR","upperBound","Math","floor","random","suggestedBackoffTimeInMillis","retryTimeoutId","MAX_RETRY_ATTEMPTS","backoffTimeInMillis","window","setTimeout","_c","clearTimeout","CourierSocket","TransactionManager","completedTransactionsToKeep","Map","addOutstandingRequest","transactionId","outstandingRequestsMap","has","transaction","start","Date","end","set","addResponse","get","delete","addCompletedTransaction","outstandingRequests","Array","from","values","completedTransactions","completedTransactionsQueue","clearOutstandingRequests","clear","push","length","shift","fixMessageEventEnvelope","envelope","NewMessage","created","toISOString","ensureCreatedTime","_CourierInboxSocket","_","pingTransactionManager","restartPingInterval","sendGetConfig","sendSubscribe","action","Ping","sendPong","tid","setConfig","isInboxMessageEvent","fixedEnvelope","listener","messageEventListeners","clearPingInterval","clearMessageEventListeners","channel","accountId","Subscribe","sendUnsubscribe","Unsubscribe","addMessageEventListener","sendPing","maxOutstandingPings","incomingMessage","Pong","GetConfig","pingIntervalId","setInterval","pingInterval","clearInterval","config","DEFAULT_PING_INTERVAL_MILLIS","DEFAULT_MAX_OUTSTANDING_PINGS","CourierInboxSocket","InboxClient","socket","getMessages","paginationLimit","startCursor","getArchivedMessages","getUnreadMessageCount","count","click","messageId","trackingId","read","unread","open","archive","unarchive","readAll","archiveRead","archiveAll","PreferenceTransformer","transformItem","item","topicId","topic_id","topicName","topic_name","sectionId","section_id","sectionName","section_name","defaultStatus","default_status","hasCustomRouting","has_custom_routing","customRouting","custom_routing","transform","items","PreferenceClient","arguments","getUserPreferences","paginationCursor","transformer","paging","getUserPreferenceTopic","res","topic","putUserPreferenceTopic","payload","getNotificationCenterUrl","key","i","charCodeAt","btoa","String","fromCharCode","encode","clientKey","binaryString","atob","decode","TokenClient","putUserToken","provider_key","provider","device","app_id","appId","ad_id","adId","device_id","deviceId","platform","manufacturer","model","token","deleteUserToken","ListClient","putSubscription","listId","deleteSubscription","TrackingClient","postInboundCourier","postTrackingUrl","CourierClient","process","env","NODE_ENV","baseOptions","jwt","publicApiKey","tokens","brands","preferences","lists","tracking","AuthenticationListener","callback","remove","Courier","shared","removeAuthenticationListener","_Courier","_paginationLimit","value","min","max","client","instanceClient","instance","signIn","signOut","notifyAuthenticationListeners","addAuthenticationListener","authenticationListeners","filter","l","forEach"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/types/socket/protocol/messages.ts","../src/types/courier-api-urls.ts","../src/utils/logger.ts","../src/utils/uuid.ts","../src/utils/request.ts","../src/client/client.ts","../src/client/brand-client.ts","../src/types/socket/protocol/errors.ts","../src/socket/courier-socket.ts","../src/socket/courier-inbox-transaction-manager.ts","../src/socket/inbox-message-utils.ts","../src/socket/courier-inbox-socket.ts","../src/client/inbox-client.ts","../src/types/preference.ts","../src/client/preference-client.ts","../src/utils/coding.ts","../src/client/token-client.ts","../src/client/list-client.ts","../src/client/tracking-client.ts","../src/client/courier-client.ts","../src/shared/authentication-listener.ts","../src/shared/courier.ts"],"sourcesContent":["import { InboxMessage } from \"../../inbox\";\n\n/** Client actions. */\nexport enum ClientAction {\n /** Subscribe to various events for a particular channel. */\n Subscribe = 'subscribe',\n\n /** Unsubscribe from a channel. */\n Unsubscribe = 'unsubscribe',\n\n /** Pong response to a ping message from the server. */\n Pong = 'pong',\n\n /** Ping the server to keep the connection alive. */\n Ping = 'ping',\n\n /** Get the current configuration. */\n GetConfig = 'get-config',\n}\n\n/** Client request envelope. */\nexport interface ClientMessageEnvelope {\n /**\n * Transaction ID.\n *\n * This is a UUID generated per-socket message.\n *\n * The server response should include the same transaction ID.\n */\n tid: string;\n\n /** Requested action for the server to perform. */\n action: ClientAction;\n\n /** Optional: Statistics describing past requests and/or client state. */\n stats?: Record<string, any>;\n\n /** Optional: Payload for the request, varying by action. */\n data?: Record<string, any>;\n}\n\nexport enum ServerAction {\n /** Ping message from the server. */\n Ping = 'ping',\n}\n\n/**\n * Server action envelope.\n *\n * This is a request for the client to perform an action and respond to the server.\n */\nexport interface ServerActionEnvelope {\n /** Transaction ID. */\n tid: string;\n\n /** Action from the server. */\n action: ServerAction;\n}\n\n/** Server response types. */\nexport enum ServerResponse {\n /** Response to an action request. */\n Ack = 'ack',\n\n /** Response to a ping request. */\n Pong = 'pong',\n}\n\n/**\n * Server response envelope.\n *\n * This is a response from the server to a {@link ClientAction} (ping, subscribe, get-config, etc.).\n */\nexport interface ServerResponseEnvelope {\n /** Transaction ID. */\n tid: string;\n\n /** Response from the server. */\n response: ServerResponse;\n\n /** Optional: Payload for the response, varying by response. */\n data?: Record<string, any>;\n}\n\n/** Message event types broadcast by the server. */\nexport enum InboxMessageEvent {\n NewMessage = 'message',\n Archive = 'archive',\n ArchiveAll = 'archive-all',\n ArchiveRead = 'archive-read',\n Clicked = 'clicked',\n MarkAllRead = 'mark-all-read',\n Opened = 'opened',\n Read = 'read',\n Unarchive = 'unarchive',\n Unopened = 'unopened',\n Unread = 'unread',\n}\n\n/** Envelope for an inbox message event. */\nexport interface InboxMessageEventEnvelope {\n /** Event type indicating a new message, or a mutation to one or more existing messages. */\n event: InboxMessageEvent;\n\n /**\n * Optional:Message ID.\n *\n * messageId is present for events that mutate a single message (e.g. read, unread, archive, etc.).\n */\n messageId?: string;\n\n /** Optional: Message data, varying by event.\n *\n * For {@link InboxMessageEvent.NewMessage}, this is an {@link InboxMessage}.\n * For other events this is undefined.\n */\n data?: InboxMessage;\n}\n\n/** Message sent by the server to indicate that the client should reconnect. */\nexport interface ReconnectMessage {\n /** Event type indicating a reconnection. */\n event: 'reconnect';\n\n /** Message describing the reason for the reconnection. */\n message: string;\n\n /** Seconds after which the client should retry the connection. */\n retryAfter: number;\n\n /** https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent/code */\n code: number;\n}\n\n/** Configuration for the client. */\nexport interface Config {\n /** The time interval in milliseconds between client ping messages to the server. */\n pingInterval: number;\n\n /**\n * Maximum number of outstanding pings before the client should\n * close the connection and retry connecting.\n */\n maxOutstandingPings: number;\n}\n\n/** Envelope for a config response. */\nexport interface ConfigResponseEnvelope {\n /** Transaction ID. */\n tid: string;\n\n response: 'config';\n\n /** Configuration data for the client. */\n data: Config;\n}\n\nexport type ServerMessage =\n | ConfigResponseEnvelope\n | InboxMessageEventEnvelope\n | ReconnectMessage\n | ServerActionEnvelope\n | ServerResponseEnvelope;\n","export interface CourierApiUrls {\n courier: {\n rest: string;\n graphql: string;\n },\n inbox: {\n graphql: string;\n webSocket: string;\n }\n}\n\nexport const getCourierApiUrls = (urls?: CourierApiUrls): CourierApiUrls => ({\n courier: {\n rest: urls?.courier.rest || 'https://api.courier.com',\n graphql: urls?.courier.graphql || 'https://api.courier.com/client/q',\n },\n inbox: {\n graphql: urls?.inbox.graphql || 'https://inbox.courier.com/q',\n webSocket: urls?.inbox.webSocket || 'wss://realtime.courier.io'\n }\n});","export class Logger {\n\n private readonly PREFIX = '[COURIER]';\n\n constructor(private readonly showLogs: boolean) { }\n\n public warn(message: string, ...args: any[]): void {\n if (this.showLogs) {\n console.warn(`${this.PREFIX} ${message}`, ...args);\n }\n }\n\n public log(message: string, ...args: any[]): void {\n if (this.showLogs) {\n console.log(`${this.PREFIX} ${message}`, ...args);\n }\n }\n\n public error(message: string, ...args: any[]): void {\n if (this.showLogs) {\n console.error(`${this.PREFIX} ${message}`, ...args);\n }\n }\n\n public debug(message: string, ...args: any[]): void {\n if (this.showLogs) {\n console.debug(`${this.PREFIX} ${message}`, ...args);\n }\n }\n\n public info(message: string, ...args: any[]): void {\n if (this.showLogs) {\n console.info(`${this.PREFIX} ${message}`, ...args);\n }\n }\n}\n","export class UUID {\n\n private static readonly ALPHABET = 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict';\n\n /**\n * nanoid\n * Copyright 2017 Andrey Sitnik <andrey@sitnik.ru>\n *\n * https://github.com/ai/nanoid/blob/main/LICENSE\n *\n * @param size - The size of the UUID to generate.\n * @returns A string representing the UUID.\n */\n static nanoid(size: number = 21): string {\n let id = '';\n let bytes = crypto.getRandomValues(new Uint8Array((size |= 0)));\n\n while (size--) {\n // Using the bitwise AND operator to \"cap\" the value of\n // the random byte from 255 to 63, in that way we can make sure\n // that the value will be a valid index for the \"chars\" string.\n id += UUID.ALPHABET[bytes[size] & 63]\n }\n return id;\n }\n\n}","import { CourierClientOptions } from \"../client/courier-client\";\nimport { Logger } from \"./logger\";\nimport { UUID } from \"./uuid\";\n\nexport class CourierRequestError extends Error {\n constructor(\n public code: number,\n message: string,\n public type?: string\n ) {\n super(message);\n this.name = 'CourierRequestError';\n }\n}\n\nfunction logRequest(logger: Logger, uid: string, type: 'HTTP' | 'GraphQL', data: {\n url: string;\n method?: string;\n headers: Record<string, string>;\n body?: any;\n query?: string;\n variables?: Record<string, any>;\n}) {\n logger.log(`\nšŸ“” New Courier ${type} Request: ${uid}\nURL: ${data.url}\n${data.method ? `Method: ${data.method}` : ''}\n${data.query ? `Query: ${data.query}` : ''}\n${data.variables ? `Variables: ${JSON.stringify(data.variables, null, 2)}` : ''}\nHeaders: ${JSON.stringify(data.headers, null, 2)}\nBody: ${data.body ? JSON.stringify(data.body, null, 2) : 'Empty'}\n `);\n}\n\nfunction logResponse(logger: Logger, uid: string, type: 'HTTP' | 'GraphQL', data: {\n status: number;\n response: any;\n}) {\n logger.log(`\nšŸ“” New Courier ${type} Response: ${uid}\nStatus Code: ${data.status}\nResponse JSON: ${JSON.stringify(data.response, null, 2)}\n `);\n}\n\nexport async function http(props: {\n url: string,\n options: CourierClientOptions,\n method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH',\n headers?: Record<string, string>,\n body?: any,\n validCodes?: number[]\n}): Promise<any> {\n const validCodes = props.validCodes ?? [200];\n const uid = props.options.showLogs ? UUID.nanoid() : undefined;\n\n // Create request\n const request = new Request(props.url, {\n method: props.method,\n headers: {\n 'Content-Type': 'application/json',\n ...props.headers\n },\n body: props.body ? JSON.stringify(props.body) : undefined\n });\n\n // Log request if enabled\n if (uid) {\n logRequest(props.options.logger, uid, 'HTTP', {\n url: request.url,\n method: request.method,\n headers: Object.fromEntries(request.headers.entries()),\n body: props.body\n });\n }\n\n // Perform request\n const response = await fetch(request);\n\n // Handle empty responses (like 204 No Content)\n if (response.status === 204) {\n return;\n }\n\n // Try to parse JSON response\n let data;\n try {\n data = await response.json();\n } catch (error) {\n\n // Weird fallback for only tracking url events :facepalm:\n if (response.status === 200) {\n return;\n }\n\n throw new CourierRequestError(\n response.status,\n 'Failed to parse response as JSON',\n 'PARSE_ERROR'\n );\n }\n\n // Log response if enabled\n if (uid) {\n logResponse(props.options.logger, uid, 'HTTP', {\n status: response.status,\n response: data\n });\n }\n\n // Handle invalid status codes\n if (!validCodes.includes(response.status)) {\n throw new CourierRequestError(\n response.status,\n data?.message || 'Unknown Error',\n data?.type\n );\n }\n\n return data;\n}\n\nexport async function graphql(props: {\n url: string,\n options: CourierClientOptions,\n headers: Record<string, string>,\n query: string,\n variables?: Record<string, any>\n}): Promise<any> {\n const uid = props.options.showLogs ? UUID.nanoid() : undefined;\n\n // Log request if enabled\n if (uid) {\n logRequest(props.options.logger, uid, 'GraphQL', {\n url: props.url,\n headers: props.headers,\n query: props.query,\n variables: props.variables\n });\n }\n\n const response = await fetch(props.url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...props.headers\n },\n body: JSON.stringify({\n query: props.query,\n variables: props.variables\n })\n });\n\n // Try to parse JSON response\n let data;\n try {\n data = await response.json();\n } catch (error) {\n throw new CourierRequestError(\n response.status,\n 'Failed to parse response as JSON',\n 'PARSE_ERROR'\n );\n }\n\n // Log response if enabled\n if (uid) {\n logResponse(props.options.logger, uid, 'GraphQL', {\n status: response.status,\n response: data\n });\n }\n\n if (!response.ok) {\n throw new CourierRequestError(\n response.status,\n data?.message || 'Unknown Error',\n data?.type\n );\n }\n\n return data;\n}\n","import { CourierClientOptions } from \"./courier-client\";\n\nexport class Client {\n\n constructor(public readonly options: CourierClientOptions) { }\n\n}\n","import { CourierBrand } from '../types/brands';\nimport { graphql } from '../utils/request';\nimport { Client } from './client';\n\nexport class BrandClient extends Client {\n\n /**\n * Get a brand by ID using GraphQL\n * @param brandId - The ID of the brand to retrieve\n * @returns Promise resolving to the requested brand\n */\n public async getBrand(props: { brandId: string }): Promise<CourierBrand> {\n const query = `\n query GetBrand {\n brand(brandId: \"${props.brandId}\") {\n settings {\n colors {\n primary\n secondary\n tertiary\n }\n inapp {\n borderRadius\n disableCourierFooter\n }\n }\n }\n }\n `;\n\n const json = await graphql({\n options: this.options,\n url: this.options.apiUrls.courier.graphql,\n headers: {\n 'x-courier-user-id': this.options.userId,\n 'x-courier-client-key': 'empty', // Empty for now. Will be removed in future.\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n query,\n variables: { brandId: props.brandId }\n });\n\n return json.data.brand as CourierBrand;\n }\n\n}\n","/**\n * Connection close code for non-error conditions.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent/code\n */\nexport const CLOSE_CODE_NORMAL_CLOSURE = 1000;\n\n/**\n * Courier-specific close event.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent\n */\nexport interface CourierCloseEvent extends CloseEvent {\n /** The number of seconds to wait before retrying the connection. */\n retryAfterSeconds?: number;\n}\n","import { CourierClientOptions } from \"../client/courier-client\";\nimport { CLOSE_CODE_NORMAL_CLOSURE, CourierCloseEvent } from \"../types/socket/protocol/errors\";\nimport { ServerMessage } from \"../types/socket/protocol/messages\";\nimport { Logger } from \"../utils/logger\";\nimport { INBOX_WIRE_PROTOCOL_VERSION } from \"./version\";\n\n/**\n * Abstract base class for Courier WebSocket implementations.\n *\n * The base class handles the connection and close events, as well as retry logic.\n * Application-specific logic should be implemented in the concrete classes.\n */\nexport abstract class CourierSocket {\n /**\n * The jitter factor for the backoff intervals.\n *\n * Backoff with jitter is calculated as a random value in the range:\n * [BACKOFF_INTERVAL - BACKOFF_JITTER_FACTOR * BACKOFF_INTERVAL,\n * BACKOFF_INTERVAL + BACKOFF_JITTER_FACTOR * BACKOFF_INTERVAL).\n */\n private static readonly BACKOFF_JITTER_FACTOR = 0.5;\n\n /**\n * The maximum number of retry attempts.\n */\n private static readonly MAX_RETRY_ATTEMPTS = 5;\n\n /**\n * Backoff intervals in milliseconds.\n *\n * Each represents an offset from the previous interval, rather than a\n * absolute offset from the initial request time.\n */\n private static readonly BACKOFF_INTERVALS_IN_MILLIS = [\n 30_000, // 30 seconds\n 60_000, // 1 minute\n 120_000, // 2 minutes\n 240_000, // 4 minutes\n 480_000, // 8 minutes\n ];\n\n /**\n * The key of the retry after time in the WebSocket close event reason.\n *\n * The Courier WebSocket server may send the close event reason in the following format:\n *\n * ```json\n * {\n * \"Retry-After\": \"10\" // The retry after time in seconds\n * }\n * ```\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent/reason\n */\n private static readonly RETRY_AFTER_KEY = 'Retry-After';\n\n /** The WebSocket instance, which may be null if the connection is not established. */\n private webSocket: WebSocket | null = null;\n\n /** The number of connection retry attempts so far, reset after a successful connection. */\n private retryAttempt: number = 0;\n\n /** The timeout ID for the current connectionretry attempt, reset when we attempt to connect. */\n private retryTimeoutId: number | null = null;\n\n /**\n * Flag indicating the application initiated a {@link CourierSocket#close} call.\n *\n * An application-initiated close may look like an abnormal closure (code 1006)\n * if it occurs before the connection is established. We differentiate to\n * prevent retrying the connection when the socket is closed intentionally.\n */\n private closeRequested: boolean = false;\n\n private readonly url: string;\n private readonly options: CourierClientOptions;\n\n constructor(\n options: CourierClientOptions\n ) {\n this.url = options.apiUrls.inbox.webSocket;\n this.options = options;\n }\n\n /**\n * Connects to the Courier WebSocket server.\n *\n * If the connection is already established, this is a no-op.\n *\n * @returns A promise that resolves when the connection is established or rejects if the connection could not be established.\n */\n public async connect(): Promise<void> {\n if (this.isConnecting || this.isOpen) {\n this.options.logger?.info(`Attempted to open a WebSocket connection, but one already exists in state '${this.webSocket?.readyState}'.`);\n\n // This isn't necessarily an error (the result is a no-op), so we resolve the promise.\n return Promise.resolve();\n }\n\n // If we're in the process of retrying, clear the timeout to prevent further retries.\n this.clearRetryTimeout();\n\n // Reset the close requested flag when we attempt to connect.\n this.closeRequested = false;\n\n return new Promise((resolve, reject) => {\n this.webSocket = new WebSocket(this.getWebSocketUrl());\n\n this.webSocket.addEventListener('open', (event: Event) => {\n // Reset the retry attempt counter when the connection is established.\n this.retryAttempt = 0;\n\n this.onOpen(event);\n\n // Resolve the promise when the WebSocket is opened (i.e. the connection is established)\n resolve();\n });\n\n this.webSocket.addEventListener('message', async (event: MessageEvent) => {\n try {\n const json = JSON.parse(event.data) as ServerMessage;\n if ('event' in json && json.event === 'reconnect') {\n this.close(CLOSE_CODE_NORMAL_CLOSURE);\n await this.retryConnection(json.retryAfter * 1000);\n return;\n }\n\n this.onMessageReceived(json)\n } catch (error) {\n this.options.logger?.error('Error parsing socket message', error);\n }\n });\n\n this.webSocket.addEventListener('close', (event: CloseEvent) => {\n // Close events are fired when the connection is closed either normally or abnormally.\n //\n // The 'close' event triggers a retry if the 'close' is:\n // 1) not a normal closure and,\n // 2) the application did not request the close (see CourierSocket#closeRequested)\n if (event.code !== CLOSE_CODE_NORMAL_CLOSURE && !this.closeRequested) {\n const courierCloseEvent = CourierSocket.parseCloseEvent(event);\n\n if (courierCloseEvent.retryAfterSeconds) {\n this.retryConnection(courierCloseEvent.retryAfterSeconds * 1000);\n } else {\n this.retryConnection();\n }\n }\n\n this.onClose(event);\n });\n\n this.webSocket.addEventListener('error', (event: Event) => {\n // If the closure was requested by the application, don't retry the connection.\n // The error event may be fired for a normal closure if it occurs before the connection is established.\n if (!this.closeRequested) {\n this.retryConnection();\n }\n\n this.onError(event);\n\n // If the HTTP Upgrade request fails, the WebSocket API fires an error event,\n // so we reject the promise to indicate that the connection could not be established.\n reject(event);\n });\n });\n }\n\n /**\n * Closes the WebSocket connection.\n *\n * See {@link https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close} for more details.\n *\n * @param code The WebSocket close code. Defaults to {@link CLOSE_CODE_NORMAL_CLOSURE}.\n * @param reason The WebSocket close reason.\n */\n public close(code = CLOSE_CODE_NORMAL_CLOSURE, reason?: string): void {\n if (this.webSocket === null) {\n return;\n }\n\n this.closeRequested = true;\n\n // Cancel any pending retries and reset the retry attempt counter.\n this.clearRetryTimeout();\n this.retryAttempt = 0;\n\n this.webSocket.close(code, reason);\n\n }\n\n /**\n * Sends a message to the Courier WebSocket server.\n *\n * @param message The message to send. The message will be serialized to a JSON string.\n */\n public send(message: Record<string, any>): void {\n if (this.webSocket === null || this.isConnecting) {\n this.options.logger?.info('Attempted to send a message, but the WebSocket is not yet open.');\n return;\n }\n\n const json = JSON.stringify(message);\n this.webSocket.send(json);\n }\n\n protected get userId(): string {\n return this.options.userId;\n }\n\n /** The sub-tenant ID, if specified by the user. */\n protected get subTenantId(): string | undefined {\n return this.options.tenantId;\n }\n\n protected get logger(): Logger | undefined {\n return this.options.logger;\n }\n\n /**\n * Called when the WebSocket connection is established with the Courier WebSocket server.\n *\n * @param event The WebSocket open event.\n */\n public abstract onOpen(event: Event): Promise<void>;\n\n /**\n * Called when a message is received from the Courier WebSocket server.\n *\n * @param data The message received.\n */\n public abstract onMessageReceived(data: ServerMessage): Promise<void>;\n\n /**\n * Called when the WebSocket connection is closed.\n *\n * @param event The WebSocket close event.\n */\n public abstract onClose(event: CloseEvent): Promise<void>;\n\n /**\n * Called when an error occurs on the WebSocket connection.\n *\n * @param event The WebSocket error event.\n */\n public abstract onError(event: Event): Promise<void>;\n\n /**\n * Whether the WebSocket connection is currently being established.\n */\n public get isConnecting(): boolean {\n return this.webSocket !== null && this.webSocket.readyState === WebSocket.CONNECTING;\n }\n\n /**\n * Whether the WebSocket connection is currently open.\n */\n public get isOpen(): boolean {\n return this.webSocket !== null && this.webSocket.readyState === WebSocket.OPEN;\n }\n\n /**\n * Constructs the WebSocket URL for the Courier WebSocket server using context\n * from the {@link CourierClientOptions} passed to the constructor.\n *\n * @returns The WebSocket URL\n */\n private getWebSocketUrl(): string {\n const accessToken = this.options.accessToken;\n const connectionId = this.options.connectionId;\n const userId = this.userId;\n\n return `${this.url}?auth=${accessToken}&cid=${connectionId}&iwpv=${INBOX_WIRE_PROTOCOL_VERSION}&userId=${userId}`;\n }\n\n /**\n * Parses the Retry-After time from the WebSocket close event reason,\n * and returns a new {@link CourierCloseEvent} with the retry after time in seconds\n * if present.\n *\n * The Courier WebSocket server may send the close event reason in the following format:\n *\n * ```json\n * {\n * \"Retry-After\": \"10\" // The retry after time in seconds\n * }\n * ```\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent/reason\n *\n * @param closeEvent The WebSocket close event.\n * @returns The WebSocket close event with the retry after time in seconds.\n */\n private static parseCloseEvent(closeEvent: CloseEvent): CourierCloseEvent {\n if (closeEvent.reason === null || closeEvent.reason === '') {\n return closeEvent;\n }\n\n try {\n const jsonReason = JSON.parse(closeEvent.reason);\n if (!jsonReason[CourierSocket.RETRY_AFTER_KEY]) {\n return closeEvent;\n }\n\n const retryAfterSeconds = parseInt(jsonReason[CourierSocket.RETRY_AFTER_KEY]);\n if (Number.isNaN(retryAfterSeconds) || retryAfterSeconds < 0) {\n return closeEvent;\n }\n\n return {\n ...closeEvent,\n retryAfterSeconds,\n };\n } catch (error) {\n return closeEvent;\n }\n }\n\n /**\n * Calculates the retry backoff time in milliseconds based on the current retry attempt.\n */\n private getBackoffTimeInMillis(): number {\n const backoffIntervalInMillis = CourierSocket.BACKOFF_INTERVALS_IN_MILLIS[this.retryAttempt];\n const lowerBound = backoffIntervalInMillis - (backoffIntervalInMillis * CourierSocket.BACKOFF_JITTER_FACTOR);\n const upperBound = backoffIntervalInMillis + (backoffIntervalInMillis * CourierSocket.BACKOFF_JITTER_FACTOR);\n\n return Math.floor(Math.random() * (upperBound - lowerBound) + lowerBound);\n }\n\n /**\n * Retries the connection to the Courier WebSocket server after\n * either {@param suggestedBackoffTimeInMillis} or a random backoff time\n * calculated using {@link getBackoffTimeInMillis}.\n *\n * @param suggestedBackoffTimeInMillis The suggested backoff time in milliseconds.\n * @returns A promise that resolves when the connection is established or rejects if the connection could not be established.\n */\n protected async retryConnection(suggestedBackoffTimeInMillis?: number): Promise<void> {\n if (this.retryTimeoutId !== null) {\n this.logger?.debug('Skipping retry attempt because a previous retry is already scheduled.');\n return;\n }\n\n if (this.retryAttempt >= CourierSocket.MAX_RETRY_ATTEMPTS) {\n this.logger?.error(`Max retry attempts (${CourierSocket.MAX_RETRY_ATTEMPTS}) reached.`);\n return;\n }\n\n const backoffTimeInMillis = suggestedBackoffTimeInMillis ?? this.getBackoffTimeInMillis();\n this.retryTimeoutId = window.setTimeout(async () => {\n try {\n await this.connect();\n } catch (error) {\n // connect() will retry if applicable\n }\n }, backoffTimeInMillis);\n this.logger?.debug(`Retrying connection in ${Math.floor(backoffTimeInMillis / 1000)}s. Retry attempt ${this.retryAttempt + 1} of ${CourierSocket.MAX_RETRY_ATTEMPTS}.`);\n\n this.retryAttempt++;\n }\n\n /**\n * Clears the retry timeout if it exists.\n */\n private clearRetryTimeout(): void {\n if (this.retryTimeoutId !== null) {\n window.clearTimeout(this.retryTimeoutId);\n this.retryTimeoutId = null;\n }\n }\n}\n","import { ClientMessageEnvelope, ServerMessage } from \"../types/socket/protocol/messages\";\n\nexport class TransactionManager {\n /**\n * The map of <transactionId, Transaction> representing outstanding requests.\n */\n private readonly outstandingRequestsMap: Map<string, Transaction> = new Map();\n\n /**\n * The queue of completed requests. This is a FIFO queue of the last N completed requests,\n * where N is {@link completedTransactionsToKeep}.\n */\n private readonly completedTransactionsQueue: Transaction[] = [];\n\n /**\n * Number of completed requests to keep in memory.\n */\n private readonly completedTransactionsToKeep: number;\n\n constructor(completedTransactionsToKeep: number = 10) {\n this.completedTransactionsToKeep = completedTransactionsToKeep;\n }\n\n public addOutstandingRequest(transactionId: string, request: ClientMessageEnvelope): void {\n const isOutstanding = this.outstandingRequestsMap.has(transactionId);\n if (isOutstanding) {\n throw new Error(`Transaction [${transactionId}] already has an outstanding request`);\n }\n\n const transaction: Transaction = {\n transactionId,\n request,\n response: null,\n start: new Date(),\n end: null,\n };\n\n this.outstandingRequestsMap.set(transactionId, transaction);\n }\n\n public addResponse(transactionId: string, response: ServerMessage): void {\n const transaction = this.outstandingRequestsMap.get(transactionId);\n if (transaction === undefined) {\n throw new Error(`Transaction [${transactionId}] does not have an outstanding request`);\n }\n\n transaction.response = response;\n transaction.end = new Date();\n\n // Move the transaction from the outstanding requests to the completed requests.\n this.outstandingRequestsMap.delete(transactionId);\n this.addCompletedTransaction(transaction);\n }\n\n public get outstandingRequests(): Transaction[] {\n return Array.from(this.outstandingRequestsMap.values());\n }\n\n public get completedTransactions(): Transaction[] {\n return this.completedTransactionsQueue;\n }\n\n public clearOutstandingRequests(): void {\n this.outstandingRequestsMap.clear();\n }\n\n /**\n * Adds a completed request to the queue.\n *\n * If the number of completed requests exceeds the maximum number of completed requests to keep,\n * remove the oldest completed request.\n */\n private addCompletedTransaction(transaction: Transaction): void {\n this.completedTransactionsQueue.push(transaction);\n\n if (this.completedTransactionsQueue.length > this.completedTransactionsToKeep) {\n this.completedTransactionsQueue.shift();\n }\n }\n}\n\ninterface Transaction {\n /**\n * The transaction ID.\n */\n transactionId: string;\n\n /**\n * The request to the server.\n */\n request: ClientMessageEnvelope;\n\n /**\n * The response to the request.\n *\n * The response is null until the request is completed.\n */\n response: ServerMessage | null;\n\n /**\n * The start time of the transaction.\n */\n start: Date;\n\n /**\n * The end time of the transaction.\n */\n end: Date | null;\n}\n","import { InboxMessage } from \"../types/inbox\";\nimport { InboxMessageEvent, InboxMessageEventEnvelope } from \"../types/socket/protocol/messages\";\n\n/**\n * Ensure the `created` timestamp is set for a new message.\n *\n * New messages received from the WebSocket may not have a created time,\n * until they are retrieved from the GraphQL API.\n *\n * @param envelope - The envelope containing the message event.\n * @returns The envelope with the created time set.\n */\nfunction ensureCreatedTime(envelope: InboxMessageEventEnvelope): InboxMessageEventEnvelope {\n if (envelope.event === InboxMessageEvent.NewMessage) {\n const message = envelope.data as InboxMessage;\n\n if (!message.created) {\n message.created = new Date().toISOString();\n }\n\n return {\n ...envelope,\n data: message,\n };\n }\n\n return envelope;\n}\n\n/**\n * Apply fixes to a message event envelope.\n *\n * @param envelope - The envelope containing the message event.\n * @returns The envelope with the fixes applied.\n */\nexport function fixMessageEventEnvelope(envelope: InboxMessageEventEnvelope): InboxMessageEventEnvelope {\n // Apply any fixes to the message event envelope.\n return ensureCreatedTime(envelope);\n}\n","import { CourierClientOptions } from '../client/courier-client';\nimport { ClientAction, ClientMessageEnvelope, Config, ConfigResponseEnvelope, InboxMessageEvent, InboxMessageEventEnvelope, ServerAction, ServerActionEnvelope, ServerMessage, ServerResponseEnvelope } from '../types/socket/protocol/messages';\nimport { CLOSE_CODE_NORMAL_CLOSURE } from '../types/socket/protocol/errors';\nimport { UUID } from '../utils/uuid';\nimport { CourierSocket } from './courier-socket';\nimport { TransactionManager } from './courier-inbox-transaction-manager';\nimport { fixMessageEventEnvelope } from './inbox-message-utils';\n\n/** Application-layer implementation of the Courier WebSocket API for Inbox messages. */\nexport class CourierInboxSocket extends CourierSocket {\n /**\n * The default interval in milliseconds at which to send a ping message to the server\n * if no other message has been received from the server.\n *\n * Fallback when the server does not provide a config.\n */\n private static readonly DEFAULT_PING_INTERVAL_MILLIS = 60_000; // 1 minute\n\n /**\n * The default maximum number of outstanding pings before the client should\n * close the connection and retry connecting.\n *\n * Fallback when the server does not provide a config.\n */\n private static readonly DEFAULT_MAX_OUTSTANDING_PINGS = 3;\n\n /**\n * The interval ID for the ping interval.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/setInterval\n */\n private pingIntervalId: number | null = null;\n\n /**\n * The list of message event listeners, called when a message event is received\n * from the Courier WebSocket server.\n */\n private messageEventListeners: ((message: InboxMessageEventEnvelope) => void)[] = [];\n\n /** Server-provided configuration for the client. */\n private config: Config | null = null;\n\n /**\n * The transaction manager, used to track outstanding requests and responses.\n */\n private readonly pingTransactionManager: TransactionManager = new TransactionManager();\n\n constructor(options: CourierClientOptions) {\n super(options);\n }\n\n public onOpen(_: Event): Promise<void> {\n // Clear any outstanding pings from the previous connection before starting to ping.\n this.pingTransactionManager.clearOutstandingRequests();\n this.restartPingInterval();\n\n // Send a request for the client's configuration.\n this.sendGetConfig();\n\n // Subscribe to all events for the user.\n this.sendSubscribe();\n\n return Promise.resolve();\n }\n\n public onMessageReceived(data: ServerMessage): Promise<void> {\n // ServerActionEnvelope\n // Respond to pings.\n if ('action' in data && data.action === ServerAction.Ping) {\n const envelope: ServerActionEnvelope = data as ServerActionEnvelope;\n this.sendPong(envelope);\n }\n\n // ServerResponseEnvelope\n // Track pongs.\n if ('response' in data && data.response === 'pong') {\n const envelope: ServerResponseEnvelope = data as ServerResponseEnvelope;\n\n // Keep track of the pong response and clear out any outstanding pings.\n // We only need to keep track of the most recent missed pings.\n this.pingTransactionManager.addResponse(envelope.tid, envelope);\n this.pingTransactionManager.clearOutstandingRequests();\n }\n\n // ConfigResponseEnvelope\n // Update the client's config.\n if ('response' in data && data.response === 'config') {\n const envelope: ConfigResponseEnvelope = data as ConfigResponseEnvelope;\n this.setConfig(envelope.data);\n }\n\n // InboxMessageEventEnvelope\n // Handle message events, calling all registered listeners.\n if ('event' in data && CourierInboxSocket.isInboxMessageEvent(data.event)) {\n const envelope: InboxMessageEventEnvelope = data as InboxMessageEventEnvelope;\n const fixedEnvelope = fixMessageEventEnvelope(envelope);\n for (const listener of this.messageEventListeners) {\n listener(fixedEnvelope);\n }\n }\n\n // Restart the ping interval if a message is received from the server.\n this.restartPingInterval();\n\n return Promise.resolve();\n }\n\n public onClose(_: CloseEvent): Promise<void> {\n // Cancel scheduled pings.\n this.clearPingInterval();\n\n // Remove any message event listeners.\n this.clearMessageEventListeners();\n\n // Clear any outstanding pings.\n this.pingTransactionManager.clearOutstandingRequests();\n\n return Promise.resolve();\n }\n\n public onError(_: Event): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Sends a subscribe message to the server.\n *\n * Subscribes to all events for the user.\n */\n public sendSubscribe(): void {\n const data: Record<string, any> = {\n channel: this.userId,\n event: '*',\n };\n\n // Set accountId to the sub-tenant ID if it is specified.\n if (this.subTenantId) {\n data.accountId = this.subTenantId;\n }\n\n const envelope: ClientMessageEnvelope = {\n tid: UUID.nanoid(),\n action: ClientAction.Subscribe,\n data,\n };\n\n this.send(envelope);\n }\n\n /**\n * Sends an unsubscribe message to the server.\n *\n * Unsubscribes from all events for the user.\n */\n public sendUnsubscribe(): void {\n const envelope: ClientMessageEnvelope = {\n tid: UUID.nanoid(),\n action: ClientAction.Unsubscribe,\n data: {\n channel: this.userId,\n },\n };\n\n this.send(envelope);\n }\n\n /**\n * Adds a message event listener, called when a message event is received\n * from the Courier WebSocket server.\n *\n * @param listener The listener function\n */\n public addMessageEventListener(listener: (message: InboxMessageEventEnvelope) => void): void {\n this.messageEventListeners.push(listener);\n }\n\n /**\n * Send a ping message to the server.\n *\n * ping/pong is implemented at the application layer since the browser's\n * WebSocket implementation does not support control-level ping/pong.\n */\n private sendPing(): void {\n if (this.pingTransactionManager.outstandingRequests.length >= this.maxOutstandingPings) {\n this.logger?.debug('Max outstanding pings reached, retrying connection.');\n this.close(CLOSE_CODE_NORMAL_CLOSURE, 'Max outstanding pings reached, retrying connection.');\n this.retryConnection();\n\n return;\n }\n\n const envelope: ClientMessageEnvelope = {\n tid: UUID.nanoid(),\n action: ClientAction.Ping,\n };\n\n this.send(envelope);\n this.pingTransactionManager.addOutstandingRequest(envelope.tid, envelope);\n }\n\n /**\n * Send a pong response to the server.\n *\n * ping/pong is implemented at the application layer since the browser's\n * WebSocket implementation does not support control-level ping/pong.\n */\n private sendPong(incomingMessage: ServerActionEnvelope): void {\n const response: ClientMessageEnvelope = {\n tid: incomingMessage.tid,\n action: ClientAction.Pong,\n };\n\n this.send(response);\n }\n\n /**\n * Send a request for the client's configuration.\n */\n private sendGetConfig(): void {\n const envelope: ClientMessageEnvelope = {\n tid: UUID.nanoid(),\n action: ClientAction.GetConfig,\n };\n\n this.send(envelope);\n }\n\n /**\n * Restart the ping interval, clearing the previous interval if it exists.\n */\n private restartPingInterval(): void {\n this.clearPingInterval();\n\n this.pingIntervalId = window.setInterval(() => {\n this.sendPing();\n }, this.pingInterval);\n }\n\n private clearPingInterval(): void {\n if (this.pingIntervalId) {\n window.clearInterval(this.pingIntervalId);\n }\n }\n\n private get pingInterval(): number {\n if (this.config) {\n // Server-provided ping interval is in seconds.\n return this.config.pingInterval * 1000;\n }\n\n return CourierInboxSocket.DEFAULT_PING_INTERVAL_MILLIS;\n }\n\n private get maxOutstandingPings(): number {\n if (this.config) {\n return this.config.maxOutstandingPings;\n }\n\n return CourierInboxSocket.DEFAULT_MAX_OUTSTANDING_PINGS;\n }\n\n private setConfig(config: Config): void {\n this.config = config;\n }\n\n /**\n * Removes all message event listeners.\n */\n private clearMessageEventListeners(): void {\n this.messageEventListeners = [];\n }\n\n private static isInboxMessageEvent(event: string): event is InboxMessageEvent {\n return Object.values(InboxMessageEvent).includes(event as InboxMessageEvent);\n }\n}\n","import { CourierInboxSocket } from '../socket/courier-inbox-socket';\nimport { CourierGetInboxMessagesResponse } from '../types/inbox';\nimport { graphql } from '../utils/request';\nimport { Client } from './client';\nimport { CourierClientOptions } from './courier-client';\n\nexport class InboxClient extends Client {\n\n readonly socket: CourierInboxSocket;\n\n constructor(options: CourierClientOptions) {\n super(options);\n this.socket = new CourierInboxSocket(options);\n }\n\n /**\n * Get paginated messages\n * @param paginationLimit - Number of messages to return per page (default: 24)\n * @param startCursor - Cursor for pagination\n * @returns Promise resolving to paginated messages response\n */\n public async getMessages(props?: { paginationLimit?: number; startCursor?: string; }): Promise<CourierGetInboxMessagesResponse> {\n const query = `\n query GetInboxMessages(\n $params: FilterParamsInput = { ${this.options.tenantId ? `accountId: \"${this.options.tenantId}\"` : ''} }\n $limit: Int = ${props?.paginationLimit ?? 24}\n $after: String ${props?.startCursor ? `= \"${props.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 `;\n\n return await graphql({\n options: this.options,\n query,\n headers: {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Get paginated archived messages\n * @param paginationLimit - Number of messages to return per page (default: 24)\n * @param startCursor - Cursor for pagination\n * @returns Promise resolving to paginated archived messages response\n */\n public async getArchivedMessages(props?: { paginationLimit?: number; startCursor?: string; }): Promise<CourierGetInboxMessagesResponse> {\n const query = `\n query GetInboxMessages(\n $params: FilterParamsInput = { ${this.options.tenantId ? `accountId: \"${this.options.tenantId}\"` : ''}, archived: true }\n $limit: Int = ${props?.paginationLimit ?? 24}\n $after: String ${props?.startCursor ? `= \"${props.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 `;\n\n return graphql({\n options: this.options,\n query,\n headers: {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Get unread message count\n * @returns Promise resolving to number of unread messages\n */\n public async getUnreadMessageCount(): Promise<number> {\n const query = `\n query GetMessages {\n count(params: { status: \"unread\" ${this.options.tenantId ? `, accountId: \"${this.options.tenantId}\"` : ''} })\n }\n `;\n\n const response = await graphql({\n options: this.options,\n query,\n headers: {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n url: this.options.apiUrls.inbox.graphql,\n });\n\n return response.data?.count ?? 0;\n }\n\n /**\n * Track a click event\n * @param messageId - ID of the message\n * @param trackingId - ID for tracking the click\n * @returns Promise resolving when click is tracked\n */\n public async click(props: { messageId: string, trackingId: string }): Promise<void> {\n const query = `\n mutation TrackEvent {\n clicked(messageId: \"${props.messageId}\", trackingId: \"${props.trackingId}\")\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Mark a message as read\n * @param messageId - ID of the message to mark as read\n * @returns Promise resolving when message is marked as read\n */\n public async read(props: { messageId: string }): Promise<void> {\n const query = `\n mutation TrackEvent {\n read(messageId: \"${props.messageId}\")\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Mark a message as unread\n * @param messageId - ID of the message to mark as unread\n * @returns Promise resolving when message is marked as unread\n */\n public async unread(props: { messageId: string }): Promise<void> {\n const query = `\n mutation TrackEvent {\n unread(messageId: \"${props.messageId}\")\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Mark a message as opened\n * @param messageId - ID of the message to mark as opened\n * @returns Promise resolving when message is marked as opened\n */\n public async open(props: { messageId: string }): Promise<void> {\n const query = `\n mutation TrackEvent {\n opened(messageId: \"${props.messageId}\")\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Archive a message\n * @param messageId - ID of the message to archive\n * @returns Promise resolving when message is archived\n */\n public async archive(props: { messageId: string }): Promise<void> {\n const query = `\n mutation TrackEvent {\n archive(messageId: \"${props.messageId}\")\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Unarchive a message\n * @param messageId - ID of the message to unarchive\n * @returns Promise resolving when message is unarchived\n */\n public async unarchive(props: { messageId: string }): Promise<void> {\n const query = `\n mutation TrackEvent {\n unarchive(messageId: \"${props.messageId}\")\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Mark all messages as read\n * @returns Promise resolving when all messages are marked as read\n */\n public async readAll(): Promise<void> {\n const query = `\n mutation TrackEvent {\n markAllRead\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Archive all read messages.\n */\n public async archiveRead(): Promise<void> {\n const query = `\n mutation TrackEvent {\n archiveRead\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Archive all read messages.\n */\n public async archiveAll(): Promise<void> {\n const query = `\n mutation TrackEvent {\n archiveAll\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n}\n","export type CourierUserPreferencesStatus = 'OPTED_IN' | 'OPTED_OUT' | 'REQUIRED' | 'UNKNOWN';\n\nexport type CourierUserPreferencesChannel = 'direct_message' | 'inbox' | 'email' | 'push' | 'sms' | 'webhook' | 'unknown';\n\nexport interface CourierUserPreferencesPaging {\n cursor?: string;\n more: boolean;\n}\n\nexport interface CourierUserPreferencesTopic {\n topicId: string;\n topicName: string;\n sectionId: string;\n sectionName: string;\n status: CourierUserPreferencesStatus;\n defaultStatus: CourierUserPreferencesStatus;\n hasCustomRouting: boolean;\n customRouting: CourierUserPreferencesChannel[];\n}\n\nexport interface CourierUserPreferences {\n items: CourierUserPreferencesTopic[];\n paging: CourierUserPreferencesPaging;\n}\n\nexport interface CourierUserPreferencesTopicResponse {\n topic: CourierUserPreferencesTopic;\n}\n\nexport class PreferenceTransformer {\n /**\n * Transforms a single API response item to the CourierUserPreferencesTopic type\n * @param item - The API response item\n * @returns A CourierUserPreferencesTopic object\n */\n transformItem(item: any): CourierUserPreferencesTopic {\n return {\n topicId: item.topic_id,\n topicName: item.topic_name,\n sectionId: item.section_id,\n sectionName: item.section_name,\n status: item.status,\n defaultStatus: item.default_status,\n hasCustomRouting: item.has_custom_routing,\n customRouting: item.custom_routing || []\n };\n }\n\n /**\n * Transforms an array of API response items to CourierUserPreferencesTopic objects\n * @param items - The API response items\n * @returns A generator of CourierUserPreferencesTopic objects\n */\n *transform(items: any[]): Generator<CourierUserPreferencesTopic> {\n for (const item of items) {\n yield this.transformItem(item);\n }\n }\n}","import { CourierUserPreferences, CourierUserPreferencesChannel, CourierUserPreferencesStatus, CourierUserPreferencesTopic, CourierUserPreferencesTopicResponse, PreferenceTransformer } from '../types/preference';\nimport { decode, encode } from '../utils/coding';\nimport { http } from '../utils/request';\nimport { Client } from './client';\n\nexport class PreferenceClient extends Client {\n private transformer = new PreferenceTransformer();\n\n /**\n * Get all preferences for a user\n * @param paginationCursor - Optional cursor for pagination\n * @returns Promise resolving to user preferences\n * @see https://www.courier.com/docs/reference/user-preferences/list-all-user-preferences\n */\n public async getUserPreferences(props?: { paginationCursor?: string; }): Promise<CourierUserPreferences> {\n let url = `${this.options.apiUrls.courier.rest}/users/${this.options.userId}/preferences`;\n\n if (props?.paginationCursor) {\n url += `?cursor=${props.paginationCursor}`;\n }\n\n const json = await http({\n options: this.options,\n url,\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n });\n\n const data = json as CourierUserPreferences;\n\n return {\n items: [...this.transformer.transform(data.items)],\n paging: data.paging\n };\n }\n\n /**\n * Get preferences for a specific topic\n * @param topicId - The ID of the topic to get preferences for\n * @returns Promise resolving to topic preferences\n * @see https://www.courier.com/docs/reference/user-preferences/get-subscription-topic-preferences\n */\n public async getUserPreferenceTopic(props: { topicId: string; }): Promise<CourierUserPreferencesTopic> {\n\n const json = await http({\n options: this.options,\n url: `${this.options.apiUrls.courier.rest}/users/${this.options.userId}/preferences/${props.topicId}`,\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n });\n\n const res = json as CourierUserPreferencesTopicResponse;\n return this.transformer.transformItem(res.topic);\n }\n\n /**\n * Update preferences for a specific topic\n * @param topicId - The ID of the topic to update preferences for\n * @param status - The new status for the topic\n * @param hasCustomRouting - Whether the topic has custom routing\n * @param customRouting - The custom routing channels for the topic\n * @returns Promise resolving when update is complete\n * @see https://www.courier.com/docs/reference/user-preferences/update-subscription-topic-preferences\n */\n public async putUserPreferenceTopic(props: { topicId: string; status: CourierUserPreferencesStatus; hasCustomRouting: boolean; customRouting: CourierUserPreferencesChannel[]; }): Promise<void> {\n\n const payload = {\n topic: {\n status: props.status,\n has_custom_routing: props.hasCustomRouting,\n custom_routing: props.customRouting,\n },\n };\n\n await http({\n options: this.options,\n url: `${this.options.apiUrls.courier.rest}/users/${this.options.userId}/preferences/${props.topicId}`,\n method: 'PUT',\n headers: {\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n body: payload,\n });\n }\n\n /**\n * Get the notification center URL\n * @param clientKey - The client key to use for the URL\n * @returns The notification center URL\n */\n public getNotificationCenterUrl(props: {\n clientKey: string;\n }): string {\n const rootTenantId = decode(props.clientKey);\n const url = encode(`${rootTenantId}#${this.options.userId}${this.options.tenantId ? `#${this.options.tenantId}` : \"\"}#${false}`);\n return `https://view.notificationcenter.app/p/${url}`;\n }\n\n}\n","export function decode(clientKey: string): string {\n const binaryString = atob(clientKey);\n const bytes = new Uint8Array(binaryString.length);\n\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n\n return String.fromCharCode(...bytes);\n}\n\nexport function encode(key: string): string {\n const bytes = new Uint8Array(key.length);\n\n for (let i = 0; i < key.length; i++) {\n bytes[i] = key.charCodeAt(i);\n }\n\n return btoa(String.fromCharCode(...bytes));\n}","import { CourierDevice } from '../types/token';\nimport { http } from '../utils/request';\nimport { Client } from './client';\n\nexport class TokenClient extends Client {\n\n /**\n * Store a push notification token for a user\n * @param token - The push notification token\n * @param provider - The provider of the token\n * @param device - The device information\n * @see https://www.courier.com/docs/reference/token-management/put-token\n */\n public async putUserToken(props: {\n token: string;\n provider: string;\n device?: CourierDevice;\n }): Promise<void> {\n const payload = {\n provider_key: props.provider,\n ...(props.device && {\n device: {\n app_id: props.device.appId,\n ad_id: props.device.adId,\n device_id: props.device.deviceId,\n platform: props.device.platform,\n manufacturer: props.device.manufacturer,\n model: props.device.model\n }\n })\n };\n\n await http({\n options: this.options,\n url: `${this.options.apiUrls.courier.rest}/users/${this.options.userId}/tokens/${props.token}`,\n method: 'PUT',\n headers: {\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n body: payload,\n validCodes: [200, 204]\n });\n }\n\n /**\n * Delete a push notification token for a user\n * @param token - The push notification token\n * @returns Promise resolving when token is deleted\n */\n public async deleteUserToken(props: {\n token: string;\n }): Promise<void> {\n await http({\n options: this.options,\n url: `${this.options.apiUrls.courier.rest}/users/${this.options.userId}/tokens/${props.token}`,\n method: 'DELETE',\n headers: {\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n validCodes: [200, 204]\n });\n }\n}\n","import { http } from '../utils/request';\nimport { Client } from './client';\n\nexport class ListClient extends Client {\n\n /**\n * Subscribe a user to a list\n * @param listId - The ID of the list to subscribe to\n * @returns Promise resolving when subscription is complete\n * @see https://www.courier.com/docs/reference/lists/recipient-subscribe\n */\n public async putSubscription(props: { listId: string; }): Promise<void> {\n return await http({\n url: `${this.options.apiUrls.courier.rest}/lists/${props.listId}/subscriptions/${this.options.userId}`,\n options: this.options,\n method: 'PUT',\n headers: {\n Authorization: `Bearer ${this.options.accessToken}`,\n },\n });\n }\n\n /**\n * Unsubscribe a user from a list\n * @param listId - The ID of the list to unsubscribe from\n * @returns Promise resolving when unsubscription is complete\n * @see https://www.courier.com/docs/reference/lists/delete-subscription\n */\n public async deleteSubscription(props: { listId: string; }): Promise<void> {\n return await http({\n url: `${this.options.apiUrls.courier.rest}/lists/${props.listId}/subscriptions/${this.options.userId}`,\n options: this.options,\n method: 'DELETE',\n headers: {\n Authorization: `Bearer ${this.options.accessToken}`,\n },\n });\n }\n\n}\n","import { CourierTrackingEvent } from '../types/tracking-event';\nimport { http } from '../utils/request';\nimport { Client } from './client';\n\nexport class TrackingClient extends Client {\n\n /**\n * Post an inbound courier event\n * @param event - The event type: Example: \"New Order Placed\"\n * @param messageId - The message ID\n * @param type - The type of event: Available options: \"track\"\n * @param properties - The properties of the event\n * @returns Promise resolving to the message ID\n * @see https://www.courier.com/docs/reference/inbound/courier-track-event\n */\n public async postInboundCourier(props: {\n event: string;\n messageId: string;\n type: 'track';\n properties?: Record<string, any>;\n }): Promise<{ messageId: string }> {\n return await http({\n url: `${this.options.apiUrls.courier.rest}/inbound/courier`,\n options: this.options,\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.options.accessToken}`,\n },\n body: {\n ...props,\n userId: this.options.userId\n },\n validCodes: [200, 202]\n });\n }\n\n /**\n * Post a tracking URL event\n * These urls are found in messages sent from Courier\n * @param url - The URL to post the event to\n * @param event - The event type: Available options: \"click\", \"open\", \"unsubscribe\"\n * @returns Promise resolving when the event is posted\n */\n public async postTrackingUrl(props: {\n url: string;\n event: CourierTrackingEvent;\n }): Promise<void> {\n return await http({\n url: props.url,\n options: this.options,\n method: 'POST',\n body: {\n event: props.event\n }\n });\n }\n\n}\n","\nimport { CourierApiUrls, getCourierApiUrls } from '../types/courier-api-urls';\nimport { Logger } from '../utils/logger';\nimport { BrandClient } from './brand-client';\nimport { InboxClient } from './inbox-client';\nimport { PreferenceClient } from './preference-client';\nimport { TokenClient } from './token-client';\nimport { Client } from './client';\nimport { ListClient } from './list-client';\nimport { TrackingClient } from './tracking-client';\n\nexport interface CourierProps {\n /** User ID for the client. Normally matches the UID in your system */\n userId: string;\n\n /** JWT token for authentication: More info at https://www.courier.com/docs/reference/auth/issue-token */\n jwt?: string;\n\n /** Public API key for testing (use JWTs in prod) */\n publicApiKey?: string;\n\n /** Inbox Websocket connection ID */\n connectionId?: string;\n\n /** Tenant ID. Used for multi-tenant apps */\n tenantId?: string;\n\n /** Flag to control logging. Logs are prefixed with [COURIER]. */\n showLogs?: boolean;\n\n /** Custom API URLs */\n apiUrls?: CourierApiUrls;\n}\n\nexport interface CourierClientOptions {\n /** JWT token for authentication: More info at https://www.courier.com/docs/reference/auth/issue-token */\n readonly jwt?: string;\n\n /** Public API key for testing (use JWTs in prod) */\n readonly publicApiKey?: string;\n\n /** User ID for the client. Normally matches the UID in your system */\n readonly userId: string;\n\n /** Inbox Websocket connection ID */\n readonly connectionId?: string;\n\n /** Tenant ID. Used for multi-tenant apps */\n readonly tenantId?: string;\n\n /** Flag to control logging. Logs are prefixed with [COURIER]. */\n readonly showLogs?: boolean;\n\n /** Combined authentication token (jwt or publicApiKey) */\n readonly accessToken?: string;\n\n /** Logger instance */\n readonly logger: Logger;\n\n /** Final API URLs configuration */\n readonly apiUrls: CourierApiUrls;\n}\n\nexport class CourierClient extends Client {\n public readonly tokens: TokenClient;\n public readonly brands: BrandClient;\n public readonly preferences: PreferenceClient;\n public readonly inbox: InboxClient;\n public readonly lists: ListClient;\n public readonly tracking: TrackingClient;\n\n constructor(props: CourierProps) {\n // Determine if we should show logs (default to false)\n const showLogs = props.showLogs !== undefined ? props.showLogs : false;\n\n // Setup base options with default values\n const baseOptions = {\n ...props,\n showLogs,\n apiUrls: props.apiUrls || getCourierApiUrls(),\n accessToken: props.jwt ?? props.publicApiKey\n };\n\n // Initialize base client with logger and URLs\n super({\n ...baseOptions,\n logger: new Logger(baseOptions.showLogs),\n apiUrls: getCourierApiUrls(baseOptions.apiUrls)\n });\n\n // Initialize all subclients with the configured options\n this.tokens = new TokenClient(this.options);\n this.brands = new BrandClient(this.options);\n this.preferences = new PreferenceClient(this.options);\n this.inbox = new InboxClient(this.options);\n this.lists = new ListClient(this.options);\n this.tracking = new TrackingClient(this.options);\n\n // Warn if no authentication method is provided\n if (!this.options.jwt && !this.options.publicApiKey) {\n this.options.logger.warn('Courier Client initialized with no authentication method. Please provide a JWT or public API key.');\n }\n\n // Warn about using public API key in production\n if (this.options.publicApiKey) {\n this.options.logger?.warn(\n 'Courier Warning: Public API Keys are for testing only. Please use JWTs for production.\\n' +\n 'You can generate a JWT with this endpoint: https://www.courier.com/docs/reference/auth/issue-token\\n' +\n 'This endpoint should be called from your backend server, not the SDK.'\n );\n }\n\n // Warn if both authentication methods are provided\n if (this.options.jwt && this.options.publicApiKey) {\n this.options.logger?.warn(\n 'Courier Warning: Both a JWT and a Public API Key were provided. The Public API Key will be ignored.'\n );\n }\n }\n}\n","import { Courier } from \"./courier\";\n\nexport class AuthenticationListener {\n readonly callback: (props: { userId?: string }) => void;\n\n constructor(callback: (props: { userId?: string }) => void) {\n this.callback = callback;\n }\n\n public remove(): void {\n Courier.shared.removeAuthenticationListener(this);\n }\n\n}","import { CourierClient, CourierProps } from \"../client/courier-client\";\nimport { AuthenticationListener } from '../shared/authentication-listener';\nimport { UUID } from \"../utils/uuid\";\n\n/**\n * Courier is a singleton class that manages a shared Courier client instance and other resources.\n * UI components will automatically syncronize with this instance.\n * If you only need to call the Courier api, you should consider using the CourierClient directly.\n */\nexport class Courier {\n\n /**\n * The unique identifier for the Courier instance\n */\n public readonly id = UUID.nanoid();\n\n /**\n * The shared Courier instance\n */\n private static instance: Courier;\n\n /**\n * The Courier client instance\n */\n private instanceClient?: CourierClient;\n\n /**\n * The pagination limit (min: 1, max: 100)\n */\n private _paginationLimit = 24;\n\n public get paginationLimit(): number {\n return this._paginationLimit;\n }\n\n public set paginationLimit(value: number) {\n this._paginationLimit = Math.min(Math.max(value, 1), 100);\n }\n\n /**\n * Get the Courier client instance\n * @returns The Courier client instance or undefined if not signed in\n */\n public get client(): CourierClient | undefined {\n return this.instanceClient;\n }\n\n /**\n * The authentication listeners\n */\n private authenticationListeners: AuthenticationListener[] = [];\n\n /**\n * Get the shared Courier instance\n * @returns The shared Courier instance\n */\n public static get shared(): Courier {\n if (!Courier.instance) {\n Courier.instance = new Courier();\n }\n return Courier.instance;\n }\n\n /**\n * Sign in to Courier\n * @param options - The options for the Courier client\n */\n public signIn(props: CourierProps) {\n // Sign out any existing user.\n if (this.instanceClient) {\n this.instanceClient.options.logger.warn('Sign in called but there is already a user signed in. Signing out the current user.');\n this.signOut();\n }\n\n // Create a new client.\n const connectionId = props.connectionId ?? UUID.nanoid();\n this.instanceClient = new CourierClient({ ...props, connectionId });\n this.notifyAuthenticationListeners({ userId: props.userId });\n }\n\n /**\n * Sign out of Courier\n */\n public signOut() {\n // Close the socket client.\n this.instanceClient?.inbox.socket?.close();\n\n // Clear the client.\n this.instanceClient = undefined;\n this.notifyAuthenticationListeners({ userId: undefined });\n }\n\n /**\n * Register a callback to be notified of authentication state changes\n * @param callback - Function to be called when authentication state changes\n * @returns AuthenticationListener instance that can be used to remove the listener\n */\n public addAuthenticationListener(callback: (props: { userId?: string }) => void): AuthenticationListener {\n this.instanceClient?.options.logger.info('Adding authentication listener');\n const listener = new AuthenticationListener(callback);\n this.authenticationListeners.push(listener);\n return listener;\n }\n\n /**\n * Unregister an authentication state change listener\n * @param listener - The AuthenticationListener instance to remove\n */\n public removeAuthenticationListener(listener: AuthenticationListener) {\n this.instanceClient?.options.logger.info('Removing authentication listener');\n this.authenticationListeners = this.authenticationListeners.filter(l => l !== listener);\n }\n\n /**\n * Notify all authentication listeners\n * @param props - The props to notify the listeners with\n */\n private notifyAuthenticationListeners(props: { userId?: string }) {\n this.authenticationListeners.forEach(listener => listener.callback(props));\n }\n\n}\n"],"names":["ClientAction","ServerAction","InboxMessageEvent","getCourierApiUrls","urls","courier","rest","graphql","inbox","webSocket","Logger","constructor","showLogs","__publicField","this","warn","message","args","console","PREFIX","log","error","debug","info","_UUID","nanoid","size","id","bytes","crypto","getRandomValues","Uint8Array","ALPHABET","UUID","CourierRequestError","Error","code","type","super","name","logRequest","logger","uid","data","url","method","query","variables","JSON","stringify","headers","body","logResponse","status","response","async","http","props","validCodes","options","request","Request","Object","fromEntries","entries","fetch","json","includes","ok","Client","BrandClient","getBrand","brandId","apiUrls","userId","Authorization","accessToken","brand","CLOSE_CODE_NORMAL_CLOSURE","_CourierSocket","connect","isConnecting","isOpen","_b","_a","readyState","Promise","resolve","clearRetryTimeout","closeRequested","reject","WebSocket","getWebSocketUrl","addEventListener","event","retryAttempt","onOpen","parse","close","retryConnection","retryAfter","onMessageReceived","courierCloseEvent","parseCloseEvent","retryAfterSeconds","onClose","onError","reason","send","subTenantId","tenantId","CONNECTING","OPEN","connectionId","closeEvent","jsonReason","RETRY_AFTER_KEY","parseInt","Number","isNaN","getBackoffTimeInMillis","backoffIntervalInMillis","BACKOFF_INTERVALS_IN_MILLIS","lowerBound","BACKOFF_JITTER_FACTOR","upperBound","Math","floor","random","suggestedBackoffTimeInMillis","retryTimeoutId","MAX_RETRY_ATTEMPTS","backoffTimeInMillis","window","setTimeout","_c","clearTimeout","CourierSocket","TransactionManager","completedTransactionsToKeep","Map","addOutstandingRequest","transactionId","outstandingRequestsMap","has","transaction","start","Date","end","set","addResponse","get","delete","addCompletedTransaction","outstandingRequests","Array","from","values","completedTransactions","completedTransactionsQueue","clearOutstandingRequests","clear","push","length","shift","fixMessageEventEnvelope","envelope","NewMessage","created","toISOString","ensureCreatedTime","_CourierInboxSocket","_","pingTransactionManager","restartPingInterval","sendGetConfig","sendSubscribe","action","Ping","sendPong","tid","setConfig","isInboxMessageEvent","fixedEnvelope","listener","messageEventListeners","clearPingInterval","clearMessageEventListeners","channel","accountId","Subscribe","sendUnsubscribe","Unsubscribe","addMessageEventListener","sendPing","maxOutstandingPings","incomingMessage","Pong","GetConfig","pingIntervalId","setInterval","pingInterval","clearInterval","config","DEFAULT_PING_INTERVAL_MILLIS","DEFAULT_MAX_OUTSTANDING_PINGS","CourierInboxSocket","InboxClient","socket","getMessages","paginationLimit","startCursor","getArchivedMessages","getUnreadMessageCount","count","click","messageId","trackingId","read","unread","open","archive","unarchive","readAll","archiveRead","archiveAll","PreferenceTransformer","transformItem","item","topicId","topic_id","topicName","topic_name","sectionId","section_id","sectionName","section_name","defaultStatus","default_status","hasCustomRouting","has_custom_routing","customRouting","custom_routing","transform","items","PreferenceClient","arguments","getUserPreferences","paginationCursor","transformer","paging","getUserPreferenceTopic","res","topic","putUserPreferenceTopic","payload","getNotificationCenterUrl","key","i","charCodeAt","btoa","String","fromCharCode","encode","clientKey","binaryString","atob","decode","TokenClient","putUserToken","provider_key","provider","device","app_id","appId","ad_id","adId","device_id","deviceId","platform","manufacturer","model","token","deleteUserToken","ListClient","putSubscription","listId","deleteSubscription","TrackingClient","postInboundCourier","postTrackingUrl","CourierClient","baseOptions","jwt","publicApiKey","tokens","brands","preferences","lists","tracking","AuthenticationListener","callback","remove","Courier","shared","removeAuthenticationListener","_Courier","_paginationLimit","value","min","max","client","instanceClient","instance","signIn","signOut","notifyAuthenticationListeners","addAuthenticationListener","authenticationListeners","filter","l","forEach"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -1428,7 +1428,7 @@ class TrackingClient extends Client {
1428
1428
  class CourierClient extends Client {
1429
1429
  constructor(props) {
1430
1430
  var _a, _b;
1431
- const showLogs = props.showLogs !== void 0 ? props.showLogs : process.env.NODE_ENV === "development";
1431
+ const showLogs = props.showLogs !== void 0 ? props.showLogs : false;
1432
1432
  const baseOptions = {
1433
1433
  ...props,
1434
1434
  showLogs,
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":["../src/types/socket/protocol/messages.ts","../src/types/courier-api-urls.ts","../src/utils/logger.ts","../src/utils/uuid.ts","../src/utils/request.ts","../src/client/client.ts","../src/client/brand-client.ts","../src/types/socket/protocol/errors.ts","../src/socket/version.ts","../src/socket/courier-socket.ts","../src/socket/courier-inbox-transaction-manager.ts","../src/socket/inbox-message-utils.ts","../src/socket/courier-inbox-socket.ts","../src/client/inbox-client.ts","../src/types/preference.ts","../src/utils/coding.ts","../src/client/preference-client.ts","../src/client/token-client.ts","../src/client/list-client.ts","../src/client/tracking-client.ts","../src/client/courier-client.ts","../src/shared/authentication-listener.ts","../src/shared/courier.ts"],"sourcesContent":["import { InboxMessage } from \"../../inbox\";\n\n/** Client actions. */\nexport enum ClientAction {\n /** Subscribe to various events for a particular channel. */\n Subscribe = 'subscribe',\n\n /** Unsubscribe from a channel. */\n Unsubscribe = 'unsubscribe',\n\n /** Pong response to a ping message from the server. */\n Pong = 'pong',\n\n /** Ping the server to keep the connection alive. */\n Ping = 'ping',\n\n /** Get the current configuration. */\n GetConfig = 'get-config',\n}\n\n/** Client request envelope. */\nexport interface ClientMessageEnvelope {\n /**\n * Transaction ID.\n *\n * This is a UUID generated per-socket message.\n *\n * The server response should include the same transaction ID.\n */\n tid: string;\n\n /** Requested action for the server to perform. */\n action: ClientAction;\n\n /** Optional: Statistics describing past requests and/or client state. */\n stats?: Record<string, any>;\n\n /** Optional: Payload for the request, varying by action. */\n data?: Record<string, any>;\n}\n\nexport enum ServerAction {\n /** Ping message from the server. */\n Ping = 'ping',\n}\n\n/**\n * Server action envelope.\n *\n * This is a request for the client to perform an action and respond to the server.\n */\nexport interface ServerActionEnvelope {\n /** Transaction ID. */\n tid: string;\n\n /** Action from the server. */\n action: ServerAction;\n}\n\n/** Server response types. */\nexport enum ServerResponse {\n /** Response to an action request. */\n Ack = 'ack',\n\n /** Response to a ping request. */\n Pong = 'pong',\n}\n\n/**\n * Server response envelope.\n *\n * This is a response from the server to a {@link ClientAction} (ping, subscribe, get-config, etc.).\n */\nexport interface ServerResponseEnvelope {\n /** Transaction ID. */\n tid: string;\n\n /** Response from the server. */\n response: ServerResponse;\n\n /** Optional: Payload for the response, varying by response. */\n data?: Record<string, any>;\n}\n\n/** Message event types broadcast by the server. */\nexport enum InboxMessageEvent {\n NewMessage = 'message',\n Archive = 'archive',\n ArchiveAll = 'archive-all',\n ArchiveRead = 'archive-read',\n Clicked = 'clicked',\n MarkAllRead = 'mark-all-read',\n Opened = 'opened',\n Read = 'read',\n Unarchive = 'unarchive',\n Unopened = 'unopened',\n Unread = 'unread',\n}\n\n/** Envelope for an inbox message event. */\nexport interface InboxMessageEventEnvelope {\n /** Event type indicating a new message, or a mutation to one or more existing messages. */\n event: InboxMessageEvent;\n\n /**\n * Optional:Message ID.\n *\n * messageId is present for events that mutate a single message (e.g. read, unread, archive, etc.).\n */\n messageId?: string;\n\n /** Optional: Message data, varying by event.\n *\n * For {@link InboxMessageEvent.NewMessage}, this is an {@link InboxMessage}.\n * For other events this is undefined.\n */\n data?: InboxMessage;\n}\n\n/** Message sent by the server to indicate that the client should reconnect. */\nexport interface ReconnectMessage {\n /** Event type indicating a reconnection. */\n event: 'reconnect';\n\n /** Message describing the reason for the reconnection. */\n message: string;\n\n /** Seconds after which the client should retry the connection. */\n retryAfter: number;\n\n /** https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent/code */\n code: number;\n}\n\n/** Configuration for the client. */\nexport interface Config {\n /** The time interval in milliseconds between client ping messages to the server. */\n pingInterval: number;\n\n /**\n * Maximum number of outstanding pings before the client should\n * close the connection and retry connecting.\n */\n maxOutstandingPings: number;\n}\n\n/** Envelope for a config response. */\nexport interface ConfigResponseEnvelope {\n /** Transaction ID. */\n tid: string;\n\n response: 'config';\n\n /** Configuration data for the client. */\n data: Config;\n}\n\nexport type ServerMessage =\n | ConfigResponseEnvelope\n | InboxMessageEventEnvelope\n | ReconnectMessage\n | ServerActionEnvelope\n | ServerResponseEnvelope;\n","export interface CourierApiUrls {\n courier: {\n rest: string;\n graphql: string;\n },\n inbox: {\n graphql: string;\n webSocket: string;\n }\n}\n\nexport const getCourierApiUrls = (urls?: CourierApiUrls): CourierApiUrls => ({\n courier: {\n rest: urls?.courier.rest || 'https://api.courier.com',\n graphql: urls?.courier.graphql || 'https://api.courier.com/client/q',\n },\n inbox: {\n graphql: urls?.inbox.graphql || 'https://inbox.courier.com/q',\n webSocket: urls?.inbox.webSocket || 'wss://realtime.courier.io'\n }\n});","export class Logger {\n\n private readonly PREFIX = '[COURIER]';\n\n constructor(private readonly showLogs: boolean) { }\n\n public warn(message: string, ...args: any[]): void {\n if (this.showLogs) {\n console.warn(`${this.PREFIX} ${message}`, ...args);\n }\n }\n\n public log(message: string, ...args: any[]): void {\n if (this.showLogs) {\n console.log(`${this.PREFIX} ${message}`, ...args);\n }\n }\n\n public error(message: string, ...args: any[]): void {\n if (this.showLogs) {\n console.error(`${this.PREFIX} ${message}`, ...args);\n }\n }\n\n public debug(message: string, ...args: any[]): void {\n if (this.showLogs) {\n console.debug(`${this.PREFIX} ${message}`, ...args);\n }\n }\n\n public info(message: string, ...args: any[]): void {\n if (this.showLogs) {\n console.info(`${this.PREFIX} ${message}`, ...args);\n }\n }\n}\n","export class UUID {\n\n private static readonly ALPHABET = 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict';\n\n /**\n * nanoid\n * Copyright 2017 Andrey Sitnik <andrey@sitnik.ru>\n *\n * https://github.com/ai/nanoid/blob/main/LICENSE\n *\n * @param size - The size of the UUID to generate.\n * @returns A string representing the UUID.\n */\n static nanoid(size: number = 21): string {\n let id = '';\n let bytes = crypto.getRandomValues(new Uint8Array((size |= 0)));\n\n while (size--) {\n // Using the bitwise AND operator to \"cap\" the value of\n // the random byte from 255 to 63, in that way we can make sure\n // that the value will be a valid index for the \"chars\" string.\n id += UUID.ALPHABET[bytes[size] & 63]\n }\n return id;\n }\n\n}","import { CourierClientOptions } from \"../client/courier-client\";\nimport { Logger } from \"./logger\";\nimport { UUID } from \"./uuid\";\n\nexport class CourierRequestError extends Error {\n constructor(\n public code: number,\n message: string,\n public type?: string\n ) {\n super(message);\n this.name = 'CourierRequestError';\n }\n}\n\nfunction logRequest(logger: Logger, uid: string, type: 'HTTP' | 'GraphQL', data: {\n url: string;\n method?: string;\n headers: Record<string, string>;\n body?: any;\n query?: string;\n variables?: Record<string, any>;\n}) {\n logger.log(`\nšŸ“” New Courier ${type} Request: ${uid}\nURL: ${data.url}\n${data.method ? `Method: ${data.method}` : ''}\n${data.query ? `Query: ${data.query}` : ''}\n${data.variables ? `Variables: ${JSON.stringify(data.variables, null, 2)}` : ''}\nHeaders: ${JSON.stringify(data.headers, null, 2)}\nBody: ${data.body ? JSON.stringify(data.body, null, 2) : 'Empty'}\n `);\n}\n\nfunction logResponse(logger: Logger, uid: string, type: 'HTTP' | 'GraphQL', data: {\n status: number;\n response: any;\n}) {\n logger.log(`\nšŸ“” New Courier ${type} Response: ${uid}\nStatus Code: ${data.status}\nResponse JSON: ${JSON.stringify(data.response, null, 2)}\n `);\n}\n\nexport async function http(props: {\n url: string,\n options: CourierClientOptions,\n method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH',\n headers?: Record<string, string>,\n body?: any,\n validCodes?: number[]\n}): Promise<any> {\n const validCodes = props.validCodes ?? [200];\n const uid = props.options.showLogs ? UUID.nanoid() : undefined;\n\n // Create request\n const request = new Request(props.url, {\n method: props.method,\n headers: {\n 'Content-Type': 'application/json',\n ...props.headers\n },\n body: props.body ? JSON.stringify(props.body) : undefined\n });\n\n // Log request if enabled\n if (uid) {\n logRequest(props.options.logger, uid, 'HTTP', {\n url: request.url,\n method: request.method,\n headers: Object.fromEntries(request.headers.entries()),\n body: props.body\n });\n }\n\n // Perform request\n const response = await fetch(request);\n\n // Handle empty responses (like 204 No Content)\n if (response.status === 204) {\n return;\n }\n\n // Try to parse JSON response\n let data;\n try {\n data = await response.json();\n } catch (error) {\n\n // Weird fallback for only tracking url events :facepalm:\n if (response.status === 200) {\n return;\n }\n\n throw new CourierRequestError(\n response.status,\n 'Failed to parse response as JSON',\n 'PARSE_ERROR'\n );\n }\n\n // Log response if enabled\n if (uid) {\n logResponse(props.options.logger, uid, 'HTTP', {\n status: response.status,\n response: data\n });\n }\n\n // Handle invalid status codes\n if (!validCodes.includes(response.status)) {\n throw new CourierRequestError(\n response.status,\n data?.message || 'Unknown Error',\n data?.type\n );\n }\n\n return data;\n}\n\nexport async function graphql(props: {\n url: string,\n options: CourierClientOptions,\n headers: Record<string, string>,\n query: string,\n variables?: Record<string, any>\n}): Promise<any> {\n const uid = props.options.showLogs ? UUID.nanoid() : undefined;\n\n // Log request if enabled\n if (uid) {\n logRequest(props.options.logger, uid, 'GraphQL', {\n url: props.url,\n headers: props.headers,\n query: props.query,\n variables: props.variables\n });\n }\n\n const response = await fetch(props.url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...props.headers\n },\n body: JSON.stringify({\n query: props.query,\n variables: props.variables\n })\n });\n\n // Try to parse JSON response\n let data;\n try {\n data = await response.json();\n } catch (error) {\n throw new CourierRequestError(\n response.status,\n 'Failed to parse response as JSON',\n 'PARSE_ERROR'\n );\n }\n\n // Log response if enabled\n if (uid) {\n logResponse(props.options.logger, uid, 'GraphQL', {\n status: response.status,\n response: data\n });\n }\n\n if (!response.ok) {\n throw new CourierRequestError(\n response.status,\n data?.message || 'Unknown Error',\n data?.type\n );\n }\n\n return data;\n}\n","import { CourierClientOptions } from \"./courier-client\";\n\nexport class Client {\n\n constructor(public readonly options: CourierClientOptions) { }\n\n}\n","import { CourierBrand } from '../types/brands';\nimport { graphql } from '../utils/request';\nimport { Client } from './client';\n\nexport class BrandClient extends Client {\n\n /**\n * Get a brand by ID using GraphQL\n * @param brandId - The ID of the brand to retrieve\n * @returns Promise resolving to the requested brand\n */\n public async getBrand(props: { brandId: string }): Promise<CourierBrand> {\n const query = `\n query GetBrand {\n brand(brandId: \"${props.brandId}\") {\n settings {\n colors {\n primary\n secondary\n tertiary\n }\n inapp {\n borderRadius\n disableCourierFooter\n }\n }\n }\n }\n `;\n\n const json = await graphql({\n options: this.options,\n url: this.options.apiUrls.courier.graphql,\n headers: {\n 'x-courier-user-id': this.options.userId,\n 'x-courier-client-key': 'empty', // Empty for now. Will be removed in future.\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n query,\n variables: { brandId: props.brandId }\n });\n\n return json.data.brand as CourierBrand;\n }\n\n}\n","/**\n * Connection close code for non-error conditions.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent/code\n */\nexport const CLOSE_CODE_NORMAL_CLOSURE = 1000;\n\n/**\n * Courier-specific close event.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent\n */\nexport interface CourierCloseEvent extends CloseEvent {\n /** The number of seconds to wait before retrying the connection. */\n retryAfterSeconds?: number;\n}\n","export const INBOX_WIRE_PROTOCOL_VERSION = 'v1';\n","import { CourierClientOptions } from \"../client/courier-client\";\nimport { CLOSE_CODE_NORMAL_CLOSURE, CourierCloseEvent } from \"../types/socket/protocol/errors\";\nimport { ServerMessage } from \"../types/socket/protocol/messages\";\nimport { Logger } from \"../utils/logger\";\nimport { INBOX_WIRE_PROTOCOL_VERSION } from \"./version\";\n\n/**\n * Abstract base class for Courier WebSocket implementations.\n *\n * The base class handles the connection and close events, as well as retry logic.\n * Application-specific logic should be implemented in the concrete classes.\n */\nexport abstract class CourierSocket {\n /**\n * The jitter factor for the backoff intervals.\n *\n * Backoff with jitter is calculated as a random value in the range:\n * [BACKOFF_INTERVAL - BACKOFF_JITTER_FACTOR * BACKOFF_INTERVAL,\n * BACKOFF_INTERVAL + BACKOFF_JITTER_FACTOR * BACKOFF_INTERVAL).\n */\n private static readonly BACKOFF_JITTER_FACTOR = 0.5;\n\n /**\n * The maximum number of retry attempts.\n */\n private static readonly MAX_RETRY_ATTEMPTS = 5;\n\n /**\n * Backoff intervals in milliseconds.\n *\n * Each represents an offset from the previous interval, rather than a\n * absolute offset from the initial request time.\n */\n private static readonly BACKOFF_INTERVALS_IN_MILLIS = [\n 30_000, // 30 seconds\n 60_000, // 1 minute\n 120_000, // 2 minutes\n 240_000, // 4 minutes\n 480_000, // 8 minutes\n ];\n\n /**\n * The key of the retry after time in the WebSocket close event reason.\n *\n * The Courier WebSocket server may send the close event reason in the following format:\n *\n * ```json\n * {\n * \"Retry-After\": \"10\" // The retry after time in seconds\n * }\n * ```\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent/reason\n */\n private static readonly RETRY_AFTER_KEY = 'Retry-After';\n\n /** The WebSocket instance, which may be null if the connection is not established. */\n private webSocket: WebSocket | null = null;\n\n /** The number of connection retry attempts so far, reset after a successful connection. */\n private retryAttempt: number = 0;\n\n /** The timeout ID for the current connectionretry attempt, reset when we attempt to connect. */\n private retryTimeoutId: number | null = null;\n\n /**\n * Flag indicating the application initiated a {@link CourierSocket#close} call.\n *\n * An application-initiated close may look like an abnormal closure (code 1006)\n * if it occurs before the connection is established. We differentiate to\n * prevent retrying the connection when the socket is closed intentionally.\n */\n private closeRequested: boolean = false;\n\n private readonly url: string;\n private readonly options: CourierClientOptions;\n\n constructor(\n options: CourierClientOptions\n ) {\n this.url = options.apiUrls.inbox.webSocket;\n this.options = options;\n }\n\n /**\n * Connects to the Courier WebSocket server.\n *\n * If the connection is already established, this is a no-op.\n *\n * @returns A promise that resolves when the connection is established or rejects if the connection could not be established.\n */\n public async connect(): Promise<void> {\n if (this.isConnecting || this.isOpen) {\n this.options.logger?.info(`Attempted to open a WebSocket connection, but one already exists in state '${this.webSocket?.readyState}'.`);\n\n // This isn't necessarily an error (the result is a no-op), so we resolve the promise.\n return Promise.resolve();\n }\n\n // If we're in the process of retrying, clear the timeout to prevent further retries.\n this.clearRetryTimeout();\n\n // Reset the close requested flag when we attempt to connect.\n this.closeRequested = false;\n\n return new Promise((resolve, reject) => {\n this.webSocket = new WebSocket(this.getWebSocketUrl());\n\n this.webSocket.addEventListener('open', (event: Event) => {\n // Reset the retry attempt counter when the connection is established.\n this.retryAttempt = 0;\n\n this.onOpen(event);\n\n // Resolve the promise when the WebSocket is opened (i.e. the connection is established)\n resolve();\n });\n\n this.webSocket.addEventListener('message', async (event: MessageEvent) => {\n try {\n const json = JSON.parse(event.data) as ServerMessage;\n if ('event' in json && json.event === 'reconnect') {\n this.close(CLOSE_CODE_NORMAL_CLOSURE);\n await this.retryConnection(json.retryAfter * 1000);\n return;\n }\n\n this.onMessageReceived(json)\n } catch (error) {\n this.options.logger?.error('Error parsing socket message', error);\n }\n });\n\n this.webSocket.addEventListener('close', (event: CloseEvent) => {\n // Close events are fired when the connection is closed either normally or abnormally.\n //\n // The 'close' event triggers a retry if the 'close' is:\n // 1) not a normal closure and,\n // 2) the application did not request the close (see CourierSocket#closeRequested)\n if (event.code !== CLOSE_CODE_NORMAL_CLOSURE && !this.closeRequested) {\n const courierCloseEvent = CourierSocket.parseCloseEvent(event);\n\n if (courierCloseEvent.retryAfterSeconds) {\n this.retryConnection(courierCloseEvent.retryAfterSeconds * 1000);\n } else {\n this.retryConnection();\n }\n }\n\n this.onClose(event);\n });\n\n this.webSocket.addEventListener('error', (event: Event) => {\n // If the closure was requested by the application, don't retry the connection.\n // The error event may be fired for a normal closure if it occurs before the connection is established.\n if (!this.closeRequested) {\n this.retryConnection();\n }\n\n this.onError(event);\n\n // If the HTTP Upgrade request fails, the WebSocket API fires an error event,\n // so we reject the promise to indicate that the connection could not be established.\n reject(event);\n });\n });\n }\n\n /**\n * Closes the WebSocket connection.\n *\n * See {@link https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close} for more details.\n *\n * @param code The WebSocket close code. Defaults to {@link CLOSE_CODE_NORMAL_CLOSURE}.\n * @param reason The WebSocket close reason.\n */\n public close(code = CLOSE_CODE_NORMAL_CLOSURE, reason?: string): void {\n if (this.webSocket === null) {\n return;\n }\n\n this.closeRequested = true;\n\n // Cancel any pending retries and reset the retry attempt counter.\n this.clearRetryTimeout();\n this.retryAttempt = 0;\n\n this.webSocket.close(code, reason);\n\n }\n\n /**\n * Sends a message to the Courier WebSocket server.\n *\n * @param message The message to send. The message will be serialized to a JSON string.\n */\n public send(message: Record<string, any>): void {\n if (this.webSocket === null || this.isConnecting) {\n this.options.logger?.info('Attempted to send a message, but the WebSocket is not yet open.');\n return;\n }\n\n const json = JSON.stringify(message);\n this.webSocket.send(json);\n }\n\n protected get userId(): string {\n return this.options.userId;\n }\n\n /** The sub-tenant ID, if specified by the user. */\n protected get subTenantId(): string | undefined {\n return this.options.tenantId;\n }\n\n protected get logger(): Logger | undefined {\n return this.options.logger;\n }\n\n /**\n * Called when the WebSocket connection is established with the Courier WebSocket server.\n *\n * @param event The WebSocket open event.\n */\n public abstract onOpen(event: Event): Promise<void>;\n\n /**\n * Called when a message is received from the Courier WebSocket server.\n *\n * @param data The message received.\n */\n public abstract onMessageReceived(data: ServerMessage): Promise<void>;\n\n /**\n * Called when the WebSocket connection is closed.\n *\n * @param event The WebSocket close event.\n */\n public abstract onClose(event: CloseEvent): Promise<void>;\n\n /**\n * Called when an error occurs on the WebSocket connection.\n *\n * @param event The WebSocket error event.\n */\n public abstract onError(event: Event): Promise<void>;\n\n /**\n * Whether the WebSocket connection is currently being established.\n */\n public get isConnecting(): boolean {\n return this.webSocket !== null && this.webSocket.readyState === WebSocket.CONNECTING;\n }\n\n /**\n * Whether the WebSocket connection is currently open.\n */\n public get isOpen(): boolean {\n return this.webSocket !== null && this.webSocket.readyState === WebSocket.OPEN;\n }\n\n /**\n * Constructs the WebSocket URL for the Courier WebSocket server using context\n * from the {@link CourierClientOptions} passed to the constructor.\n *\n * @returns The WebSocket URL\n */\n private getWebSocketUrl(): string {\n const accessToken = this.options.accessToken;\n const connectionId = this.options.connectionId;\n const userId = this.userId;\n\n return `${this.url}?auth=${accessToken}&cid=${connectionId}&iwpv=${INBOX_WIRE_PROTOCOL_VERSION}&userId=${userId}`;\n }\n\n /**\n * Parses the Retry-After time from the WebSocket close event reason,\n * and returns a new {@link CourierCloseEvent} with the retry after time in seconds\n * if present.\n *\n * The Courier WebSocket server may send the close event reason in the following format:\n *\n * ```json\n * {\n * \"Retry-After\": \"10\" // The retry after time in seconds\n * }\n * ```\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent/reason\n *\n * @param closeEvent The WebSocket close event.\n * @returns The WebSocket close event with the retry after time in seconds.\n */\n private static parseCloseEvent(closeEvent: CloseEvent): CourierCloseEvent {\n if (closeEvent.reason === null || closeEvent.reason === '') {\n return closeEvent;\n }\n\n try {\n const jsonReason = JSON.parse(closeEvent.reason);\n if (!jsonReason[CourierSocket.RETRY_AFTER_KEY]) {\n return closeEvent;\n }\n\n const retryAfterSeconds = parseInt(jsonReason[CourierSocket.RETRY_AFTER_KEY]);\n if (Number.isNaN(retryAfterSeconds) || retryAfterSeconds < 0) {\n return closeEvent;\n }\n\n return {\n ...closeEvent,\n retryAfterSeconds,\n };\n } catch (error) {\n return closeEvent;\n }\n }\n\n /**\n * Calculates the retry backoff time in milliseconds based on the current retry attempt.\n */\n private getBackoffTimeInMillis(): number {\n const backoffIntervalInMillis = CourierSocket.BACKOFF_INTERVALS_IN_MILLIS[this.retryAttempt];\n const lowerBound = backoffIntervalInMillis - (backoffIntervalInMillis * CourierSocket.BACKOFF_JITTER_FACTOR);\n const upperBound = backoffIntervalInMillis + (backoffIntervalInMillis * CourierSocket.BACKOFF_JITTER_FACTOR);\n\n return Math.floor(Math.random() * (upperBound - lowerBound) + lowerBound);\n }\n\n /**\n * Retries the connection to the Courier WebSocket server after\n * either {@param suggestedBackoffTimeInMillis} or a random backoff time\n * calculated using {@link getBackoffTimeInMillis}.\n *\n * @param suggestedBackoffTimeInMillis The suggested backoff time in milliseconds.\n * @returns A promise that resolves when the connection is established or rejects if the connection could not be established.\n */\n protected async retryConnection(suggestedBackoffTimeInMillis?: number): Promise<void> {\n if (this.retryTimeoutId !== null) {\n this.logger?.debug('Skipping retry attempt because a previous retry is already scheduled.');\n return;\n }\n\n if (this.retryAttempt >= CourierSocket.MAX_RETRY_ATTEMPTS) {\n this.logger?.error(`Max retry attempts (${CourierSocket.MAX_RETRY_ATTEMPTS}) reached.`);\n return;\n }\n\n const backoffTimeInMillis = suggestedBackoffTimeInMillis ?? this.getBackoffTimeInMillis();\n this.retryTimeoutId = window.setTimeout(async () => {\n try {\n await this.connect();\n } catch (error) {\n // connect() will retry if applicable\n }\n }, backoffTimeInMillis);\n this.logger?.debug(`Retrying connection in ${Math.floor(backoffTimeInMillis / 1000)}s. Retry attempt ${this.retryAttempt + 1} of ${CourierSocket.MAX_RETRY_ATTEMPTS}.`);\n\n this.retryAttempt++;\n }\n\n /**\n * Clears the retry timeout if it exists.\n */\n private clearRetryTimeout(): void {\n if (this.retryTimeoutId !== null) {\n window.clearTimeout(this.retryTimeoutId);\n this.retryTimeoutId = null;\n }\n }\n}\n","import { ClientMessageEnvelope, ServerMessage } from \"../types/socket/protocol/messages\";\n\nexport class TransactionManager {\n /**\n * The map of <transactionId, Transaction> representing outstanding requests.\n */\n private readonly outstandingRequestsMap: Map<string, Transaction> = new Map();\n\n /**\n * The queue of completed requests. This is a FIFO queue of the last N completed requests,\n * where N is {@link completedTransactionsToKeep}.\n */\n private readonly completedTransactionsQueue: Transaction[] = [];\n\n /**\n * Number of completed requests to keep in memory.\n */\n private readonly completedTransactionsToKeep: number;\n\n constructor(completedTransactionsToKeep: number = 10) {\n this.completedTransactionsToKeep = completedTransactionsToKeep;\n }\n\n public addOutstandingRequest(transactionId: string, request: ClientMessageEnvelope): void {\n const isOutstanding = this.outstandingRequestsMap.has(transactionId);\n if (isOutstanding) {\n throw new Error(`Transaction [${transactionId}] already has an outstanding request`);\n }\n\n const transaction: Transaction = {\n transactionId,\n request,\n response: null,\n start: new Date(),\n end: null,\n };\n\n this.outstandingRequestsMap.set(transactionId, transaction);\n }\n\n public addResponse(transactionId: string, response: ServerMessage): void {\n const transaction = this.outstandingRequestsMap.get(transactionId);\n if (transaction === undefined) {\n throw new Error(`Transaction [${transactionId}] does not have an outstanding request`);\n }\n\n transaction.response = response;\n transaction.end = new Date();\n\n // Move the transaction from the outstanding requests to the completed requests.\n this.outstandingRequestsMap.delete(transactionId);\n this.addCompletedTransaction(transaction);\n }\n\n public get outstandingRequests(): Transaction[] {\n return Array.from(this.outstandingRequestsMap.values());\n }\n\n public get completedTransactions(): Transaction[] {\n return this.completedTransactionsQueue;\n }\n\n public clearOutstandingRequests(): void {\n this.outstandingRequestsMap.clear();\n }\n\n /**\n * Adds a completed request to the queue.\n *\n * If the number of completed requests exceeds the maximum number of completed requests to keep,\n * remove the oldest completed request.\n */\n private addCompletedTransaction(transaction: Transaction): void {\n this.completedTransactionsQueue.push(transaction);\n\n if (this.completedTransactionsQueue.length > this.completedTransactionsToKeep) {\n this.completedTransactionsQueue.shift();\n }\n }\n}\n\ninterface Transaction {\n /**\n * The transaction ID.\n */\n transactionId: string;\n\n /**\n * The request to the server.\n */\n request: ClientMessageEnvelope;\n\n /**\n * The response to the request.\n *\n * The response is null until the request is completed.\n */\n response: ServerMessage | null;\n\n /**\n * The start time of the transaction.\n */\n start: Date;\n\n /**\n * The end time of the transaction.\n */\n end: Date | null;\n}\n","import { InboxMessage } from \"../types/inbox\";\nimport { InboxMessageEvent, InboxMessageEventEnvelope } from \"../types/socket/protocol/messages\";\n\n/**\n * Ensure the `created` timestamp is set for a new message.\n *\n * New messages received from the WebSocket may not have a created time,\n * until they are retrieved from the GraphQL API.\n *\n * @param envelope - The envelope containing the message event.\n * @returns The envelope with the created time set.\n */\nfunction ensureCreatedTime(envelope: InboxMessageEventEnvelope): InboxMessageEventEnvelope {\n if (envelope.event === InboxMessageEvent.NewMessage) {\n const message = envelope.data as InboxMessage;\n\n if (!message.created) {\n message.created = new Date().toISOString();\n }\n\n return {\n ...envelope,\n data: message,\n };\n }\n\n return envelope;\n}\n\n/**\n * Apply fixes to a message event envelope.\n *\n * @param envelope - The envelope containing the message event.\n * @returns The envelope with the fixes applied.\n */\nexport function fixMessageEventEnvelope(envelope: InboxMessageEventEnvelope): InboxMessageEventEnvelope {\n // Apply any fixes to the message event envelope.\n return ensureCreatedTime(envelope);\n}\n","import { CourierClientOptions } from '../client/courier-client';\nimport { ClientAction, ClientMessageEnvelope, Config, ConfigResponseEnvelope, InboxMessageEvent, InboxMessageEventEnvelope, ServerAction, ServerActionEnvelope, ServerMessage, ServerResponseEnvelope } from '../types/socket/protocol/messages';\nimport { CLOSE_CODE_NORMAL_CLOSURE } from '../types/socket/protocol/errors';\nimport { UUID } from '../utils/uuid';\nimport { CourierSocket } from './courier-socket';\nimport { TransactionManager } from './courier-inbox-transaction-manager';\nimport { fixMessageEventEnvelope } from './inbox-message-utils';\n\n/** Application-layer implementation of the Courier WebSocket API for Inbox messages. */\nexport class CourierInboxSocket extends CourierSocket {\n /**\n * The default interval in milliseconds at which to send a ping message to the server\n * if no other message has been received from the server.\n *\n * Fallback when the server does not provide a config.\n */\n private static readonly DEFAULT_PING_INTERVAL_MILLIS = 60_000; // 1 minute\n\n /**\n * The default maximum number of outstanding pings before the client should\n * close the connection and retry connecting.\n *\n * Fallback when the server does not provide a config.\n */\n private static readonly DEFAULT_MAX_OUTSTANDING_PINGS = 3;\n\n /**\n * The interval ID for the ping interval.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/setInterval\n */\n private pingIntervalId: number | null = null;\n\n /**\n * The list of message event listeners, called when a message event is received\n * from the Courier WebSocket server.\n */\n private messageEventListeners: ((message: InboxMessageEventEnvelope) => void)[] = [];\n\n /** Server-provided configuration for the client. */\n private config: Config | null = null;\n\n /**\n * The transaction manager, used to track outstanding requests and responses.\n */\n private readonly pingTransactionManager: TransactionManager = new TransactionManager();\n\n constructor(options: CourierClientOptions) {\n super(options);\n }\n\n public onOpen(_: Event): Promise<void> {\n // Clear any outstanding pings from the previous connection before starting to ping.\n this.pingTransactionManager.clearOutstandingRequests();\n this.restartPingInterval();\n\n // Send a request for the client's configuration.\n this.sendGetConfig();\n\n // Subscribe to all events for the user.\n this.sendSubscribe();\n\n return Promise.resolve();\n }\n\n public onMessageReceived(data: ServerMessage): Promise<void> {\n // ServerActionEnvelope\n // Respond to pings.\n if ('action' in data && data.action === ServerAction.Ping) {\n const envelope: ServerActionEnvelope = data as ServerActionEnvelope;\n this.sendPong(envelope);\n }\n\n // ServerResponseEnvelope\n // Track pongs.\n if ('response' in data && data.response === 'pong') {\n const envelope: ServerResponseEnvelope = data as ServerResponseEnvelope;\n\n // Keep track of the pong response and clear out any outstanding pings.\n // We only need to keep track of the most recent missed pings.\n this.pingTransactionManager.addResponse(envelope.tid, envelope);\n this.pingTransactionManager.clearOutstandingRequests();\n }\n\n // ConfigResponseEnvelope\n // Update the client's config.\n if ('response' in data && data.response === 'config') {\n const envelope: ConfigResponseEnvelope = data as ConfigResponseEnvelope;\n this.setConfig(envelope.data);\n }\n\n // InboxMessageEventEnvelope\n // Handle message events, calling all registered listeners.\n if ('event' in data && CourierInboxSocket.isInboxMessageEvent(data.event)) {\n const envelope: InboxMessageEventEnvelope = data as InboxMessageEventEnvelope;\n const fixedEnvelope = fixMessageEventEnvelope(envelope);\n for (const listener of this.messageEventListeners) {\n listener(fixedEnvelope);\n }\n }\n\n // Restart the ping interval if a message is received from the server.\n this.restartPingInterval();\n\n return Promise.resolve();\n }\n\n public onClose(_: CloseEvent): Promise<void> {\n // Cancel scheduled pings.\n this.clearPingInterval();\n\n // Remove any message event listeners.\n this.clearMessageEventListeners();\n\n // Clear any outstanding pings.\n this.pingTransactionManager.clearOutstandingRequests();\n\n return Promise.resolve();\n }\n\n public onError(_: Event): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Sends a subscribe message to the server.\n *\n * Subscribes to all events for the user.\n */\n public sendSubscribe(): void {\n const data: Record<string, any> = {\n channel: this.userId,\n event: '*',\n };\n\n // Set accountId to the sub-tenant ID if it is specified.\n if (this.subTenantId) {\n data.accountId = this.subTenantId;\n }\n\n const envelope: ClientMessageEnvelope = {\n tid: UUID.nanoid(),\n action: ClientAction.Subscribe,\n data,\n };\n\n this.send(envelope);\n }\n\n /**\n * Sends an unsubscribe message to the server.\n *\n * Unsubscribes from all events for the user.\n */\n public sendUnsubscribe(): void {\n const envelope: ClientMessageEnvelope = {\n tid: UUID.nanoid(),\n action: ClientAction.Unsubscribe,\n data: {\n channel: this.userId,\n },\n };\n\n this.send(envelope);\n }\n\n /**\n * Adds a message event listener, called when a message event is received\n * from the Courier WebSocket server.\n *\n * @param listener The listener function\n */\n public addMessageEventListener(listener: (message: InboxMessageEventEnvelope) => void): void {\n this.messageEventListeners.push(listener);\n }\n\n /**\n * Send a ping message to the server.\n *\n * ping/pong is implemented at the application layer since the browser's\n * WebSocket implementation does not support control-level ping/pong.\n */\n private sendPing(): void {\n if (this.pingTransactionManager.outstandingRequests.length >= this.maxOutstandingPings) {\n this.logger?.debug('Max outstanding pings reached, retrying connection.');\n this.close(CLOSE_CODE_NORMAL_CLOSURE, 'Max outstanding pings reached, retrying connection.');\n this.retryConnection();\n\n return;\n }\n\n const envelope: ClientMessageEnvelope = {\n tid: UUID.nanoid(),\n action: ClientAction.Ping,\n };\n\n this.send(envelope);\n this.pingTransactionManager.addOutstandingRequest(envelope.tid, envelope);\n }\n\n /**\n * Send a pong response to the server.\n *\n * ping/pong is implemented at the application layer since the browser's\n * WebSocket implementation does not support control-level ping/pong.\n */\n private sendPong(incomingMessage: ServerActionEnvelope): void {\n const response: ClientMessageEnvelope = {\n tid: incomingMessage.tid,\n action: ClientAction.Pong,\n };\n\n this.send(response);\n }\n\n /**\n * Send a request for the client's configuration.\n */\n private sendGetConfig(): void {\n const envelope: ClientMessageEnvelope = {\n tid: UUID.nanoid(),\n action: ClientAction.GetConfig,\n };\n\n this.send(envelope);\n }\n\n /**\n * Restart the ping interval, clearing the previous interval if it exists.\n */\n private restartPingInterval(): void {\n this.clearPingInterval();\n\n this.pingIntervalId = window.setInterval(() => {\n this.sendPing();\n }, this.pingInterval);\n }\n\n private clearPingInterval(): void {\n if (this.pingIntervalId) {\n window.clearInterval(this.pingIntervalId);\n }\n }\n\n private get pingInterval(): number {\n if (this.config) {\n // Server-provided ping interval is in seconds.\n return this.config.pingInterval * 1000;\n }\n\n return CourierInboxSocket.DEFAULT_PING_INTERVAL_MILLIS;\n }\n\n private get maxOutstandingPings(): number {\n if (this.config) {\n return this.config.maxOutstandingPings;\n }\n\n return CourierInboxSocket.DEFAULT_MAX_OUTSTANDING_PINGS;\n }\n\n private setConfig(config: Config): void {\n this.config = config;\n }\n\n /**\n * Removes all message event listeners.\n */\n private clearMessageEventListeners(): void {\n this.messageEventListeners = [];\n }\n\n private static isInboxMessageEvent(event: string): event is InboxMessageEvent {\n return Object.values(InboxMessageEvent).includes(event as InboxMessageEvent);\n }\n}\n","import { CourierInboxSocket } from '../socket/courier-inbox-socket';\nimport { CourierGetInboxMessagesResponse } from '../types/inbox';\nimport { graphql } from '../utils/request';\nimport { Client } from './client';\nimport { CourierClientOptions } from './courier-client';\n\nexport class InboxClient extends Client {\n\n readonly socket: CourierInboxSocket;\n\n constructor(options: CourierClientOptions) {\n super(options);\n this.socket = new CourierInboxSocket(options);\n }\n\n /**\n * Get paginated messages\n * @param paginationLimit - Number of messages to return per page (default: 24)\n * @param startCursor - Cursor for pagination\n * @returns Promise resolving to paginated messages response\n */\n public async getMessages(props?: { paginationLimit?: number; startCursor?: string; }): Promise<CourierGetInboxMessagesResponse> {\n const query = `\n query GetInboxMessages(\n $params: FilterParamsInput = { ${this.options.tenantId ? `accountId: \"${this.options.tenantId}\"` : ''} }\n $limit: Int = ${props?.paginationLimit ?? 24}\n $after: String ${props?.startCursor ? `= \"${props.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 `;\n\n return await graphql({\n options: this.options,\n query,\n headers: {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Get paginated archived messages\n * @param paginationLimit - Number of messages to return per page (default: 24)\n * @param startCursor - Cursor for pagination\n * @returns Promise resolving to paginated archived messages response\n */\n public async getArchivedMessages(props?: { paginationLimit?: number; startCursor?: string; }): Promise<CourierGetInboxMessagesResponse> {\n const query = `\n query GetInboxMessages(\n $params: FilterParamsInput = { ${this.options.tenantId ? `accountId: \"${this.options.tenantId}\"` : ''}, archived: true }\n $limit: Int = ${props?.paginationLimit ?? 24}\n $after: String ${props?.startCursor ? `= \"${props.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 `;\n\n return graphql({\n options: this.options,\n query,\n headers: {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Get unread message count\n * @returns Promise resolving to number of unread messages\n */\n public async getUnreadMessageCount(): Promise<number> {\n const query = `\n query GetMessages {\n count(params: { status: \"unread\" ${this.options.tenantId ? `, accountId: \"${this.options.tenantId}\"` : ''} })\n }\n `;\n\n const response = await graphql({\n options: this.options,\n query,\n headers: {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n url: this.options.apiUrls.inbox.graphql,\n });\n\n return response.data?.count ?? 0;\n }\n\n /**\n * Track a click event\n * @param messageId - ID of the message\n * @param trackingId - ID for tracking the click\n * @returns Promise resolving when click is tracked\n */\n public async click(props: { messageId: string, trackingId: string }): Promise<void> {\n const query = `\n mutation TrackEvent {\n clicked(messageId: \"${props.messageId}\", trackingId: \"${props.trackingId}\")\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Mark a message as read\n * @param messageId - ID of the message to mark as read\n * @returns Promise resolving when message is marked as read\n */\n public async read(props: { messageId: string }): Promise<void> {\n const query = `\n mutation TrackEvent {\n read(messageId: \"${props.messageId}\")\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Mark a message as unread\n * @param messageId - ID of the message to mark as unread\n * @returns Promise resolving when message is marked as unread\n */\n public async unread(props: { messageId: string }): Promise<void> {\n const query = `\n mutation TrackEvent {\n unread(messageId: \"${props.messageId}\")\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Mark a message as opened\n * @param messageId - ID of the message to mark as opened\n * @returns Promise resolving when message is marked as opened\n */\n public async open(props: { messageId: string }): Promise<void> {\n const query = `\n mutation TrackEvent {\n opened(messageId: \"${props.messageId}\")\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Archive a message\n * @param messageId - ID of the message to archive\n * @returns Promise resolving when message is archived\n */\n public async archive(props: { messageId: string }): Promise<void> {\n const query = `\n mutation TrackEvent {\n archive(messageId: \"${props.messageId}\")\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Unarchive a message\n * @param messageId - ID of the message to unarchive\n * @returns Promise resolving when message is unarchived\n */\n public async unarchive(props: { messageId: string }): Promise<void> {\n const query = `\n mutation TrackEvent {\n unarchive(messageId: \"${props.messageId}\")\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Mark all messages as read\n * @returns Promise resolving when all messages are marked as read\n */\n public async readAll(): Promise<void> {\n const query = `\n mutation TrackEvent {\n markAllRead\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Archive all read messages.\n */\n public async archiveRead(): Promise<void> {\n const query = `\n mutation TrackEvent {\n archiveRead\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Archive all read messages.\n */\n public async archiveAll(): Promise<void> {\n const query = `\n mutation TrackEvent {\n archiveAll\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n}\n","export type CourierUserPreferencesStatus = 'OPTED_IN' | 'OPTED_OUT' | 'REQUIRED' | 'UNKNOWN';\n\nexport type CourierUserPreferencesChannel = 'direct_message' | 'inbox' | 'email' | 'push' | 'sms' | 'webhook' | 'unknown';\n\nexport interface CourierUserPreferencesPaging {\n cursor?: string;\n more: boolean;\n}\n\nexport interface CourierUserPreferencesTopic {\n topicId: string;\n topicName: string;\n sectionId: string;\n sectionName: string;\n status: CourierUserPreferencesStatus;\n defaultStatus: CourierUserPreferencesStatus;\n hasCustomRouting: boolean;\n customRouting: CourierUserPreferencesChannel[];\n}\n\nexport interface CourierUserPreferences {\n items: CourierUserPreferencesTopic[];\n paging: CourierUserPreferencesPaging;\n}\n\nexport interface CourierUserPreferencesTopicResponse {\n topic: CourierUserPreferencesTopic;\n}\n\nexport class PreferenceTransformer {\n /**\n * Transforms a single API response item to the CourierUserPreferencesTopic type\n * @param item - The API response item\n * @returns A CourierUserPreferencesTopic object\n */\n transformItem(item: any): CourierUserPreferencesTopic {\n return {\n topicId: item.topic_id,\n topicName: item.topic_name,\n sectionId: item.section_id,\n sectionName: item.section_name,\n status: item.status,\n defaultStatus: item.default_status,\n hasCustomRouting: item.has_custom_routing,\n customRouting: item.custom_routing || []\n };\n }\n\n /**\n * Transforms an array of API response items to CourierUserPreferencesTopic objects\n * @param items - The API response items\n * @returns A generator of CourierUserPreferencesTopic objects\n */\n *transform(items: any[]): Generator<CourierUserPreferencesTopic> {\n for (const item of items) {\n yield this.transformItem(item);\n }\n }\n}","export function decode(clientKey: string): string {\n const binaryString = atob(clientKey);\n const bytes = new Uint8Array(binaryString.length);\n\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n\n return String.fromCharCode(...bytes);\n}\n\nexport function encode(key: string): string {\n const bytes = new Uint8Array(key.length);\n\n for (let i = 0; i < key.length; i++) {\n bytes[i] = key.charCodeAt(i);\n }\n\n return btoa(String.fromCharCode(...bytes));\n}","import { CourierUserPreferences, CourierUserPreferencesChannel, CourierUserPreferencesStatus, CourierUserPreferencesTopic, CourierUserPreferencesTopicResponse, PreferenceTransformer } from '../types/preference';\nimport { decode, encode } from '../utils/coding';\nimport { http } from '../utils/request';\nimport { Client } from './client';\n\nexport class PreferenceClient extends Client {\n private transformer = new PreferenceTransformer();\n\n /**\n * Get all preferences for a user\n * @param paginationCursor - Optional cursor for pagination\n * @returns Promise resolving to user preferences\n * @see https://www.courier.com/docs/reference/user-preferences/list-all-user-preferences\n */\n public async getUserPreferences(props?: { paginationCursor?: string; }): Promise<CourierUserPreferences> {\n let url = `${this.options.apiUrls.courier.rest}/users/${this.options.userId}/preferences`;\n\n if (props?.paginationCursor) {\n url += `?cursor=${props.paginationCursor}`;\n }\n\n const json = await http({\n options: this.options,\n url,\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n });\n\n const data = json as CourierUserPreferences;\n\n return {\n items: [...this.transformer.transform(data.items)],\n paging: data.paging\n };\n }\n\n /**\n * Get preferences for a specific topic\n * @param topicId - The ID of the topic to get preferences for\n * @returns Promise resolving to topic preferences\n * @see https://www.courier.com/docs/reference/user-preferences/get-subscription-topic-preferences\n */\n public async getUserPreferenceTopic(props: { topicId: string; }): Promise<CourierUserPreferencesTopic> {\n\n const json = await http({\n options: this.options,\n url: `${this.options.apiUrls.courier.rest}/users/${this.options.userId}/preferences/${props.topicId}`,\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n });\n\n const res = json as CourierUserPreferencesTopicResponse;\n return this.transformer.transformItem(res.topic);\n }\n\n /**\n * Update preferences for a specific topic\n * @param topicId - The ID of the topic to update preferences for\n * @param status - The new status for the topic\n * @param hasCustomRouting - Whether the topic has custom routing\n * @param customRouting - The custom routing channels for the topic\n * @returns Promise resolving when update is complete\n * @see https://www.courier.com/docs/reference/user-preferences/update-subscription-topic-preferences\n */\n public async putUserPreferenceTopic(props: { topicId: string; status: CourierUserPreferencesStatus; hasCustomRouting: boolean; customRouting: CourierUserPreferencesChannel[]; }): Promise<void> {\n\n const payload = {\n topic: {\n status: props.status,\n has_custom_routing: props.hasCustomRouting,\n custom_routing: props.customRouting,\n },\n };\n\n await http({\n options: this.options,\n url: `${this.options.apiUrls.courier.rest}/users/${this.options.userId}/preferences/${props.topicId}`,\n method: 'PUT',\n headers: {\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n body: payload,\n });\n }\n\n /**\n * Get the notification center URL\n * @param clientKey - The client key to use for the URL\n * @returns The notification center URL\n */\n public getNotificationCenterUrl(props: {\n clientKey: string;\n }): string {\n const rootTenantId = decode(props.clientKey);\n const url = encode(`${rootTenantId}#${this.options.userId}${this.options.tenantId ? `#${this.options.tenantId}` : \"\"}#${false}`);\n return `https://view.notificationcenter.app/p/${url}`;\n }\n\n}\n","import { CourierDevice } from '../types/token';\nimport { http } from '../utils/request';\nimport { Client } from './client';\n\nexport class TokenClient extends Client {\n\n /**\n * Store a push notification token for a user\n * @param token - The push notification token\n * @param provider - The provider of the token\n * @param device - The device information\n * @see https://www.courier.com/docs/reference/token-management/put-token\n */\n public async putUserToken(props: {\n token: string;\n provider: string;\n device?: CourierDevice;\n }): Promise<void> {\n const payload = {\n provider_key: props.provider,\n ...(props.device && {\n device: {\n app_id: props.device.appId,\n ad_id: props.device.adId,\n device_id: props.device.deviceId,\n platform: props.device.platform,\n manufacturer: props.device.manufacturer,\n model: props.device.model\n }\n })\n };\n\n await http({\n options: this.options,\n url: `${this.options.apiUrls.courier.rest}/users/${this.options.userId}/tokens/${props.token}`,\n method: 'PUT',\n headers: {\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n body: payload,\n validCodes: [200, 204]\n });\n }\n\n /**\n * Delete a push notification token for a user\n * @param token - The push notification token\n * @returns Promise resolving when token is deleted\n */\n public async deleteUserToken(props: {\n token: string;\n }): Promise<void> {\n await http({\n options: this.options,\n url: `${this.options.apiUrls.courier.rest}/users/${this.options.userId}/tokens/${props.token}`,\n method: 'DELETE',\n headers: {\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n validCodes: [200, 204]\n });\n }\n}\n","import { http } from '../utils/request';\nimport { Client } from './client';\n\nexport class ListClient extends Client {\n\n /**\n * Subscribe a user to a list\n * @param listId - The ID of the list to subscribe to\n * @returns Promise resolving when subscription is complete\n * @see https://www.courier.com/docs/reference/lists/recipient-subscribe\n */\n public async putSubscription(props: { listId: string; }): Promise<void> {\n return await http({\n url: `${this.options.apiUrls.courier.rest}/lists/${props.listId}/subscriptions/${this.options.userId}`,\n options: this.options,\n method: 'PUT',\n headers: {\n Authorization: `Bearer ${this.options.accessToken}`,\n },\n });\n }\n\n /**\n * Unsubscribe a user from a list\n * @param listId - The ID of the list to unsubscribe from\n * @returns Promise resolving when unsubscription is complete\n * @see https://www.courier.com/docs/reference/lists/delete-subscription\n */\n public async deleteSubscription(props: { listId: string; }): Promise<void> {\n return await http({\n url: `${this.options.apiUrls.courier.rest}/lists/${props.listId}/subscriptions/${this.options.userId}`,\n options: this.options,\n method: 'DELETE',\n headers: {\n Authorization: `Bearer ${this.options.accessToken}`,\n },\n });\n }\n\n}\n","import { CourierTrackingEvent } from '../types/tracking-event';\nimport { http } from '../utils/request';\nimport { Client } from './client';\n\nexport class TrackingClient extends Client {\n\n /**\n * Post an inbound courier event\n * @param event - The event type: Example: \"New Order Placed\"\n * @param messageId - The message ID\n * @param type - The type of event: Available options: \"track\"\n * @param properties - The properties of the event\n * @returns Promise resolving to the message ID\n * @see https://www.courier.com/docs/reference/inbound/courier-track-event\n */\n public async postInboundCourier(props: {\n event: string;\n messageId: string;\n type: 'track';\n properties?: Record<string, any>;\n }): Promise<{ messageId: string }> {\n return await http({\n url: `${this.options.apiUrls.courier.rest}/inbound/courier`,\n options: this.options,\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.options.accessToken}`,\n },\n body: {\n ...props,\n userId: this.options.userId\n },\n validCodes: [200, 202]\n });\n }\n\n /**\n * Post a tracking URL event\n * These urls are found in messages sent from Courier\n * @param url - The URL to post the event to\n * @param event - The event type: Available options: \"click\", \"open\", \"unsubscribe\"\n * @returns Promise resolving when the event is posted\n */\n public async postTrackingUrl(props: {\n url: string;\n event: CourierTrackingEvent;\n }): Promise<void> {\n return await http({\n url: props.url,\n options: this.options,\n method: 'POST',\n body: {\n event: props.event\n }\n });\n }\n\n}\n","\nimport { CourierApiUrls, getCourierApiUrls } from '../types/courier-api-urls';\nimport { Logger } from '../utils/logger';\nimport { BrandClient } from './brand-client';\nimport { InboxClient } from './inbox-client';\nimport { PreferenceClient } from './preference-client';\nimport { TokenClient } from './token-client';\nimport { Client } from './client';\nimport { ListClient } from './list-client';\nimport { TrackingClient } from './tracking-client';\n\nexport interface CourierProps {\n /** User ID for the client. Normally matches the UID in your system */\n userId: string;\n\n /** JWT token for authentication: More info at https://www.courier.com/docs/reference/auth/issue-token */\n jwt?: string;\n\n /** Public API key for testing (use JWTs in prod) */\n publicApiKey?: string;\n\n /** Inbox Websocket connection ID */\n connectionId?: string;\n\n /** Tenant ID. Used for multi-tenant apps */\n tenantId?: string;\n\n /** Flag to control logging. Logs are prefixed with [COURIER]. */\n showLogs?: boolean;\n\n /** Custom API URLs */\n apiUrls?: CourierApiUrls;\n}\n\nexport interface CourierClientOptions {\n /** JWT token for authentication: More info at https://www.courier.com/docs/reference/auth/issue-token */\n readonly jwt?: string;\n\n /** Public API key for testing (use JWTs in prod) */\n readonly publicApiKey?: string;\n\n /** User ID for the client. Normally matches the UID in your system */\n readonly userId: string;\n\n /** Inbox Websocket connection ID */\n readonly connectionId?: string;\n\n /** Tenant ID. Used for multi-tenant apps */\n readonly tenantId?: string;\n\n /** Flag to control logging. Logs are prefixed with [COURIER]. */\n readonly showLogs?: boolean;\n\n /** Combined authentication token (jwt or publicApiKey) */\n readonly accessToken?: string;\n\n /** Logger instance */\n readonly logger: Logger;\n\n /** Final API URLs configuration */\n readonly apiUrls: CourierApiUrls;\n}\n\nexport class CourierClient extends Client {\n public readonly tokens: TokenClient;\n public readonly brands: BrandClient;\n public readonly preferences: PreferenceClient;\n public readonly inbox: InboxClient;\n public readonly lists: ListClient;\n public readonly tracking: TrackingClient;\n\n constructor(props: CourierProps) {\n // Determine if we should show logs based on props or environment\n const showLogs = props.showLogs !== undefined ? props.showLogs : process.env.NODE_ENV === 'development';\n\n // Setup base options with default values\n const baseOptions = {\n ...props,\n showLogs,\n apiUrls: props.apiUrls || getCourierApiUrls(),\n accessToken: props.jwt ?? props.publicApiKey\n };\n\n // Initialize base client with logger and URLs\n super({\n ...baseOptions,\n logger: new Logger(baseOptions.showLogs),\n apiUrls: getCourierApiUrls(baseOptions.apiUrls)\n });\n\n // Initialize all subclients with the configured options\n this.tokens = new TokenClient(this.options);\n this.brands = new BrandClient(this.options);\n this.preferences = new PreferenceClient(this.options);\n this.inbox = new InboxClient(this.options);\n this.lists = new ListClient(this.options);\n this.tracking = new TrackingClient(this.options);\n\n // Warn if no authentication method is provided\n if (!this.options.jwt && !this.options.publicApiKey) {\n this.options.logger.warn('Courier Client initialized with no authentication method. Please provide a JWT or public API key.');\n }\n\n // Warn about using public API key in production\n if (this.options.publicApiKey) {\n this.options.logger?.warn(\n 'Courier Warning: Public API Keys are for testing only. Please use JWTs for production.\\n' +\n 'You can generate a JWT with this endpoint: https://www.courier.com/docs/reference/auth/issue-token\\n' +\n 'This endpoint should be called from your backend server, not the SDK.'\n );\n }\n\n // Warn if both authentication methods are provided\n if (this.options.jwt && this.options.publicApiKey) {\n this.options.logger?.warn(\n 'Courier Warning: Both a JWT and a Public API Key were provided. The Public API Key will be ignored.'\n );\n }\n }\n}\n","import { Courier } from \"./courier\";\n\nexport class AuthenticationListener {\n readonly callback: (props: { userId?: string }) => void;\n\n constructor(callback: (props: { userId?: string }) => void) {\n this.callback = callback;\n }\n\n public remove(): void {\n Courier.shared.removeAuthenticationListener(this);\n }\n\n}","import { CourierClient, CourierProps } from \"../client/courier-client\";\nimport { AuthenticationListener } from '../shared/authentication-listener';\nimport { UUID } from \"../utils/uuid\";\n\n/**\n * Courier is a singleton class that manages a shared Courier client instance and other resources.\n * UI components will automatically syncronize with this instance.\n * If you only need to call the Courier api, you should consider using the CourierClient directly.\n */\nexport class Courier {\n\n /**\n * The unique identifier for the Courier instance\n */\n public readonly id = UUID.nanoid();\n\n /**\n * The shared Courier instance\n */\n private static instance: Courier;\n\n /**\n * The Courier client instance\n */\n private instanceClient?: CourierClient;\n\n /**\n * The pagination limit (min: 1, max: 100)\n */\n private _paginationLimit = 24;\n\n public get paginationLimit(): number {\n return this._paginationLimit;\n }\n\n public set paginationLimit(value: number) {\n this._paginationLimit = Math.min(Math.max(value, 1), 100);\n }\n\n /**\n * Get the Courier client instance\n * @returns The Courier client instance or undefined if not signed in\n */\n public get client(): CourierClient | undefined {\n return this.instanceClient;\n }\n\n /**\n * The authentication listeners\n */\n private authenticationListeners: AuthenticationListener[] = [];\n\n /**\n * Get the shared Courier instance\n * @returns The shared Courier instance\n */\n public static get shared(): Courier {\n if (!Courier.instance) {\n Courier.instance = new Courier();\n }\n return Courier.instance;\n }\n\n /**\n * Sign in to Courier\n * @param options - The options for the Courier client\n */\n public signIn(props: CourierProps) {\n // Sign out any existing user.\n if (this.instanceClient) {\n this.instanceClient.options.logger.warn('Sign in called but there is already a user signed in. Signing out the current user.');\n this.signOut();\n }\n\n // Create a new client.\n const connectionId = props.connectionId ?? UUID.nanoid();\n this.instanceClient = new CourierClient({ ...props, connectionId });\n this.notifyAuthenticationListeners({ userId: props.userId });\n }\n\n /**\n * Sign out of Courier\n */\n public signOut() {\n // Close the socket client.\n this.instanceClient?.inbox.socket?.close();\n\n // Clear the client.\n this.instanceClient = undefined;\n this.notifyAuthenticationListeners({ userId: undefined });\n }\n\n /**\n * Register a callback to be notified of authentication state changes\n * @param callback - Function to be called when authentication state changes\n * @returns AuthenticationListener instance that can be used to remove the listener\n */\n public addAuthenticationListener(callback: (props: { userId?: string }) => void): AuthenticationListener {\n this.instanceClient?.options.logger.info('Adding authentication listener');\n const listener = new AuthenticationListener(callback);\n this.authenticationListeners.push(listener);\n return listener;\n }\n\n /**\n * Unregister an authentication state change listener\n * @param listener - The AuthenticationListener instance to remove\n */\n public removeAuthenticationListener(listener: AuthenticationListener) {\n this.instanceClient?.options.logger.info('Removing authentication listener');\n this.authenticationListeners = this.authenticationListeners.filter(l => l !== listener);\n }\n\n /**\n * Notify all authentication listeners\n * @param props - The props to notify the listeners with\n */\n private notifyAuthenticationListeners(props: { userId?: string }) {\n this.authenticationListeners.forEach(listener => listener.callback(props));\n }\n\n}\n"],"names":["ClientAction","ServerAction","InboxMessageEvent","_a"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","sources":["../src/types/socket/protocol/messages.ts","../src/types/courier-api-urls.ts","../src/utils/logger.ts","../src/utils/uuid.ts","../src/utils/request.ts","../src/client/client.ts","../src/client/brand-client.ts","../src/types/socket/protocol/errors.ts","../src/socket/version.ts","../src/socket/courier-socket.ts","../src/socket/courier-inbox-transaction-manager.ts","../src/socket/inbox-message-utils.ts","../src/socket/courier-inbox-socket.ts","../src/client/inbox-client.ts","../src/types/preference.ts","../src/utils/coding.ts","../src/client/preference-client.ts","../src/client/token-client.ts","../src/client/list-client.ts","../src/client/tracking-client.ts","../src/client/courier-client.ts","../src/shared/authentication-listener.ts","../src/shared/courier.ts"],"sourcesContent":["import { InboxMessage } from \"../../inbox\";\n\n/** Client actions. */\nexport enum ClientAction {\n /** Subscribe to various events for a particular channel. */\n Subscribe = 'subscribe',\n\n /** Unsubscribe from a channel. */\n Unsubscribe = 'unsubscribe',\n\n /** Pong response to a ping message from the server. */\n Pong = 'pong',\n\n /** Ping the server to keep the connection alive. */\n Ping = 'ping',\n\n /** Get the current configuration. */\n GetConfig = 'get-config',\n}\n\n/** Client request envelope. */\nexport interface ClientMessageEnvelope {\n /**\n * Transaction ID.\n *\n * This is a UUID generated per-socket message.\n *\n * The server response should include the same transaction ID.\n */\n tid: string;\n\n /** Requested action for the server to perform. */\n action: ClientAction;\n\n /** Optional: Statistics describing past requests and/or client state. */\n stats?: Record<string, any>;\n\n /** Optional: Payload for the request, varying by action. */\n data?: Record<string, any>;\n}\n\nexport enum ServerAction {\n /** Ping message from the server. */\n Ping = 'ping',\n}\n\n/**\n * Server action envelope.\n *\n * This is a request for the client to perform an action and respond to the server.\n */\nexport interface ServerActionEnvelope {\n /** Transaction ID. */\n tid: string;\n\n /** Action from the server. */\n action: ServerAction;\n}\n\n/** Server response types. */\nexport enum ServerResponse {\n /** Response to an action request. */\n Ack = 'ack',\n\n /** Response to a ping request. */\n Pong = 'pong',\n}\n\n/**\n * Server response envelope.\n *\n * This is a response from the server to a {@link ClientAction} (ping, subscribe, get-config, etc.).\n */\nexport interface ServerResponseEnvelope {\n /** Transaction ID. */\n tid: string;\n\n /** Response from the server. */\n response: ServerResponse;\n\n /** Optional: Payload for the response, varying by response. */\n data?: Record<string, any>;\n}\n\n/** Message event types broadcast by the server. */\nexport enum InboxMessageEvent {\n NewMessage = 'message',\n Archive = 'archive',\n ArchiveAll = 'archive-all',\n ArchiveRead = 'archive-read',\n Clicked = 'clicked',\n MarkAllRead = 'mark-all-read',\n Opened = 'opened',\n Read = 'read',\n Unarchive = 'unarchive',\n Unopened = 'unopened',\n Unread = 'unread',\n}\n\n/** Envelope for an inbox message event. */\nexport interface InboxMessageEventEnvelope {\n /** Event type indicating a new message, or a mutation to one or more existing messages. */\n event: InboxMessageEvent;\n\n /**\n * Optional:Message ID.\n *\n * messageId is present for events that mutate a single message (e.g. read, unread, archive, etc.).\n */\n messageId?: string;\n\n /** Optional: Message data, varying by event.\n *\n * For {@link InboxMessageEvent.NewMessage}, this is an {@link InboxMessage}.\n * For other events this is undefined.\n */\n data?: InboxMessage;\n}\n\n/** Message sent by the server to indicate that the client should reconnect. */\nexport interface ReconnectMessage {\n /** Event type indicating a reconnection. */\n event: 'reconnect';\n\n /** Message describing the reason for the reconnection. */\n message: string;\n\n /** Seconds after which the client should retry the connection. */\n retryAfter: number;\n\n /** https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent/code */\n code: number;\n}\n\n/** Configuration for the client. */\nexport interface Config {\n /** The time interval in milliseconds between client ping messages to the server. */\n pingInterval: number;\n\n /**\n * Maximum number of outstanding pings before the client should\n * close the connection and retry connecting.\n */\n maxOutstandingPings: number;\n}\n\n/** Envelope for a config response. */\nexport interface ConfigResponseEnvelope {\n /** Transaction ID. */\n tid: string;\n\n response: 'config';\n\n /** Configuration data for the client. */\n data: Config;\n}\n\nexport type ServerMessage =\n | ConfigResponseEnvelope\n | InboxMessageEventEnvelope\n | ReconnectMessage\n | ServerActionEnvelope\n | ServerResponseEnvelope;\n","export interface CourierApiUrls {\n courier: {\n rest: string;\n graphql: string;\n },\n inbox: {\n graphql: string;\n webSocket: string;\n }\n}\n\nexport const getCourierApiUrls = (urls?: CourierApiUrls): CourierApiUrls => ({\n courier: {\n rest: urls?.courier.rest || 'https://api.courier.com',\n graphql: urls?.courier.graphql || 'https://api.courier.com/client/q',\n },\n inbox: {\n graphql: urls?.inbox.graphql || 'https://inbox.courier.com/q',\n webSocket: urls?.inbox.webSocket || 'wss://realtime.courier.io'\n }\n});","export class Logger {\n\n private readonly PREFIX = '[COURIER]';\n\n constructor(private readonly showLogs: boolean) { }\n\n public warn(message: string, ...args: any[]): void {\n if (this.showLogs) {\n console.warn(`${this.PREFIX} ${message}`, ...args);\n }\n }\n\n public log(message: string, ...args: any[]): void {\n if (this.showLogs) {\n console.log(`${this.PREFIX} ${message}`, ...args);\n }\n }\n\n public error(message: string, ...args: any[]): void {\n if (this.showLogs) {\n console.error(`${this.PREFIX} ${message}`, ...args);\n }\n }\n\n public debug(message: string, ...args: any[]): void {\n if (this.showLogs) {\n console.debug(`${this.PREFIX} ${message}`, ...args);\n }\n }\n\n public info(message: string, ...args: any[]): void {\n if (this.showLogs) {\n console.info(`${this.PREFIX} ${message}`, ...args);\n }\n }\n}\n","export class UUID {\n\n private static readonly ALPHABET = 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict';\n\n /**\n * nanoid\n * Copyright 2017 Andrey Sitnik <andrey@sitnik.ru>\n *\n * https://github.com/ai/nanoid/blob/main/LICENSE\n *\n * @param size - The size of the UUID to generate.\n * @returns A string representing the UUID.\n */\n static nanoid(size: number = 21): string {\n let id = '';\n let bytes = crypto.getRandomValues(new Uint8Array((size |= 0)));\n\n while (size--) {\n // Using the bitwise AND operator to \"cap\" the value of\n // the random byte from 255 to 63, in that way we can make sure\n // that the value will be a valid index for the \"chars\" string.\n id += UUID.ALPHABET[bytes[size] & 63]\n }\n return id;\n }\n\n}","import { CourierClientOptions } from \"../client/courier-client\";\nimport { Logger } from \"./logger\";\nimport { UUID } from \"./uuid\";\n\nexport class CourierRequestError extends Error {\n constructor(\n public code: number,\n message: string,\n public type?: string\n ) {\n super(message);\n this.name = 'CourierRequestError';\n }\n}\n\nfunction logRequest(logger: Logger, uid: string, type: 'HTTP' | 'GraphQL', data: {\n url: string;\n method?: string;\n headers: Record<string, string>;\n body?: any;\n query?: string;\n variables?: Record<string, any>;\n}) {\n logger.log(`\nšŸ“” New Courier ${type} Request: ${uid}\nURL: ${data.url}\n${data.method ? `Method: ${data.method}` : ''}\n${data.query ? `Query: ${data.query}` : ''}\n${data.variables ? `Variables: ${JSON.stringify(data.variables, null, 2)}` : ''}\nHeaders: ${JSON.stringify(data.headers, null, 2)}\nBody: ${data.body ? JSON.stringify(data.body, null, 2) : 'Empty'}\n `);\n}\n\nfunction logResponse(logger: Logger, uid: string, type: 'HTTP' | 'GraphQL', data: {\n status: number;\n response: any;\n}) {\n logger.log(`\nšŸ“” New Courier ${type} Response: ${uid}\nStatus Code: ${data.status}\nResponse JSON: ${JSON.stringify(data.response, null, 2)}\n `);\n}\n\nexport async function http(props: {\n url: string,\n options: CourierClientOptions,\n method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH',\n headers?: Record<string, string>,\n body?: any,\n validCodes?: number[]\n}): Promise<any> {\n const validCodes = props.validCodes ?? [200];\n const uid = props.options.showLogs ? UUID.nanoid() : undefined;\n\n // Create request\n const request = new Request(props.url, {\n method: props.method,\n headers: {\n 'Content-Type': 'application/json',\n ...props.headers\n },\n body: props.body ? JSON.stringify(props.body) : undefined\n });\n\n // Log request if enabled\n if (uid) {\n logRequest(props.options.logger, uid, 'HTTP', {\n url: request.url,\n method: request.method,\n headers: Object.fromEntries(request.headers.entries()),\n body: props.body\n });\n }\n\n // Perform request\n const response = await fetch(request);\n\n // Handle empty responses (like 204 No Content)\n if (response.status === 204) {\n return;\n }\n\n // Try to parse JSON response\n let data;\n try {\n data = await response.json();\n } catch (error) {\n\n // Weird fallback for only tracking url events :facepalm:\n if (response.status === 200) {\n return;\n }\n\n throw new CourierRequestError(\n response.status,\n 'Failed to parse response as JSON',\n 'PARSE_ERROR'\n );\n }\n\n // Log response if enabled\n if (uid) {\n logResponse(props.options.logger, uid, 'HTTP', {\n status: response.status,\n response: data\n });\n }\n\n // Handle invalid status codes\n if (!validCodes.includes(response.status)) {\n throw new CourierRequestError(\n response.status,\n data?.message || 'Unknown Error',\n data?.type\n );\n }\n\n return data;\n}\n\nexport async function graphql(props: {\n url: string,\n options: CourierClientOptions,\n headers: Record<string, string>,\n query: string,\n variables?: Record<string, any>\n}): Promise<any> {\n const uid = props.options.showLogs ? UUID.nanoid() : undefined;\n\n // Log request if enabled\n if (uid) {\n logRequest(props.options.logger, uid, 'GraphQL', {\n url: props.url,\n headers: props.headers,\n query: props.query,\n variables: props.variables\n });\n }\n\n const response = await fetch(props.url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...props.headers\n },\n body: JSON.stringify({\n query: props.query,\n variables: props.variables\n })\n });\n\n // Try to parse JSON response\n let data;\n try {\n data = await response.json();\n } catch (error) {\n throw new CourierRequestError(\n response.status,\n 'Failed to parse response as JSON',\n 'PARSE_ERROR'\n );\n }\n\n // Log response if enabled\n if (uid) {\n logResponse(props.options.logger, uid, 'GraphQL', {\n status: response.status,\n response: data\n });\n }\n\n if (!response.ok) {\n throw new CourierRequestError(\n response.status,\n data?.message || 'Unknown Error',\n data?.type\n );\n }\n\n return data;\n}\n","import { CourierClientOptions } from \"./courier-client\";\n\nexport class Client {\n\n constructor(public readonly options: CourierClientOptions) { }\n\n}\n","import { CourierBrand } from '../types/brands';\nimport { graphql } from '../utils/request';\nimport { Client } from './client';\n\nexport class BrandClient extends Client {\n\n /**\n * Get a brand by ID using GraphQL\n * @param brandId - The ID of the brand to retrieve\n * @returns Promise resolving to the requested brand\n */\n public async getBrand(props: { brandId: string }): Promise<CourierBrand> {\n const query = `\n query GetBrand {\n brand(brandId: \"${props.brandId}\") {\n settings {\n colors {\n primary\n secondary\n tertiary\n }\n inapp {\n borderRadius\n disableCourierFooter\n }\n }\n }\n }\n `;\n\n const json = await graphql({\n options: this.options,\n url: this.options.apiUrls.courier.graphql,\n headers: {\n 'x-courier-user-id': this.options.userId,\n 'x-courier-client-key': 'empty', // Empty for now. Will be removed in future.\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n query,\n variables: { brandId: props.brandId }\n });\n\n return json.data.brand as CourierBrand;\n }\n\n}\n","/**\n * Connection close code for non-error conditions.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent/code\n */\nexport const CLOSE_CODE_NORMAL_CLOSURE = 1000;\n\n/**\n * Courier-specific close event.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent\n */\nexport interface CourierCloseEvent extends CloseEvent {\n /** The number of seconds to wait before retrying the connection. */\n retryAfterSeconds?: number;\n}\n","export const INBOX_WIRE_PROTOCOL_VERSION = 'v1';\n","import { CourierClientOptions } from \"../client/courier-client\";\nimport { CLOSE_CODE_NORMAL_CLOSURE, CourierCloseEvent } from \"../types/socket/protocol/errors\";\nimport { ServerMessage } from \"../types/socket/protocol/messages\";\nimport { Logger } from \"../utils/logger\";\nimport { INBOX_WIRE_PROTOCOL_VERSION } from \"./version\";\n\n/**\n * Abstract base class for Courier WebSocket implementations.\n *\n * The base class handles the connection and close events, as well as retry logic.\n * Application-specific logic should be implemented in the concrete classes.\n */\nexport abstract class CourierSocket {\n /**\n * The jitter factor for the backoff intervals.\n *\n * Backoff with jitter is calculated as a random value in the range:\n * [BACKOFF_INTERVAL - BACKOFF_JITTER_FACTOR * BACKOFF_INTERVAL,\n * BACKOFF_INTERVAL + BACKOFF_JITTER_FACTOR * BACKOFF_INTERVAL).\n */\n private static readonly BACKOFF_JITTER_FACTOR = 0.5;\n\n /**\n * The maximum number of retry attempts.\n */\n private static readonly MAX_RETRY_ATTEMPTS = 5;\n\n /**\n * Backoff intervals in milliseconds.\n *\n * Each represents an offset from the previous interval, rather than a\n * absolute offset from the initial request time.\n */\n private static readonly BACKOFF_INTERVALS_IN_MILLIS = [\n 30_000, // 30 seconds\n 60_000, // 1 minute\n 120_000, // 2 minutes\n 240_000, // 4 minutes\n 480_000, // 8 minutes\n ];\n\n /**\n * The key of the retry after time in the WebSocket close event reason.\n *\n * The Courier WebSocket server may send the close event reason in the following format:\n *\n * ```json\n * {\n * \"Retry-After\": \"10\" // The retry after time in seconds\n * }\n * ```\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent/reason\n */\n private static readonly RETRY_AFTER_KEY = 'Retry-After';\n\n /** The WebSocket instance, which may be null if the connection is not established. */\n private webSocket: WebSocket | null = null;\n\n /** The number of connection retry attempts so far, reset after a successful connection. */\n private retryAttempt: number = 0;\n\n /** The timeout ID for the current connectionretry attempt, reset when we attempt to connect. */\n private retryTimeoutId: number | null = null;\n\n /**\n * Flag indicating the application initiated a {@link CourierSocket#close} call.\n *\n * An application-initiated close may look like an abnormal closure (code 1006)\n * if it occurs before the connection is established. We differentiate to\n * prevent retrying the connection when the socket is closed intentionally.\n */\n private closeRequested: boolean = false;\n\n private readonly url: string;\n private readonly options: CourierClientOptions;\n\n constructor(\n options: CourierClientOptions\n ) {\n this.url = options.apiUrls.inbox.webSocket;\n this.options = options;\n }\n\n /**\n * Connects to the Courier WebSocket server.\n *\n * If the connection is already established, this is a no-op.\n *\n * @returns A promise that resolves when the connection is established or rejects if the connection could not be established.\n */\n public async connect(): Promise<void> {\n if (this.isConnecting || this.isOpen) {\n this.options.logger?.info(`Attempted to open a WebSocket connection, but one already exists in state '${this.webSocket?.readyState}'.`);\n\n // This isn't necessarily an error (the result is a no-op), so we resolve the promise.\n return Promise.resolve();\n }\n\n // If we're in the process of retrying, clear the timeout to prevent further retries.\n this.clearRetryTimeout();\n\n // Reset the close requested flag when we attempt to connect.\n this.closeRequested = false;\n\n return new Promise((resolve, reject) => {\n this.webSocket = new WebSocket(this.getWebSocketUrl());\n\n this.webSocket.addEventListener('open', (event: Event) => {\n // Reset the retry attempt counter when the connection is established.\n this.retryAttempt = 0;\n\n this.onOpen(event);\n\n // Resolve the promise when the WebSocket is opened (i.e. the connection is established)\n resolve();\n });\n\n this.webSocket.addEventListener('message', async (event: MessageEvent) => {\n try {\n const json = JSON.parse(event.data) as ServerMessage;\n if ('event' in json && json.event === 'reconnect') {\n this.close(CLOSE_CODE_NORMAL_CLOSURE);\n await this.retryConnection(json.retryAfter * 1000);\n return;\n }\n\n this.onMessageReceived(json)\n } catch (error) {\n this.options.logger?.error('Error parsing socket message', error);\n }\n });\n\n this.webSocket.addEventListener('close', (event: CloseEvent) => {\n // Close events are fired when the connection is closed either normally or abnormally.\n //\n // The 'close' event triggers a retry if the 'close' is:\n // 1) not a normal closure and,\n // 2) the application did not request the close (see CourierSocket#closeRequested)\n if (event.code !== CLOSE_CODE_NORMAL_CLOSURE && !this.closeRequested) {\n const courierCloseEvent = CourierSocket.parseCloseEvent(event);\n\n if (courierCloseEvent.retryAfterSeconds) {\n this.retryConnection(courierCloseEvent.retryAfterSeconds * 1000);\n } else {\n this.retryConnection();\n }\n }\n\n this.onClose(event);\n });\n\n this.webSocket.addEventListener('error', (event: Event) => {\n // If the closure was requested by the application, don't retry the connection.\n // The error event may be fired for a normal closure if it occurs before the connection is established.\n if (!this.closeRequested) {\n this.retryConnection();\n }\n\n this.onError(event);\n\n // If the HTTP Upgrade request fails, the WebSocket API fires an error event,\n // so we reject the promise to indicate that the connection could not be established.\n reject(event);\n });\n });\n }\n\n /**\n * Closes the WebSocket connection.\n *\n * See {@link https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close} for more details.\n *\n * @param code The WebSocket close code. Defaults to {@link CLOSE_CODE_NORMAL_CLOSURE}.\n * @param reason The WebSocket close reason.\n */\n public close(code = CLOSE_CODE_NORMAL_CLOSURE, reason?: string): void {\n if (this.webSocket === null) {\n return;\n }\n\n this.closeRequested = true;\n\n // Cancel any pending retries and reset the retry attempt counter.\n this.clearRetryTimeout();\n this.retryAttempt = 0;\n\n this.webSocket.close(code, reason);\n\n }\n\n /**\n * Sends a message to the Courier WebSocket server.\n *\n * @param message The message to send. The message will be serialized to a JSON string.\n */\n public send(message: Record<string, any>): void {\n if (this.webSocket === null || this.isConnecting) {\n this.options.logger?.info('Attempted to send a message, but the WebSocket is not yet open.');\n return;\n }\n\n const json = JSON.stringify(message);\n this.webSocket.send(json);\n }\n\n protected get userId(): string {\n return this.options.userId;\n }\n\n /** The sub-tenant ID, if specified by the user. */\n protected get subTenantId(): string | undefined {\n return this.options.tenantId;\n }\n\n protected get logger(): Logger | undefined {\n return this.options.logger;\n }\n\n /**\n * Called when the WebSocket connection is established with the Courier WebSocket server.\n *\n * @param event The WebSocket open event.\n */\n public abstract onOpen(event: Event): Promise<void>;\n\n /**\n * Called when a message is received from the Courier WebSocket server.\n *\n * @param data The message received.\n */\n public abstract onMessageReceived(data: ServerMessage): Promise<void>;\n\n /**\n * Called when the WebSocket connection is closed.\n *\n * @param event The WebSocket close event.\n */\n public abstract onClose(event: CloseEvent): Promise<void>;\n\n /**\n * Called when an error occurs on the WebSocket connection.\n *\n * @param event The WebSocket error event.\n */\n public abstract onError(event: Event): Promise<void>;\n\n /**\n * Whether the WebSocket connection is currently being established.\n */\n public get isConnecting(): boolean {\n return this.webSocket !== null && this.webSocket.readyState === WebSocket.CONNECTING;\n }\n\n /**\n * Whether the WebSocket connection is currently open.\n */\n public get isOpen(): boolean {\n return this.webSocket !== null && this.webSocket.readyState === WebSocket.OPEN;\n }\n\n /**\n * Constructs the WebSocket URL for the Courier WebSocket server using context\n * from the {@link CourierClientOptions} passed to the constructor.\n *\n * @returns The WebSocket URL\n */\n private getWebSocketUrl(): string {\n const accessToken = this.options.accessToken;\n const connectionId = this.options.connectionId;\n const userId = this.userId;\n\n return `${this.url}?auth=${accessToken}&cid=${connectionId}&iwpv=${INBOX_WIRE_PROTOCOL_VERSION}&userId=${userId}`;\n }\n\n /**\n * Parses the Retry-After time from the WebSocket close event reason,\n * and returns a new {@link CourierCloseEvent} with the retry after time in seconds\n * if present.\n *\n * The Courier WebSocket server may send the close event reason in the following format:\n *\n * ```json\n * {\n * \"Retry-After\": \"10\" // The retry after time in seconds\n * }\n * ```\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent/reason\n *\n * @param closeEvent The WebSocket close event.\n * @returns The WebSocket close event with the retry after time in seconds.\n */\n private static parseCloseEvent(closeEvent: CloseEvent): CourierCloseEvent {\n if (closeEvent.reason === null || closeEvent.reason === '') {\n return closeEvent;\n }\n\n try {\n const jsonReason = JSON.parse(closeEvent.reason);\n if (!jsonReason[CourierSocket.RETRY_AFTER_KEY]) {\n return closeEvent;\n }\n\n const retryAfterSeconds = parseInt(jsonReason[CourierSocket.RETRY_AFTER_KEY]);\n if (Number.isNaN(retryAfterSeconds) || retryAfterSeconds < 0) {\n return closeEvent;\n }\n\n return {\n ...closeEvent,\n retryAfterSeconds,\n };\n } catch (error) {\n return closeEvent;\n }\n }\n\n /**\n * Calculates the retry backoff time in milliseconds based on the current retry attempt.\n */\n private getBackoffTimeInMillis(): number {\n const backoffIntervalInMillis = CourierSocket.BACKOFF_INTERVALS_IN_MILLIS[this.retryAttempt];\n const lowerBound = backoffIntervalInMillis - (backoffIntervalInMillis * CourierSocket.BACKOFF_JITTER_FACTOR);\n const upperBound = backoffIntervalInMillis + (backoffIntervalInMillis * CourierSocket.BACKOFF_JITTER_FACTOR);\n\n return Math.floor(Math.random() * (upperBound - lowerBound) + lowerBound);\n }\n\n /**\n * Retries the connection to the Courier WebSocket server after\n * either {@param suggestedBackoffTimeInMillis} or a random backoff time\n * calculated using {@link getBackoffTimeInMillis}.\n *\n * @param suggestedBackoffTimeInMillis The suggested backoff time in milliseconds.\n * @returns A promise that resolves when the connection is established or rejects if the connection could not be established.\n */\n protected async retryConnection(suggestedBackoffTimeInMillis?: number): Promise<void> {\n if (this.retryTimeoutId !== null) {\n this.logger?.debug('Skipping retry attempt because a previous retry is already scheduled.');\n return;\n }\n\n if (this.retryAttempt >= CourierSocket.MAX_RETRY_ATTEMPTS) {\n this.logger?.error(`Max retry attempts (${CourierSocket.MAX_RETRY_ATTEMPTS}) reached.`);\n return;\n }\n\n const backoffTimeInMillis = suggestedBackoffTimeInMillis ?? this.getBackoffTimeInMillis();\n this.retryTimeoutId = window.setTimeout(async () => {\n try {\n await this.connect();\n } catch (error) {\n // connect() will retry if applicable\n }\n }, backoffTimeInMillis);\n this.logger?.debug(`Retrying connection in ${Math.floor(backoffTimeInMillis / 1000)}s. Retry attempt ${this.retryAttempt + 1} of ${CourierSocket.MAX_RETRY_ATTEMPTS}.`);\n\n this.retryAttempt++;\n }\n\n /**\n * Clears the retry timeout if it exists.\n */\n private clearRetryTimeout(): void {\n if (this.retryTimeoutId !== null) {\n window.clearTimeout(this.retryTimeoutId);\n this.retryTimeoutId = null;\n }\n }\n}\n","import { ClientMessageEnvelope, ServerMessage } from \"../types/socket/protocol/messages\";\n\nexport class TransactionManager {\n /**\n * The map of <transactionId, Transaction> representing outstanding requests.\n */\n private readonly outstandingRequestsMap: Map<string, Transaction> = new Map();\n\n /**\n * The queue of completed requests. This is a FIFO queue of the last N completed requests,\n * where N is {@link completedTransactionsToKeep}.\n */\n private readonly completedTransactionsQueue: Transaction[] = [];\n\n /**\n * Number of completed requests to keep in memory.\n */\n private readonly completedTransactionsToKeep: number;\n\n constructor(completedTransactionsToKeep: number = 10) {\n this.completedTransactionsToKeep = completedTransactionsToKeep;\n }\n\n public addOutstandingRequest(transactionId: string, request: ClientMessageEnvelope): void {\n const isOutstanding = this.outstandingRequestsMap.has(transactionId);\n if (isOutstanding) {\n throw new Error(`Transaction [${transactionId}] already has an outstanding request`);\n }\n\n const transaction: Transaction = {\n transactionId,\n request,\n response: null,\n start: new Date(),\n end: null,\n };\n\n this.outstandingRequestsMap.set(transactionId, transaction);\n }\n\n public addResponse(transactionId: string, response: ServerMessage): void {\n const transaction = this.outstandingRequestsMap.get(transactionId);\n if (transaction === undefined) {\n throw new Error(`Transaction [${transactionId}] does not have an outstanding request`);\n }\n\n transaction.response = response;\n transaction.end = new Date();\n\n // Move the transaction from the outstanding requests to the completed requests.\n this.outstandingRequestsMap.delete(transactionId);\n this.addCompletedTransaction(transaction);\n }\n\n public get outstandingRequests(): Transaction[] {\n return Array.from(this.outstandingRequestsMap.values());\n }\n\n public get completedTransactions(): Transaction[] {\n return this.completedTransactionsQueue;\n }\n\n public clearOutstandingRequests(): void {\n this.outstandingRequestsMap.clear();\n }\n\n /**\n * Adds a completed request to the queue.\n *\n * If the number of completed requests exceeds the maximum number of completed requests to keep,\n * remove the oldest completed request.\n */\n private addCompletedTransaction(transaction: Transaction): void {\n this.completedTransactionsQueue.push(transaction);\n\n if (this.completedTransactionsQueue.length > this.completedTransactionsToKeep) {\n this.completedTransactionsQueue.shift();\n }\n }\n}\n\ninterface Transaction {\n /**\n * The transaction ID.\n */\n transactionId: string;\n\n /**\n * The request to the server.\n */\n request: ClientMessageEnvelope;\n\n /**\n * The response to the request.\n *\n * The response is null until the request is completed.\n */\n response: ServerMessage | null;\n\n /**\n * The start time of the transaction.\n */\n start: Date;\n\n /**\n * The end time of the transaction.\n */\n end: Date | null;\n}\n","import { InboxMessage } from \"../types/inbox\";\nimport { InboxMessageEvent, InboxMessageEventEnvelope } from \"../types/socket/protocol/messages\";\n\n/**\n * Ensure the `created` timestamp is set for a new message.\n *\n * New messages received from the WebSocket may not have a created time,\n * until they are retrieved from the GraphQL API.\n *\n * @param envelope - The envelope containing the message event.\n * @returns The envelope with the created time set.\n */\nfunction ensureCreatedTime(envelope: InboxMessageEventEnvelope): InboxMessageEventEnvelope {\n if (envelope.event === InboxMessageEvent.NewMessage) {\n const message = envelope.data as InboxMessage;\n\n if (!message.created) {\n message.created = new Date().toISOString();\n }\n\n return {\n ...envelope,\n data: message,\n };\n }\n\n return envelope;\n}\n\n/**\n * Apply fixes to a message event envelope.\n *\n * @param envelope - The envelope containing the message event.\n * @returns The envelope with the fixes applied.\n */\nexport function fixMessageEventEnvelope(envelope: InboxMessageEventEnvelope): InboxMessageEventEnvelope {\n // Apply any fixes to the message event envelope.\n return ensureCreatedTime(envelope);\n}\n","import { CourierClientOptions } from '../client/courier-client';\nimport { ClientAction, ClientMessageEnvelope, Config, ConfigResponseEnvelope, InboxMessageEvent, InboxMessageEventEnvelope, ServerAction, ServerActionEnvelope, ServerMessage, ServerResponseEnvelope } from '../types/socket/protocol/messages';\nimport { CLOSE_CODE_NORMAL_CLOSURE } from '../types/socket/protocol/errors';\nimport { UUID } from '../utils/uuid';\nimport { CourierSocket } from './courier-socket';\nimport { TransactionManager } from './courier-inbox-transaction-manager';\nimport { fixMessageEventEnvelope } from './inbox-message-utils';\n\n/** Application-layer implementation of the Courier WebSocket API for Inbox messages. */\nexport class CourierInboxSocket extends CourierSocket {\n /**\n * The default interval in milliseconds at which to send a ping message to the server\n * if no other message has been received from the server.\n *\n * Fallback when the server does not provide a config.\n */\n private static readonly DEFAULT_PING_INTERVAL_MILLIS = 60_000; // 1 minute\n\n /**\n * The default maximum number of outstanding pings before the client should\n * close the connection and retry connecting.\n *\n * Fallback when the server does not provide a config.\n */\n private static readonly DEFAULT_MAX_OUTSTANDING_PINGS = 3;\n\n /**\n * The interval ID for the ping interval.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/setInterval\n */\n private pingIntervalId: number | null = null;\n\n /**\n * The list of message event listeners, called when a message event is received\n * from the Courier WebSocket server.\n */\n private messageEventListeners: ((message: InboxMessageEventEnvelope) => void)[] = [];\n\n /** Server-provided configuration for the client. */\n private config: Config | null = null;\n\n /**\n * The transaction manager, used to track outstanding requests and responses.\n */\n private readonly pingTransactionManager: TransactionManager = new TransactionManager();\n\n constructor(options: CourierClientOptions) {\n super(options);\n }\n\n public onOpen(_: Event): Promise<void> {\n // Clear any outstanding pings from the previous connection before starting to ping.\n this.pingTransactionManager.clearOutstandingRequests();\n this.restartPingInterval();\n\n // Send a request for the client's configuration.\n this.sendGetConfig();\n\n // Subscribe to all events for the user.\n this.sendSubscribe();\n\n return Promise.resolve();\n }\n\n public onMessageReceived(data: ServerMessage): Promise<void> {\n // ServerActionEnvelope\n // Respond to pings.\n if ('action' in data && data.action === ServerAction.Ping) {\n const envelope: ServerActionEnvelope = data as ServerActionEnvelope;\n this.sendPong(envelope);\n }\n\n // ServerResponseEnvelope\n // Track pongs.\n if ('response' in data && data.response === 'pong') {\n const envelope: ServerResponseEnvelope = data as ServerResponseEnvelope;\n\n // Keep track of the pong response and clear out any outstanding pings.\n // We only need to keep track of the most recent missed pings.\n this.pingTransactionManager.addResponse(envelope.tid, envelope);\n this.pingTransactionManager.clearOutstandingRequests();\n }\n\n // ConfigResponseEnvelope\n // Update the client's config.\n if ('response' in data && data.response === 'config') {\n const envelope: ConfigResponseEnvelope = data as ConfigResponseEnvelope;\n this.setConfig(envelope.data);\n }\n\n // InboxMessageEventEnvelope\n // Handle message events, calling all registered listeners.\n if ('event' in data && CourierInboxSocket.isInboxMessageEvent(data.event)) {\n const envelope: InboxMessageEventEnvelope = data as InboxMessageEventEnvelope;\n const fixedEnvelope = fixMessageEventEnvelope(envelope);\n for (const listener of this.messageEventListeners) {\n listener(fixedEnvelope);\n }\n }\n\n // Restart the ping interval if a message is received from the server.\n this.restartPingInterval();\n\n return Promise.resolve();\n }\n\n public onClose(_: CloseEvent): Promise<void> {\n // Cancel scheduled pings.\n this.clearPingInterval();\n\n // Remove any message event listeners.\n this.clearMessageEventListeners();\n\n // Clear any outstanding pings.\n this.pingTransactionManager.clearOutstandingRequests();\n\n return Promise.resolve();\n }\n\n public onError(_: Event): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Sends a subscribe message to the server.\n *\n * Subscribes to all events for the user.\n */\n public sendSubscribe(): void {\n const data: Record<string, any> = {\n channel: this.userId,\n event: '*',\n };\n\n // Set accountId to the sub-tenant ID if it is specified.\n if (this.subTenantId) {\n data.accountId = this.subTenantId;\n }\n\n const envelope: ClientMessageEnvelope = {\n tid: UUID.nanoid(),\n action: ClientAction.Subscribe,\n data,\n };\n\n this.send(envelope);\n }\n\n /**\n * Sends an unsubscribe message to the server.\n *\n * Unsubscribes from all events for the user.\n */\n public sendUnsubscribe(): void {\n const envelope: ClientMessageEnvelope = {\n tid: UUID.nanoid(),\n action: ClientAction.Unsubscribe,\n data: {\n channel: this.userId,\n },\n };\n\n this.send(envelope);\n }\n\n /**\n * Adds a message event listener, called when a message event is received\n * from the Courier WebSocket server.\n *\n * @param listener The listener function\n */\n public addMessageEventListener(listener: (message: InboxMessageEventEnvelope) => void): void {\n this.messageEventListeners.push(listener);\n }\n\n /**\n * Send a ping message to the server.\n *\n * ping/pong is implemented at the application layer since the browser's\n * WebSocket implementation does not support control-level ping/pong.\n */\n private sendPing(): void {\n if (this.pingTransactionManager.outstandingRequests.length >= this.maxOutstandingPings) {\n this.logger?.debug('Max outstanding pings reached, retrying connection.');\n this.close(CLOSE_CODE_NORMAL_CLOSURE, 'Max outstanding pings reached, retrying connection.');\n this.retryConnection();\n\n return;\n }\n\n const envelope: ClientMessageEnvelope = {\n tid: UUID.nanoid(),\n action: ClientAction.Ping,\n };\n\n this.send(envelope);\n this.pingTransactionManager.addOutstandingRequest(envelope.tid, envelope);\n }\n\n /**\n * Send a pong response to the server.\n *\n * ping/pong is implemented at the application layer since the browser's\n * WebSocket implementation does not support control-level ping/pong.\n */\n private sendPong(incomingMessage: ServerActionEnvelope): void {\n const response: ClientMessageEnvelope = {\n tid: incomingMessage.tid,\n action: ClientAction.Pong,\n };\n\n this.send(response);\n }\n\n /**\n * Send a request for the client's configuration.\n */\n private sendGetConfig(): void {\n const envelope: ClientMessageEnvelope = {\n tid: UUID.nanoid(),\n action: ClientAction.GetConfig,\n };\n\n this.send(envelope);\n }\n\n /**\n * Restart the ping interval, clearing the previous interval if it exists.\n */\n private restartPingInterval(): void {\n this.clearPingInterval();\n\n this.pingIntervalId = window.setInterval(() => {\n this.sendPing();\n }, this.pingInterval);\n }\n\n private clearPingInterval(): void {\n if (this.pingIntervalId) {\n window.clearInterval(this.pingIntervalId);\n }\n }\n\n private get pingInterval(): number {\n if (this.config) {\n // Server-provided ping interval is in seconds.\n return this.config.pingInterval * 1000;\n }\n\n return CourierInboxSocket.DEFAULT_PING_INTERVAL_MILLIS;\n }\n\n private get maxOutstandingPings(): number {\n if (this.config) {\n return this.config.maxOutstandingPings;\n }\n\n return CourierInboxSocket.DEFAULT_MAX_OUTSTANDING_PINGS;\n }\n\n private setConfig(config: Config): void {\n this.config = config;\n }\n\n /**\n * Removes all message event listeners.\n */\n private clearMessageEventListeners(): void {\n this.messageEventListeners = [];\n }\n\n private static isInboxMessageEvent(event: string): event is InboxMessageEvent {\n return Object.values(InboxMessageEvent).includes(event as InboxMessageEvent);\n }\n}\n","import { CourierInboxSocket } from '../socket/courier-inbox-socket';\nimport { CourierGetInboxMessagesResponse } from '../types/inbox';\nimport { graphql } from '../utils/request';\nimport { Client } from './client';\nimport { CourierClientOptions } from './courier-client';\n\nexport class InboxClient extends Client {\n\n readonly socket: CourierInboxSocket;\n\n constructor(options: CourierClientOptions) {\n super(options);\n this.socket = new CourierInboxSocket(options);\n }\n\n /**\n * Get paginated messages\n * @param paginationLimit - Number of messages to return per page (default: 24)\n * @param startCursor - Cursor for pagination\n * @returns Promise resolving to paginated messages response\n */\n public async getMessages(props?: { paginationLimit?: number; startCursor?: string; }): Promise<CourierGetInboxMessagesResponse> {\n const query = `\n query GetInboxMessages(\n $params: FilterParamsInput = { ${this.options.tenantId ? `accountId: \"${this.options.tenantId}\"` : ''} }\n $limit: Int = ${props?.paginationLimit ?? 24}\n $after: String ${props?.startCursor ? `= \"${props.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 `;\n\n return await graphql({\n options: this.options,\n query,\n headers: {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Get paginated archived messages\n * @param paginationLimit - Number of messages to return per page (default: 24)\n * @param startCursor - Cursor for pagination\n * @returns Promise resolving to paginated archived messages response\n */\n public async getArchivedMessages(props?: { paginationLimit?: number; startCursor?: string; }): Promise<CourierGetInboxMessagesResponse> {\n const query = `\n query GetInboxMessages(\n $params: FilterParamsInput = { ${this.options.tenantId ? `accountId: \"${this.options.tenantId}\"` : ''}, archived: true }\n $limit: Int = ${props?.paginationLimit ?? 24}\n $after: String ${props?.startCursor ? `= \"${props.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 `;\n\n return graphql({\n options: this.options,\n query,\n headers: {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Get unread message count\n * @returns Promise resolving to number of unread messages\n */\n public async getUnreadMessageCount(): Promise<number> {\n const query = `\n query GetMessages {\n count(params: { status: \"unread\" ${this.options.tenantId ? `, accountId: \"${this.options.tenantId}\"` : ''} })\n }\n `;\n\n const response = await graphql({\n options: this.options,\n query,\n headers: {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n url: this.options.apiUrls.inbox.graphql,\n });\n\n return response.data?.count ?? 0;\n }\n\n /**\n * Track a click event\n * @param messageId - ID of the message\n * @param trackingId - ID for tracking the click\n * @returns Promise resolving when click is tracked\n */\n public async click(props: { messageId: string, trackingId: string }): Promise<void> {\n const query = `\n mutation TrackEvent {\n clicked(messageId: \"${props.messageId}\", trackingId: \"${props.trackingId}\")\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Mark a message as read\n * @param messageId - ID of the message to mark as read\n * @returns Promise resolving when message is marked as read\n */\n public async read(props: { messageId: string }): Promise<void> {\n const query = `\n mutation TrackEvent {\n read(messageId: \"${props.messageId}\")\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Mark a message as unread\n * @param messageId - ID of the message to mark as unread\n * @returns Promise resolving when message is marked as unread\n */\n public async unread(props: { messageId: string }): Promise<void> {\n const query = `\n mutation TrackEvent {\n unread(messageId: \"${props.messageId}\")\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Mark a message as opened\n * @param messageId - ID of the message to mark as opened\n * @returns Promise resolving when message is marked as opened\n */\n public async open(props: { messageId: string }): Promise<void> {\n const query = `\n mutation TrackEvent {\n opened(messageId: \"${props.messageId}\")\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Archive a message\n * @param messageId - ID of the message to archive\n * @returns Promise resolving when message is archived\n */\n public async archive(props: { messageId: string }): Promise<void> {\n const query = `\n mutation TrackEvent {\n archive(messageId: \"${props.messageId}\")\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Unarchive a message\n * @param messageId - ID of the message to unarchive\n * @returns Promise resolving when message is unarchived\n */\n public async unarchive(props: { messageId: string }): Promise<void> {\n const query = `\n mutation TrackEvent {\n unarchive(messageId: \"${props.messageId}\")\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Mark all messages as read\n * @returns Promise resolving when all messages are marked as read\n */\n public async readAll(): Promise<void> {\n const query = `\n mutation TrackEvent {\n markAllRead\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Archive all read messages.\n */\n public async archiveRead(): Promise<void> {\n const query = `\n mutation TrackEvent {\n archiveRead\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n /**\n * Archive all read messages.\n */\n public async archiveAll(): Promise<void> {\n const query = `\n mutation TrackEvent {\n archiveAll\n }\n `;\n\n const headers: Record<string, string> = {\n 'x-courier-user-id': this.options.userId,\n 'Authorization': `Bearer ${this.options.accessToken}`\n };\n\n if (this.options.connectionId) {\n headers['x-courier-client-source-id'] = this.options.connectionId;\n }\n\n await graphql({\n options: this.options,\n query,\n headers,\n url: this.options.apiUrls.inbox.graphql,\n });\n }\n\n}\n","export type CourierUserPreferencesStatus = 'OPTED_IN' | 'OPTED_OUT' | 'REQUIRED' | 'UNKNOWN';\n\nexport type CourierUserPreferencesChannel = 'direct_message' | 'inbox' | 'email' | 'push' | 'sms' | 'webhook' | 'unknown';\n\nexport interface CourierUserPreferencesPaging {\n cursor?: string;\n more: boolean;\n}\n\nexport interface CourierUserPreferencesTopic {\n topicId: string;\n topicName: string;\n sectionId: string;\n sectionName: string;\n status: CourierUserPreferencesStatus;\n defaultStatus: CourierUserPreferencesStatus;\n hasCustomRouting: boolean;\n customRouting: CourierUserPreferencesChannel[];\n}\n\nexport interface CourierUserPreferences {\n items: CourierUserPreferencesTopic[];\n paging: CourierUserPreferencesPaging;\n}\n\nexport interface CourierUserPreferencesTopicResponse {\n topic: CourierUserPreferencesTopic;\n}\n\nexport class PreferenceTransformer {\n /**\n * Transforms a single API response item to the CourierUserPreferencesTopic type\n * @param item - The API response item\n * @returns A CourierUserPreferencesTopic object\n */\n transformItem(item: any): CourierUserPreferencesTopic {\n return {\n topicId: item.topic_id,\n topicName: item.topic_name,\n sectionId: item.section_id,\n sectionName: item.section_name,\n status: item.status,\n defaultStatus: item.default_status,\n hasCustomRouting: item.has_custom_routing,\n customRouting: item.custom_routing || []\n };\n }\n\n /**\n * Transforms an array of API response items to CourierUserPreferencesTopic objects\n * @param items - The API response items\n * @returns A generator of CourierUserPreferencesTopic objects\n */\n *transform(items: any[]): Generator<CourierUserPreferencesTopic> {\n for (const item of items) {\n yield this.transformItem(item);\n }\n }\n}","export function decode(clientKey: string): string {\n const binaryString = atob(clientKey);\n const bytes = new Uint8Array(binaryString.length);\n\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n\n return String.fromCharCode(...bytes);\n}\n\nexport function encode(key: string): string {\n const bytes = new Uint8Array(key.length);\n\n for (let i = 0; i < key.length; i++) {\n bytes[i] = key.charCodeAt(i);\n }\n\n return btoa(String.fromCharCode(...bytes));\n}","import { CourierUserPreferences, CourierUserPreferencesChannel, CourierUserPreferencesStatus, CourierUserPreferencesTopic, CourierUserPreferencesTopicResponse, PreferenceTransformer } from '../types/preference';\nimport { decode, encode } from '../utils/coding';\nimport { http } from '../utils/request';\nimport { Client } from './client';\n\nexport class PreferenceClient extends Client {\n private transformer = new PreferenceTransformer();\n\n /**\n * Get all preferences for a user\n * @param paginationCursor - Optional cursor for pagination\n * @returns Promise resolving to user preferences\n * @see https://www.courier.com/docs/reference/user-preferences/list-all-user-preferences\n */\n public async getUserPreferences(props?: { paginationCursor?: string; }): Promise<CourierUserPreferences> {\n let url = `${this.options.apiUrls.courier.rest}/users/${this.options.userId}/preferences`;\n\n if (props?.paginationCursor) {\n url += `?cursor=${props.paginationCursor}`;\n }\n\n const json = await http({\n options: this.options,\n url,\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n });\n\n const data = json as CourierUserPreferences;\n\n return {\n items: [...this.transformer.transform(data.items)],\n paging: data.paging\n };\n }\n\n /**\n * Get preferences for a specific topic\n * @param topicId - The ID of the topic to get preferences for\n * @returns Promise resolving to topic preferences\n * @see https://www.courier.com/docs/reference/user-preferences/get-subscription-topic-preferences\n */\n public async getUserPreferenceTopic(props: { topicId: string; }): Promise<CourierUserPreferencesTopic> {\n\n const json = await http({\n options: this.options,\n url: `${this.options.apiUrls.courier.rest}/users/${this.options.userId}/preferences/${props.topicId}`,\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n });\n\n const res = json as CourierUserPreferencesTopicResponse;\n return this.transformer.transformItem(res.topic);\n }\n\n /**\n * Update preferences for a specific topic\n * @param topicId - The ID of the topic to update preferences for\n * @param status - The new status for the topic\n * @param hasCustomRouting - Whether the topic has custom routing\n * @param customRouting - The custom routing channels for the topic\n * @returns Promise resolving when update is complete\n * @see https://www.courier.com/docs/reference/user-preferences/update-subscription-topic-preferences\n */\n public async putUserPreferenceTopic(props: { topicId: string; status: CourierUserPreferencesStatus; hasCustomRouting: boolean; customRouting: CourierUserPreferencesChannel[]; }): Promise<void> {\n\n const payload = {\n topic: {\n status: props.status,\n has_custom_routing: props.hasCustomRouting,\n custom_routing: props.customRouting,\n },\n };\n\n await http({\n options: this.options,\n url: `${this.options.apiUrls.courier.rest}/users/${this.options.userId}/preferences/${props.topicId}`,\n method: 'PUT',\n headers: {\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n body: payload,\n });\n }\n\n /**\n * Get the notification center URL\n * @param clientKey - The client key to use for the URL\n * @returns The notification center URL\n */\n public getNotificationCenterUrl(props: {\n clientKey: string;\n }): string {\n const rootTenantId = decode(props.clientKey);\n const url = encode(`${rootTenantId}#${this.options.userId}${this.options.tenantId ? `#${this.options.tenantId}` : \"\"}#${false}`);\n return `https://view.notificationcenter.app/p/${url}`;\n }\n\n}\n","import { CourierDevice } from '../types/token';\nimport { http } from '../utils/request';\nimport { Client } from './client';\n\nexport class TokenClient extends Client {\n\n /**\n * Store a push notification token for a user\n * @param token - The push notification token\n * @param provider - The provider of the token\n * @param device - The device information\n * @see https://www.courier.com/docs/reference/token-management/put-token\n */\n public async putUserToken(props: {\n token: string;\n provider: string;\n device?: CourierDevice;\n }): Promise<void> {\n const payload = {\n provider_key: props.provider,\n ...(props.device && {\n device: {\n app_id: props.device.appId,\n ad_id: props.device.adId,\n device_id: props.device.deviceId,\n platform: props.device.platform,\n manufacturer: props.device.manufacturer,\n model: props.device.model\n }\n })\n };\n\n await http({\n options: this.options,\n url: `${this.options.apiUrls.courier.rest}/users/${this.options.userId}/tokens/${props.token}`,\n method: 'PUT',\n headers: {\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n body: payload,\n validCodes: [200, 204]\n });\n }\n\n /**\n * Delete a push notification token for a user\n * @param token - The push notification token\n * @returns Promise resolving when token is deleted\n */\n public async deleteUserToken(props: {\n token: string;\n }): Promise<void> {\n await http({\n options: this.options,\n url: `${this.options.apiUrls.courier.rest}/users/${this.options.userId}/tokens/${props.token}`,\n method: 'DELETE',\n headers: {\n 'Authorization': `Bearer ${this.options.accessToken}`\n },\n validCodes: [200, 204]\n });\n }\n}\n","import { http } from '../utils/request';\nimport { Client } from './client';\n\nexport class ListClient extends Client {\n\n /**\n * Subscribe a user to a list\n * @param listId - The ID of the list to subscribe to\n * @returns Promise resolving when subscription is complete\n * @see https://www.courier.com/docs/reference/lists/recipient-subscribe\n */\n public async putSubscription(props: { listId: string; }): Promise<void> {\n return await http({\n url: `${this.options.apiUrls.courier.rest}/lists/${props.listId}/subscriptions/${this.options.userId}`,\n options: this.options,\n method: 'PUT',\n headers: {\n Authorization: `Bearer ${this.options.accessToken}`,\n },\n });\n }\n\n /**\n * Unsubscribe a user from a list\n * @param listId - The ID of the list to unsubscribe from\n * @returns Promise resolving when unsubscription is complete\n * @see https://www.courier.com/docs/reference/lists/delete-subscription\n */\n public async deleteSubscription(props: { listId: string; }): Promise<void> {\n return await http({\n url: `${this.options.apiUrls.courier.rest}/lists/${props.listId}/subscriptions/${this.options.userId}`,\n options: this.options,\n method: 'DELETE',\n headers: {\n Authorization: `Bearer ${this.options.accessToken}`,\n },\n });\n }\n\n}\n","import { CourierTrackingEvent } from '../types/tracking-event';\nimport { http } from '../utils/request';\nimport { Client } from './client';\n\nexport class TrackingClient extends Client {\n\n /**\n * Post an inbound courier event\n * @param event - The event type: Example: \"New Order Placed\"\n * @param messageId - The message ID\n * @param type - The type of event: Available options: \"track\"\n * @param properties - The properties of the event\n * @returns Promise resolving to the message ID\n * @see https://www.courier.com/docs/reference/inbound/courier-track-event\n */\n public async postInboundCourier(props: {\n event: string;\n messageId: string;\n type: 'track';\n properties?: Record<string, any>;\n }): Promise<{ messageId: string }> {\n return await http({\n url: `${this.options.apiUrls.courier.rest}/inbound/courier`,\n options: this.options,\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.options.accessToken}`,\n },\n body: {\n ...props,\n userId: this.options.userId\n },\n validCodes: [200, 202]\n });\n }\n\n /**\n * Post a tracking URL event\n * These urls are found in messages sent from Courier\n * @param url - The URL to post the event to\n * @param event - The event type: Available options: \"click\", \"open\", \"unsubscribe\"\n * @returns Promise resolving when the event is posted\n */\n public async postTrackingUrl(props: {\n url: string;\n event: CourierTrackingEvent;\n }): Promise<void> {\n return await http({\n url: props.url,\n options: this.options,\n method: 'POST',\n body: {\n event: props.event\n }\n });\n }\n\n}\n","\nimport { CourierApiUrls, getCourierApiUrls } from '../types/courier-api-urls';\nimport { Logger } from '../utils/logger';\nimport { BrandClient } from './brand-client';\nimport { InboxClient } from './inbox-client';\nimport { PreferenceClient } from './preference-client';\nimport { TokenClient } from './token-client';\nimport { Client } from './client';\nimport { ListClient } from './list-client';\nimport { TrackingClient } from './tracking-client';\n\nexport interface CourierProps {\n /** User ID for the client. Normally matches the UID in your system */\n userId: string;\n\n /** JWT token for authentication: More info at https://www.courier.com/docs/reference/auth/issue-token */\n jwt?: string;\n\n /** Public API key for testing (use JWTs in prod) */\n publicApiKey?: string;\n\n /** Inbox Websocket connection ID */\n connectionId?: string;\n\n /** Tenant ID. Used for multi-tenant apps */\n tenantId?: string;\n\n /** Flag to control logging. Logs are prefixed with [COURIER]. */\n showLogs?: boolean;\n\n /** Custom API URLs */\n apiUrls?: CourierApiUrls;\n}\n\nexport interface CourierClientOptions {\n /** JWT token for authentication: More info at https://www.courier.com/docs/reference/auth/issue-token */\n readonly jwt?: string;\n\n /** Public API key for testing (use JWTs in prod) */\n readonly publicApiKey?: string;\n\n /** User ID for the client. Normally matches the UID in your system */\n readonly userId: string;\n\n /** Inbox Websocket connection ID */\n readonly connectionId?: string;\n\n /** Tenant ID. Used for multi-tenant apps */\n readonly tenantId?: string;\n\n /** Flag to control logging. Logs are prefixed with [COURIER]. */\n readonly showLogs?: boolean;\n\n /** Combined authentication token (jwt or publicApiKey) */\n readonly accessToken?: string;\n\n /** Logger instance */\n readonly logger: Logger;\n\n /** Final API URLs configuration */\n readonly apiUrls: CourierApiUrls;\n}\n\nexport class CourierClient extends Client {\n public readonly tokens: TokenClient;\n public readonly brands: BrandClient;\n public readonly preferences: PreferenceClient;\n public readonly inbox: InboxClient;\n public readonly lists: ListClient;\n public readonly tracking: TrackingClient;\n\n constructor(props: CourierProps) {\n // Determine if we should show logs (default to false)\n const showLogs = props.showLogs !== undefined ? props.showLogs : false;\n\n // Setup base options with default values\n const baseOptions = {\n ...props,\n showLogs,\n apiUrls: props.apiUrls || getCourierApiUrls(),\n accessToken: props.jwt ?? props.publicApiKey\n };\n\n // Initialize base client with logger and URLs\n super({\n ...baseOptions,\n logger: new Logger(baseOptions.showLogs),\n apiUrls: getCourierApiUrls(baseOptions.apiUrls)\n });\n\n // Initialize all subclients with the configured options\n this.tokens = new TokenClient(this.options);\n this.brands = new BrandClient(this.options);\n this.preferences = new PreferenceClient(this.options);\n this.inbox = new InboxClient(this.options);\n this.lists = new ListClient(this.options);\n this.tracking = new TrackingClient(this.options);\n\n // Warn if no authentication method is provided\n if (!this.options.jwt && !this.options.publicApiKey) {\n this.options.logger.warn('Courier Client initialized with no authentication method. Please provide a JWT or public API key.');\n }\n\n // Warn about using public API key in production\n if (this.options.publicApiKey) {\n this.options.logger?.warn(\n 'Courier Warning: Public API Keys are for testing only. Please use JWTs for production.\\n' +\n 'You can generate a JWT with this endpoint: https://www.courier.com/docs/reference/auth/issue-token\\n' +\n 'This endpoint should be called from your backend server, not the SDK.'\n );\n }\n\n // Warn if both authentication methods are provided\n if (this.options.jwt && this.options.publicApiKey) {\n this.options.logger?.warn(\n 'Courier Warning: Both a JWT and a Public API Key were provided. The Public API Key will be ignored.'\n );\n }\n }\n}\n","import { Courier } from \"./courier\";\n\nexport class AuthenticationListener {\n readonly callback: (props: { userId?: string }) => void;\n\n constructor(callback: (props: { userId?: string }) => void) {\n this.callback = callback;\n }\n\n public remove(): void {\n Courier.shared.removeAuthenticationListener(this);\n }\n\n}","import { CourierClient, CourierProps } from \"../client/courier-client\";\nimport { AuthenticationListener } from '../shared/authentication-listener';\nimport { UUID } from \"../utils/uuid\";\n\n/**\n * Courier is a singleton class that manages a shared Courier client instance and other resources.\n * UI components will automatically syncronize with this instance.\n * If you only need to call the Courier api, you should consider using the CourierClient directly.\n */\nexport class Courier {\n\n /**\n * The unique identifier for the Courier instance\n */\n public readonly id = UUID.nanoid();\n\n /**\n * The shared Courier instance\n */\n private static instance: Courier;\n\n /**\n * The Courier client instance\n */\n private instanceClient?: CourierClient;\n\n /**\n * The pagination limit (min: 1, max: 100)\n */\n private _paginationLimit = 24;\n\n public get paginationLimit(): number {\n return this._paginationLimit;\n }\n\n public set paginationLimit(value: number) {\n this._paginationLimit = Math.min(Math.max(value, 1), 100);\n }\n\n /**\n * Get the Courier client instance\n * @returns The Courier client instance or undefined if not signed in\n */\n public get client(): CourierClient | undefined {\n return this.instanceClient;\n }\n\n /**\n * The authentication listeners\n */\n private authenticationListeners: AuthenticationListener[] = [];\n\n /**\n * Get the shared Courier instance\n * @returns The shared Courier instance\n */\n public static get shared(): Courier {\n if (!Courier.instance) {\n Courier.instance = new Courier();\n }\n return Courier.instance;\n }\n\n /**\n * Sign in to Courier\n * @param options - The options for the Courier client\n */\n public signIn(props: CourierProps) {\n // Sign out any existing user.\n if (this.instanceClient) {\n this.instanceClient.options.logger.warn('Sign in called but there is already a user signed in. Signing out the current user.');\n this.signOut();\n }\n\n // Create a new client.\n const connectionId = props.connectionId ?? UUID.nanoid();\n this.instanceClient = new CourierClient({ ...props, connectionId });\n this.notifyAuthenticationListeners({ userId: props.userId });\n }\n\n /**\n * Sign out of Courier\n */\n public signOut() {\n // Close the socket client.\n this.instanceClient?.inbox.socket?.close();\n\n // Clear the client.\n this.instanceClient = undefined;\n this.notifyAuthenticationListeners({ userId: undefined });\n }\n\n /**\n * Register a callback to be notified of authentication state changes\n * @param callback - Function to be called when authentication state changes\n * @returns AuthenticationListener instance that can be used to remove the listener\n */\n public addAuthenticationListener(callback: (props: { userId?: string }) => void): AuthenticationListener {\n this.instanceClient?.options.logger.info('Adding authentication listener');\n const listener = new AuthenticationListener(callback);\n this.authenticationListeners.push(listener);\n return listener;\n }\n\n /**\n * Unregister an authentication state change listener\n * @param listener - The AuthenticationListener instance to remove\n */\n public removeAuthenticationListener(listener: AuthenticationListener) {\n this.instanceClient?.options.logger.info('Removing authentication listener');\n this.authenticationListeners = this.authenticationListeners.filter(l => l !== listener);\n }\n\n /**\n * Notify all authentication listeners\n * @param props - The props to notify the listeners with\n */\n private notifyAuthenticationListeners(props: { userId?: string }) {\n this.authenticationListeners.forEach(listener => listener.callback(props));\n }\n\n}\n"],"names":["ClientAction","ServerAction","InboxMessageEvent","_a"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trycourier/courier-js",
3
- "version": "2.0.10",
3
+ "version": "2.0.12",
4
4
  "description": "A browser-safe API wrapper",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",