@usesophi/sophi-web-sdk 0.1.0-beta.3 → 0.1.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,3 +1,3 @@
1
- 'use strict';var g={ADD_TO_CART:"add_to_cart",READY:"ready",ERROR:"error",SEND_TO_CHECKOUT:"send_to_checkout"},a={USER_DATA:"user_data",CONFIG:"config",UPDATE_USER_CART:"update_user_cart",DESTROY:"destroy",SESSION_DATA:"session_data",CLIENT_ID_UPDATED:"client_id_updated"};var o=class{constructor(e,t,r){this.baseUrl=e,this.apiKey=t,this.clientId=r;}async createSession(e){let t=`${this.baseUrl}/api/v1/auth/client/session`;try{let r=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json","x-api-key":this.apiKey,"x-sophi-client-id":this.clientId},body:JSON.stringify(e)});if(!r.ok){let s=await r.text();throw new Error(`Failed to create session: ${r.status} ${r.statusText}. ${s}`)}let i=await r.json();if(!i.success)throw new Error(`Session creation failed: ${i.message||"Unknown error"}`);if(!i.data)throw new Error("Session data is missing from response");return i.data}catch(r){throw r instanceof Error?r:new Error(`Unexpected error during session creation: ${String(r)}`)}}validateSessionData(e){return !!(e.accessToken&&e.sessionId&&e.clientId&&e.companyCode&&e.expiresAt)}async mergeUser(e){let t=`${this.baseUrl}/api/v1/auth/client/info`;try{let r=await fetch(t,{method:"PATCH",headers:{"Content-Type":"application/json",Accept:"application/json","x-api-key":this.apiKey,"x-sophi-client-id":e.guestId},body:JSON.stringify({externalUserId:e.userId})});if(!r.ok){let s=await r.text();throw new Error(`Failed to merge user: ${r.status} ${r.statusText}. ${s}`)}let i=await r.json();if(!i.success)throw new Error(`User merge failed: ${i.message||"Unknown error"}`);if(!i.data)throw new Error("Merge data is missing from response");return i.data}catch(r){throw r instanceof Error?r:new Error(`Unexpected error during user merge: ${String(r)}`)}}};var h={WIDTH:"100%",HEIGHT:"600px"},d={test:"http://10.0.2.127:80",production:"https://api.usesophi.ai"},u={CREATE_SESSION:"/api/v1/auth/client/session"},n={MISSING_API_KEY:"API key is required and must be a string",MISSING_CLIENT_ID:"Client ID is required and must be a string",MISSING_USER_ID:"User ID is required and must be a string",MISSING_CONTAINER:"Container is required",MISSING_IFRAME_URL:"Iframe URL is required and must be a string",INVALID_IFRAME_URL:"Invalid iframe URL",INVALID_ENVIRONMENT:"Invalid environment. Must be 'dev', 'test', 'staging', or 'production'",CONTAINER_NOT_FOUND:"Container element not found",NOT_INITIALIZED:"Widget not initialized. Call init() first.",ALREADY_INITIALIZED:"Sophi Widget is already initialized. Call destroy() first to reinitialize.",SESSION_CREATION_FAILED:"Failed to create authentication session",INVALID_SESSION_DATA:"Invalid session data received from API",YOU_NEED_TO_INIT_THE_WIDGET_FIRST:"You need to initialize the widget first. Call init() before calling this method."};var c=class{constructor(){this.config=null;this.iframe=null;this.container=null;this.eventListeners=new Map;this.messageHandler=null;this.isInitialized=false;this.iframeOrigin=null;this.sessionData=null;this.apiService=null;this.apiBaseUrl=null;}async init(e){if(this.isInitialized){console.warn(n.ALREADY_INITIALIZED);return}try{this.validateConfig(e),this.config=e,this.apiBaseUrl=d[e.environment||"production"],this.apiService=new o(this.apiBaseUrl,e.apiKey,e.clientId),await this.createAuthSession(),this.isInitialized=!0;try{let t=new URL(this.getIframeUrl());this.iframeOrigin=t.origin;}catch{let r=new Error(`${n.INVALID_IFRAME_URL}: ${this.getIframeUrl()}`);throw this.emitError(r),r}if(this.container=this.resolveContainer(e.container),!this.container){let t=new Error(n.CONTAINER_NOT_FOUND);throw this.emitError(t),t}this.createIframe(),this.setupMessageListener(),this.iframe?.addEventListener("load",()=>{console.log("Iframe loaded, sending session data..."),setTimeout(()=>{this.sendSessionData();},100);}),e.onReady&&e.onReady();}catch(t){this.isInitialized=false,this.config=null,this.apiService=null,this.sessionData=null;let r=t instanceof Error?t:new Error(String(t));throw this.emitError(r),r}}sendUserData(e){if(!this.isInitialized||!this.iframe){console.warn("Widget not initialized. Call init() first.");return}let t={type:a.USER_DATA,data:e};this.postMessage(t);}updateUserCart(e){if(!this.isInitialized||!this.iframe){console.warn("Widget not initialized. Call init() first.");return}let t={type:a.UPDATE_USER_CART,data:e};this.postMessage(t);}async mergeUser(e,t,r){try{let i;if(!this.apiBaseUrl)throw new Error(n.YOU_NEED_TO_INIT_THE_WIDGET_FIRST);if(this.apiService)i=this.apiService;else {if(!r)throw new Error("API key is required when widget is not initialized");i=new o(this.apiBaseUrl,r,e);}let s=await i.mergeUser({guestId:e,userId:t});if(this.isInitialized&&this.iframe){let p={type:a.CLIENT_ID_UPDATED,data:{clientId:s.clientId,externalUserId:s.externalUserId}};this.postMessage(p);}return s}catch(i){let s=i instanceof Error?i:new Error(String(i));throw this.isInitialized&&this.emitError(s),s}}static async mergeUser(e,t,r,{environment:i="test"}={}){let s=d[i];return await new o(s,r,e).mergeUser({guestId:e,userId:t})}onDestroy(){if(!this.isInitialized||!this.iframe){console.warn("Widget not initialized. Call init() first.");return}let e={type:a.DESTROY};this.postMessage(e);}on(e,t){this.eventListeners.has(e)||this.eventListeners.set(e,new Set),this.eventListeners.get(e).add(t);}off(e,t){let r=this.eventListeners.get(e);r&&r.delete(t);}show(){this.iframe&&(this.iframe.style.display="block");}hide(){this.iframe&&(this.iframe.style.display="none");}async destroy(){this.onDestroy(),await new Promise(e=>setTimeout(e,300)),this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.iframe&&this.iframe.parentNode&&(this.iframe.parentNode.removeChild(this.iframe),this.iframe=null),this.eventListeners.clear(),this.config=null,this.container=null,this.isInitialized=false,this.iframeOrigin=null,this.sessionData=null,this.apiService=null;}isReady(){return this.isInitialized&&this.iframe!==null}validateConfig(e){if(!e.apiKey||typeof e.apiKey!="string")throw new Error(n.MISSING_API_KEY);if(!e.clientId||typeof e.clientId!="string")throw new Error(n.MISSING_CLIENT_ID);if(!e.clientId||typeof e.clientId!="string")throw new Error(n.MISSING_USER_ID);if(!e.container)throw new Error(n.MISSING_CONTAINER)}async createAuthSession(){if(!this.config||!this.apiService)throw new Error(n.NOT_INITIALIZED);try{if(this.sessionData=await this.apiService.createSession({externalUserId:this.config.clientId,metadata:this.config.metadata}),!this.apiService.validateSessionData(this.sessionData))throw new Error(n.INVALID_SESSION_DATA)}catch(e){throw e instanceof Error?e:new Error(n.SESSION_CREATION_FAILED)}}sendSessionData(){if(!this.sessionData){console.warn("Session data not available");return}let e={type:a.SESSION_DATA,data:this.sessionData};this.postMessage(e);}resolveContainer(e){return typeof e=="string"?document.querySelector(e):e}createIframe(){if(!this.container||!this.config)return;let e=document.createElement("iframe");e.src=this.buildIframeUrl(),e.style.width=this.config.width||"100%",e.style.height=this.config.height||"600px",e.style.border="none",e.title="Sophi AI Widget",e.allow="microphone; camera",this.container.appendChild(e),this.iframe=e;}buildIframeUrl(){return this.config?new URL(this.getIframeUrl()).toString():""}getIframeUrl(){return this.config?this.config.environment==="production"?"http://app.usesophi.com":"http://10.0.2.127:80":""}setupMessageListener(){this.messageHandler=e=>{if(this.iframeOrigin&&e.origin!==this.iframeOrigin){console.warn(`Received message from untrusted origin: ${e.origin}`);return}try{let t=typeof e.data=="string"?JSON.parse(e.data):e.data;this.handleIncomingMessage(t);}catch(t){console.error("Error processing message from iframe:",t);}},window.addEventListener("message",this.messageHandler);}handleIncomingMessage(e){switch(e.type){case "add_to_cart":this.isValidAddToCartMessage(e.data)?this.emit("add_to_cart",e.data):(console.error("Invalid add_to_cart message structure:",e.data),this.emitError(new Error("Invalid add_to_cart message format")));break;case "send_to_checkout":this.emit("send_to_checkout",void 0);break;case "ready":this.emit("ready",void 0);break;case "error":let t=e.data instanceof Error?e.data:new Error(String(e.data));this.emitError(t);break;default:console.warn("Unknown message type:",e);}}isValidAddToCartMessage(e){if(!e||typeof e!="object")return false;let t=e;return Array.isArray(t.products)?t.products.every(r=>{if(!r||typeof r!="object")return false;let i=r;return !(typeof i.productId!="string"||i.variantId!==void 0&&typeof i.variantId!="string")}):false}postMessage(e){if(!this.iframe||!this.iframe.contentWindow||!this.iframeOrigin){console.warn("Cannot send message: iframe not ready");return}try{console.log("Posting message to iframe:",e.type,"to origin:",this.iframeOrigin),this.iframe.contentWindow.postMessage(e,this.iframeOrigin),console.log("Message posted successfully");}catch(t){console.error("Error sending message to iframe:",t),this.emitError(t instanceof Error?t:new Error(String(t)));}}emit(e,t){let r=this.eventListeners.get(e);r&&r.forEach(i=>{try{i(t);}catch(s){console.error(`Error in ${e} event handler:`,s);}});}emitError(e){this.emit("error",e),this.config?.onError&&this.config.onError(e);}};
1
+ 'use strict';var g={ADD_TO_CART:"add_to_cart",READY:"ready",ERROR:"error",SEND_TO_CHECKOUT:"send_to_checkout"},a={USER_DATA:"user_data",CONFIG:"config",UPDATE_USER_CART:"update_user_cart",DESTROY:"destroy",SESSION_DATA:"session_data",CLIENT_ID_UPDATED:"client_id_updated"};var o=class{constructor(e,t,r){this.baseUrl=e,this.apiKey=t,this.clientId=r;}async createSession(e){let t=`${this.baseUrl}/api/v1/auth/client/session`;try{let r=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json","x-api-key":this.apiKey,"x-sophi-client-id":this.clientId},body:JSON.stringify(e)});if(!r.ok){let s=await r.text();throw new Error(`Failed to create session: ${r.status} ${r.statusText}. ${s}`)}let i=await r.json();if(!i.success)throw new Error(`Session creation failed: ${i.message||"Unknown error"}`);if(!i.data)throw new Error("Session data is missing from response");return i.data}catch(r){throw r instanceof Error?r:new Error(`Unexpected error during session creation: ${String(r)}`)}}validateSessionData(e){return !!(e.accessToken&&e.sessionId&&e.clientId&&e.companyCode&&e.expiresAt)}async mergeUser(e){let t=`${this.baseUrl}/api/v1/auth/client/info`;try{let r=await fetch(t,{method:"PATCH",headers:{"Content-Type":"application/json",Accept:"application/json","x-api-key":this.apiKey,"x-sophi-client-id":e.guestId},body:JSON.stringify({externalUserId:e.userId})});if(!r.ok){let s=await r.text();throw new Error(`Failed to merge user: ${r.status} ${r.statusText}. ${s}`)}let i=await r.json();if(!i.success)throw new Error(`User merge failed: ${i.message||"Unknown error"}`);if(!i.data)throw new Error("Merge data is missing from response");return i.data}catch(r){throw r instanceof Error?r:new Error(`Unexpected error during user merge: ${String(r)}`)}}};var h={WIDTH:"100%",HEIGHT:"600px"},d={test:"http://10.0.2.127:80",production:"https://api.usesophi.com"},u={CREATE_SESSION:"/api/v1/auth/client/session"},n={MISSING_API_KEY:"API key is required and must be a string",MISSING_CLIENT_ID:"Client ID is required and must be a string",MISSING_USER_ID:"User ID is required and must be a string",MISSING_CONTAINER:"Container is required",MISSING_IFRAME_URL:"Iframe URL is required and must be a string",INVALID_IFRAME_URL:"Invalid iframe URL",INVALID_ENVIRONMENT:"Invalid environment. Must be 'dev', 'test', 'staging', or 'production'",CONTAINER_NOT_FOUND:"Container element not found",NOT_INITIALIZED:"Widget not initialized. Call init() first.",ALREADY_INITIALIZED:"Sophi Widget is already initialized. Call destroy() first to reinitialize.",SESSION_CREATION_FAILED:"Failed to create authentication session",INVALID_SESSION_DATA:"Invalid session data received from API",YOU_NEED_TO_INIT_THE_WIDGET_FIRST:"You need to initialize the widget first. Call init() before calling this method."};var c=class{constructor(){this.config=null;this.iframe=null;this.container=null;this.eventListeners=new Map;this.messageHandler=null;this.isInitialized=false;this.iframeOrigin=null;this.sessionData=null;this.apiService=null;this.apiBaseUrl=null;}async init(e){if(this.isInitialized){console.warn(n.ALREADY_INITIALIZED);return}try{this.validateConfig(e),this.config=e,this.apiBaseUrl=d[e.environment||"production"],this.apiService=new o(this.apiBaseUrl,e.apiKey,e.clientId),await this.createAuthSession(),this.isInitialized=!0;try{let t=new URL(this.getIframeUrl());this.iframeOrigin=t.origin;}catch{let r=new Error(`${n.INVALID_IFRAME_URL}: ${this.getIframeUrl()}`);throw this.emitError(r),r}if(this.container=this.resolveContainer(e.container),!this.container){let t=new Error(n.CONTAINER_NOT_FOUND);throw this.emitError(t),t}this.createIframe(),this.setupMessageListener(),this.iframe?.addEventListener("load",()=>{console.log("Iframe loaded, sending session data..."),setTimeout(()=>{this.sendSessionData();},100);}),e.onReady&&e.onReady();}catch(t){this.isInitialized=false,this.config=null,this.apiService=null,this.sessionData=null;let r=t instanceof Error?t:new Error(String(t));throw this.emitError(r),r}}sendUserData(e){if(!this.isInitialized||!this.iframe){console.warn("Widget not initialized. Call init() first.");return}let t={type:a.USER_DATA,data:e};this.postMessage(t);}updateUserCart(e){if(!this.isInitialized||!this.iframe){console.warn("Widget not initialized. Call init() first.");return}let t={type:a.UPDATE_USER_CART,data:e};this.postMessage(t);}async mergeUser(e,t,r){try{let i;if(!this.apiBaseUrl)throw new Error(n.YOU_NEED_TO_INIT_THE_WIDGET_FIRST);if(this.apiService)i=this.apiService;else {if(!r)throw new Error("API key is required when widget is not initialized");i=new o(this.apiBaseUrl,r,e);}let s=await i.mergeUser({guestId:e,userId:t});if(this.isInitialized&&this.iframe){let p={type:a.CLIENT_ID_UPDATED,data:{clientId:s.clientId,externalUserId:s.externalUserId}};this.postMessage(p);}return s}catch(i){let s=i instanceof Error?i:new Error(String(i));throw this.isInitialized&&this.emitError(s),s}}static async mergeUser(e,t,r,{environment:i="test"}={}){let s=d[i];return await new o(s,r,e).mergeUser({guestId:e,userId:t})}onDestroy(){if(!this.isInitialized||!this.iframe){console.warn("Widget not initialized. Call init() first.");return}let e={type:a.DESTROY};this.postMessage(e);}on(e,t){this.eventListeners.has(e)||this.eventListeners.set(e,new Set),this.eventListeners.get(e).add(t);}off(e,t){let r=this.eventListeners.get(e);r&&r.delete(t);}show(){this.iframe&&(this.iframe.style.display="block");}hide(){this.iframe&&(this.iframe.style.display="none");}async destroy(){this.onDestroy(),await new Promise(e=>setTimeout(e,300)),this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.iframe&&this.iframe.parentNode&&(this.iframe.parentNode.removeChild(this.iframe),this.iframe=null),this.eventListeners.clear(),this.config=null,this.container=null,this.isInitialized=false,this.iframeOrigin=null,this.sessionData=null,this.apiService=null;}isReady(){return this.isInitialized&&this.iframe!==null}validateConfig(e){if(!e.apiKey||typeof e.apiKey!="string")throw new Error(n.MISSING_API_KEY);if(!e.clientId||typeof e.clientId!="string")throw new Error(n.MISSING_CLIENT_ID);if(!e.clientId||typeof e.clientId!="string")throw new Error(n.MISSING_USER_ID);if(!e.container)throw new Error(n.MISSING_CONTAINER)}async createAuthSession(){if(!this.config||!this.apiService)throw new Error(n.NOT_INITIALIZED);try{if(this.sessionData=await this.apiService.createSession({externalUserId:this.config.clientId,metadata:this.config.metadata}),!this.apiService.validateSessionData(this.sessionData))throw new Error(n.INVALID_SESSION_DATA)}catch(e){throw e instanceof Error?e:new Error(n.SESSION_CREATION_FAILED)}}sendSessionData(){if(!this.sessionData){console.warn("Session data not available");return}let e={type:a.SESSION_DATA,data:this.sessionData};this.postMessage(e);}resolveContainer(e){return typeof e=="string"?document.querySelector(e):e}createIframe(){if(!this.container||!this.config)return;let e=document.createElement("iframe");e.src=this.buildIframeUrl(),e.style.width=this.config.width||"100%",e.style.height=this.config.height||"600px",e.style.border="none",e.title="Sophi AI Widget",e.allow="microphone; camera",this.container.appendChild(e),this.iframe=e;}buildIframeUrl(){return this.config?new URL(this.getIframeUrl()).toString():""}getIframeUrl(){return this.config?this.config.environment==="production"?"http://app.usesophi.com":"http://10.0.2.127:80":""}setupMessageListener(){this.messageHandler=e=>{if(this.iframeOrigin&&e.origin!==this.iframeOrigin){console.warn(`Received message from untrusted origin: ${e.origin}`);return}try{let t=typeof e.data=="string"?JSON.parse(e.data):e.data;this.handleIncomingMessage(t);}catch(t){console.error("Error processing message from iframe:",t);}},window.addEventListener("message",this.messageHandler);}handleIncomingMessage(e){switch(e.type){case "add_to_cart":this.isValidAddToCartMessage(e.data)?this.emit("add_to_cart",e.data):(console.error("Invalid add_to_cart message structure:",e.data),this.emitError(new Error("Invalid add_to_cart message format")));break;case "send_to_checkout":this.emit("send_to_checkout",void 0);break;case "ready":this.emit("ready",void 0);break;case "error":let t=e.data instanceof Error?e.data:new Error(String(e.data));this.emitError(t);break;default:console.warn("Unknown message type:",e);}}isValidAddToCartMessage(e){if(!e||typeof e!="object")return false;let t=e;return Array.isArray(t.products)?t.products.every(r=>{if(!r||typeof r!="object")return false;let i=r;return !(typeof i.productId!="string"||i.variantId!==void 0&&typeof i.variantId!="string")}):false}postMessage(e){if(!this.iframe||!this.iframe.contentWindow||!this.iframeOrigin){console.warn("Cannot send message: iframe not ready");return}try{console.log("Posting message to iframe:",e.type,"to origin:",this.iframeOrigin),this.iframe.contentWindow.postMessage(e,this.iframeOrigin),console.log("Message posted successfully");}catch(t){console.error("Error sending message to iframe:",t),this.emitError(t instanceof Error?t:new Error(String(t)));}}emit(e,t){let r=this.eventListeners.get(e);r&&r.forEach(i=>{try{i(t);}catch(s){console.error(`Error in ${e} event handler:`,s);}});}emitError(e){this.emit("error",e),this.config?.onError&&this.config.onError(e);}};
2
2
  exports.API_BASE_URLS=d;exports.API_ENDPOINTS=u;exports.ApiService=o;exports.DEFAULT_CONFIG=h;exports.ERROR_MESSAGES=n;exports.IncomingMessageTypes=g;exports.OutgoingMessageTypes=a;exports.SophiWidget=c;//# sourceMappingURL=index.cjs.map
3
3
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts","../src/services/api.service.ts","../src/constants/config.ts","../src/SophiWidget.ts"],"names":["IncomingMessageTypes","OutgoingMessageTypes","ApiService","baseUrl","apiKey","clientId","request","url","response","errorText","data","error","sessionData","DEFAULT_CONFIG","API_BASE_URLS","API_ENDPOINTS","ERROR_MESSAGES","SophiWidget","config","err","message","cart","guestId","userId","apiService","mergeData","environment","apiBaseUrl","event","handler","handlers","resolve","container","iframe","product","prod"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Configuration for initializing the Sophi Widget\n */\nexport interface SophiConfig {\n /**\n * API key for authentication with Sophi services (x-api-key header)\n * This comes from the parent application\n */\n apiKey: string;\n\n /**\n * Client ID for authentication (x-sophi-client-id header)\n * This comes from the parent application\n */\n clientId: string;\n\n /**\n * Container element selector (e.g., '#widget') or HTMLElement where the widget will be mounted\n */\n container: string | HTMLElement;\n\n /**\n * Optional metadata to send with the session request\n */\n metadata?: Record<string, unknown>;\n\n /**\n * Width of the widget iframe\n * @default '100%'\n */\n width?: string;\n\n /**\n * Height of the widget iframe\n * @default '600px'\n */\n height?: string;\n\n /**\n * Callback fired when the widget is ready\n */\n onReady?: () => void;\n\n /**\n * Callback fired when an error occurs\n */\n onError?: (error: Error) => void;\n\n /**\n * Environment to use for the API\n */\n environment?: \"test\" | \"production\";\n}\n\n/**\n * Product information for add to cart events\n */\nexport interface Product {\n /**\n * Unique product identifier\n */\n productId: string;\n\n /**\n * Optional variant identifier\n */\n variantId?: string;\n}\n\n/**\n * Add to cart event data\n */\nexport interface AddToCartEvent {\n /**\n * List of products to add to cart\n */\n products: Product[];\n}\n\n/**\n * User data that can be sent to the widget\n */\nexport interface UserData {\n /**\n * User ID\n */\n userId?: string;\n\n /**\n * User email\n */\n email?: string;\n\n /**\n * User name\n */\n name?: string;\n\n /**\n * Additional custom data\n */\n [key: string]: unknown;\n}\n\nexport interface IUserCartItem {\n productId: string;\n variantId: string;\n quantity?: number;\n}\n\nexport interface IUserCart {\n cardId: string;\n items: IUserCartItem[];\n}\n\n/**\n * Message types that can be received from the iframe\n */\nexport type IncomingMessageType = \"add_to_cart\" | \"send_to_checkout\" | \"ready\" | \"error\";\n\n/**\n * Incoming message type constants for easy access\n * @example\n * widget.on(IncomingMessageTypes.ADD_TO_CART, (data) => {...})\n */\nexport const IncomingMessageTypes = {\n ADD_TO_CART: \"add_to_cart\",\n READY: \"ready\",\n ERROR: \"error\",\n SEND_TO_CHECKOUT: \"send_to_checkout\",\n} as const;\n\n/**\n * Message types that can be sent to the iframe\n */\nexport type OutgoingMessageType = \"user_data\" | \"config\" | \"update_user_cart\" | \"destroy\" | \"session_data\" | \"client_id_updated\";\n\n/**\n * Outgoing message type constants for easy access\n * @example\n * widget.sendMessage({ type: OutgoingMessageTypes.USER_DATA, data: {...} })\n */\nexport const OutgoingMessageTypes = {\n USER_DATA: \"user_data\",\n CONFIG: \"config\",\n UPDATE_USER_CART: \"update_user_cart\",\n DESTROY: \"destroy\",\n SESSION_DATA: \"session_data\",\n CLIENT_ID_UPDATED: \"client_id_updated\",\n} as const;\n\n/**\n * Incoming message structure from iframe\n */\nexport interface IncomingMessage {\n type: IncomingMessageType;\n data?: unknown;\n}\n\n/**\n * Outgoing message structure to iframe\n */\nexport interface OutgoingMessage {\n type: OutgoingMessageType;\n data?: unknown;\n}\n\n/**\n * Event map for type-safe event handling\n */\nexport interface SophiEventMap {\n /**\n * Fired when products should be added to cart\n */\n add_to_cart: AddToCartEvent;\n\n /**\n * Fired when the widget is ready\n */\n ready: void;\n\n /**\n * Fired when an error occurs\n */\n error: Error;\n\n /**\n * Fired when the user should be sent to checkout\n */\n send_to_checkout: void;\n}\n\n/**\n * Event handler type\n */\nexport type EventHandler<T> = (data: T) => void;\n\n/**\n * Event name type\n */\nexport type EventName = keyof SophiEventMap;\n\n/**\n * Authentication session request payload\n */\nexport interface AuthSessionRequest {\n /**\n * External user ID from the parent application\n */\n externalUserId: string;\n\n /**\n * Optional metadata to include with the session\n */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Session data returned from the authentication API\n */\nexport interface SessionData {\n /**\n * JWT access token for authenticating requests\n */\n accessToken: string;\n\n /**\n * Unique session identifier\n */\n sessionId: string;\n\n /**\n * Client ID\n */\n clientId: string;\n\n /**\n * Company code\n */\n companyCode: string;\n\n /**\n * Session expiration timestamp\n */\n expiresAt: string;\n}\n\n/**\n * Authentication session API response\n */\nexport interface AuthSessionResponse {\n /**\n * Whether the request was successful\n */\n success: boolean;\n\n /**\n * Response message\n */\n message: string;\n\n /**\n * Session data\n */\n data?: SessionData;\n}\n\n/**\n * Merge user request payload\n */\nexport interface MergeUserRequest {\n /**\n * The guest client ID to merge from\n */\n guestId: string;\n\n /**\n * The logged-in user ID to merge to\n */\n userId: string;\n}\n\n/**\n * Merge user data returned from the API\n */\nexport interface MergeUserData {\n /**\n * The resulting client ID after merge\n */\n clientId: string;\n\n /**\n * The external user ID\n */\n externalUserId: string;\n}\n\n/**\n * Merge user API response\n */\nexport interface MergeUserResponse {\n /**\n * Whether the request was successful\n */\n success: boolean;\n\n /**\n * Response message\n */\n message: string;\n\n /**\n * Merge result data\n */\n data?: MergeUserData;\n}\n","import type { AuthSessionRequest, AuthSessionResponse, SessionData, MergeUserRequest, MergeUserResponse, MergeUserData } from \"../types\";\n\n/**\n * API Service for Sophi authentication and session management\n */\nexport class ApiService {\n private baseUrl: string;\n private apiKey: string;\n private clientId: string;\n\n constructor(baseUrl: string, apiKey: string, clientId: string) {\n this.baseUrl = baseUrl;\n this.apiKey = apiKey;\n this.clientId = clientId;\n }\n\n /**\n * Create a client session by calling the authentication endpoint\n * @param request - Session request with externalUserId and optional metadata\n * @returns Session data including accessToken, sessionId, etc.\n */\n async createSession(request: AuthSessionRequest): Promise<SessionData> {\n const url = `${this.baseUrl}/api/v1/auth/client/session`;\n\n try {\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\",\n \"x-api-key\": this.apiKey,\n \"x-sophi-client-id\": this.clientId,\n },\n body: JSON.stringify(request),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Failed to create session: ${response.status} ${response.statusText}. ${errorText}`\n );\n }\n\n const data: AuthSessionResponse = await response.json();\n\n if (!data.success) {\n throw new Error(`Session creation failed: ${data.message || \"Unknown error\"}`);\n }\n\n if (!data.data) {\n throw new Error(\"Session data is missing from response\");\n }\n\n return data.data;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(`Unexpected error during session creation: ${String(error)}`);\n }\n }\n\n /**\n * Validate session data\n */\n validateSessionData(sessionData: SessionData): boolean {\n return !!(\n sessionData.accessToken &&\n sessionData.sessionId &&\n sessionData.clientId &&\n sessionData.companyCode &&\n sessionData.expiresAt\n );\n }\n\n /**\n * Merge guest user with logged-in user\n * @param request - Merge request with guestId and userId\n * @returns Merge result data including the resulting clientId\n */\n async mergeUser(request: MergeUserRequest): Promise<MergeUserData> {\n const url = `${this.baseUrl}/api/v1/auth/client/info`;\n\n try {\n const response = await fetch(url, {\n method: \"PATCH\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\",\n \"x-api-key\": this.apiKey,\n \"x-sophi-client-id\": request.guestId,\n },\n body: JSON.stringify({\n externalUserId: request.userId,\n }),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Failed to merge user: ${response.status} ${response.statusText}. ${errorText}`\n );\n }\n\n const data: MergeUserResponse = await response.json();\n\n if (!data.success) {\n throw new Error(`User merge failed: ${data.message || \"Unknown error\"}`);\n }\n\n if (!data.data) {\n throw new Error(\"Merge data is missing from response\");\n }\n\n return data.data;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(`Unexpected error during user merge: ${String(error)}`);\n }\n }\n}\n","/**\n * Default configuration constants for Sophi Widget\n */\nexport const DEFAULT_CONFIG = {\n /**\n * Default width of the widget iframe\n */\n WIDTH: \"100%\",\n\n /**\n * Default height of the widget iframe\n */\n HEIGHT: \"600px\",\n} as const;\n\n/**\n * API base URLs for different environments\n *\n * Note: These URLs will be visible in the client-side bundle.\n * This is normal and safe - the real security is in your API key validation.\n *\n * To use different URLs, modify these values before building the SDK.\n *\n * Production API URL - update before building for production\n */\nexport const API_BASE_URLS = {\n test: \"http://10.0.2.127:80\",\n production: \"https://api.usesophi.ai\",\n} as const;\n\n/**\n * API endpoints configuration\n */\nexport const API_ENDPOINTS = {\n /**\n * Session creation endpoint path\n */\n CREATE_SESSION: \"/api/v1/auth/client/session\",\n} as const;\n\n/**\n * Error messages\n */\nexport const ERROR_MESSAGES = {\n MISSING_API_KEY: \"API key is required and must be a string\",\n MISSING_CLIENT_ID: \"Client ID is required and must be a string\",\n MISSING_USER_ID: \"User ID is required and must be a string\",\n MISSING_CONTAINER: \"Container is required\",\n MISSING_IFRAME_URL: \"Iframe URL is required and must be a string\",\n INVALID_IFRAME_URL: \"Invalid iframe URL\",\n INVALID_ENVIRONMENT: \"Invalid environment. Must be 'dev', 'test', 'staging', or 'production'\",\n CONTAINER_NOT_FOUND: \"Container element not found\",\n NOT_INITIALIZED: \"Widget not initialized. Call init() first.\",\n ALREADY_INITIALIZED: \"Sophi Widget is already initialized. Call destroy() first to reinitialize.\",\n SESSION_CREATION_FAILED: \"Failed to create authentication session\",\n INVALID_SESSION_DATA: \"Invalid session data received from API\",\n YOU_NEED_TO_INIT_THE_WIDGET_FIRST: \"You need to initialize the widget first. Call init() before calling this method.\",\n} as const;\n","import { type SophiConfig, type UserData, type EventHandler, type EventName, type SophiEventMap, type IncomingMessage, type OutgoingMessage, type AddToCartEvent, type IUserCart, type SessionData, type MergeUserRequest, type MergeUserData, OutgoingMessageTypes } from \"./types\";\nimport { ApiService } from \"./services/api.service\";\nimport { DEFAULT_CONFIG, ERROR_MESSAGES, API_BASE_URLS } from \"./constants/config\";\n\n/**\n * Main Sophi Widget SDK class\n * Provides a framework-agnostic way to embed and interact with the Sophi AI widget\n */\nexport class SophiWidget {\n private config: SophiConfig | null = null;\n private iframe: HTMLIFrameElement | null = null;\n private container: HTMLElement | null = null;\n private eventListeners: Map<EventName, Set<EventHandler<any>>> = new Map();\n private messageHandler: ((event: MessageEvent) => void) | null = null;\n private isInitialized = false;\n private iframeOrigin: string | null = null;\n private sessionData: SessionData | null = null;\n private apiService: ApiService | null = null;\n private apiBaseUrl: string | null = null;\n\n /**\n * Initialize the widget with configuration\n * @param config - Widget configuration\n */\n public async init(config: SophiConfig): Promise<void> {\n if (this.isInitialized) {\n console.warn(ERROR_MESSAGES.ALREADY_INITIALIZED);\n return;\n }\n\n try {\n // Validate configuration\n this.validateConfig(config);\n\n this.config = config;\n\n // Determine API URL based on environment\n this.apiBaseUrl = API_BASE_URLS[config.environment || \"production\"];\n\n // Initialize API service\n this.apiService = new ApiService(this.apiBaseUrl, config.apiKey, config.clientId);\n\n // Create authentication session\n await this.createAuthSession();\n\n this.isInitialized = true;\n\n // Extract origin from iframe URL for security\n try {\n const url = new URL(this.getIframeUrl());\n this.iframeOrigin = url.origin;\n } catch (error) {\n const err = new Error(`${ERROR_MESSAGES.INVALID_IFRAME_URL}: ${this.getIframeUrl()}`);\n this.emitError(err);\n throw err;\n }\n\n // Resolve container element\n this.container = this.resolveContainer(config.container);\n\n if (!this.container) {\n const err = new Error(ERROR_MESSAGES.CONTAINER_NOT_FOUND);\n this.emitError(err);\n throw err;\n }\n\n // Create and mount iframe\n this.createIframe();\n\n // Setup postMessage listener\n this.setupMessageListener();\n\n // Send session data to iframe after it's loaded\n this.iframe?.addEventListener(\"load\", () => {\n console.log(\"Iframe loaded, sending session data...\");\n // Small delay to ensure iframe is fully ready\n setTimeout(() => {\n this.sendSessionData();\n }, 100);\n });\n\n // Call onReady callback if provided\n if (config.onReady) {\n config.onReady();\n }\n } catch (error) {\n // Reset state on error\n this.isInitialized = false;\n this.config = null;\n this.apiService = null;\n this.sessionData = null;\n\n const err = error instanceof Error ? error : new Error(String(error));\n this.emitError(err);\n throw err;\n }\n }\n\n /**\n * Send user data to the widget\n * @param data - User data object\n */\n public sendUserData(data: UserData): void {\n if (!this.isInitialized || !this.iframe) {\n console.warn(\"Widget not initialized. Call init() first.\");\n return;\n }\n\n const message: OutgoingMessage = {\n type: OutgoingMessageTypes.USER_DATA,\n data,\n };\n\n this.postMessage(message);\n }\n\n public updateUserCart(cart: IUserCart): void {\n if (!this.isInitialized || !this.iframe) {\n console.warn(\"Widget not initialized. Call init() first.\");\n return;\n }\n\n const message: OutgoingMessage = {\n type: OutgoingMessageTypes.UPDATE_USER_CART,\n data: cart,\n };\n\n this.postMessage(message);\n }\n\n /**\n * Merge guest user with logged-in user\n * This can be called anytime, even before widget initialization\n * @param guestId - The guest client ID (previous clientId)\n * @param userId - The logged-in user ID (new externalUserId)\n * @param apiKey - API key for authentication (required if widget not initialized)\n * @returns Promise with merge result data\n */\n public async mergeUser(guestId: string, userId: string, apiKey?: string): Promise<MergeUserData> {\n try {\n let apiService: ApiService;\n if (!this.apiBaseUrl) {\n throw new Error(ERROR_MESSAGES.YOU_NEED_TO_INIT_THE_WIDGET_FIRST);\n }\n // Use existing API service if widget is initialized, otherwise create a temporary one\n if (this.apiService) {\n apiService = this.apiService;\n } else {\n // Widget not initialized - create temporary API service\n if (!apiKey) {\n throw new Error(\"API key is required when widget is not initialized\");\n }\n apiService = new ApiService(this.apiBaseUrl, apiKey, guestId);\n }\n\n const mergeData = await apiService.mergeUser({ guestId, userId });\n\n // If iframe exists, send the updated clientId to it\n // This allows the iframe to update its own stores automatically\n if (this.isInitialized && this.iframe) {\n const message: OutgoingMessage = {\n type: OutgoingMessageTypes.CLIENT_ID_UPDATED,\n data: {\n clientId: mergeData.clientId,\n externalUserId: mergeData.externalUserId,\n },\n };\n this.postMessage(message);\n }\n\n return mergeData;\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n // Only emit error if widget is initialized\n if (this.isInitialized) {\n this.emitError(err);\n }\n throw err;\n }\n }\n\n /**\n * Static method to merge guest user with logged-in user\n * Use this when you don't have a widget instance or want to merge before initialization\n * @param guestId - The guest client ID (previous clientId)\n * @param userId - The logged-in user ID (new externalUserId)\n * @param apiKey - API key for authentication\n * @returns Promise with merge result data\n */\n public static async mergeUser(\n guestId: string,\n userId: string,\n apiKey: string,\n {\n environment = \"test\",\n }: {\n environment?: SophiConfig[\"environment\"];\n } = {}\n ): Promise<MergeUserData> {\n const apiBaseUrl = API_BASE_URLS[environment];\n const apiService = new ApiService(apiBaseUrl, apiKey, guestId);\n return await apiService.mergeUser({ guestId, userId });\n }\n\n private onDestroy(): void {\n if (!this.isInitialized || !this.iframe) {\n console.warn(\"Widget not initialized. Call init() first.\");\n return;\n }\n\n const message: OutgoingMessage = {\n type: OutgoingMessageTypes.DESTROY,\n };\n\n this.postMessage(message);\n }\n\n /**\n * Register an event listener\n * @param event - Event name\n * @param handler - Event handler function\n */\n public on<K extends EventName>(event: K, handler: EventHandler<SophiEventMap[K]>): void {\n if (!this.eventListeners.has(event)) {\n this.eventListeners.set(event, new Set());\n }\n this.eventListeners.get(event)!.add(handler);\n }\n\n /**\n * Unregister an event listener\n * @param event - Event name\n * @param handler - Event handler function to remove\n */\n public off<K extends EventName>(event: K, handler: EventHandler<SophiEventMap[K]>): void {\n const handlers = this.eventListeners.get(event);\n if (handlers) {\n handlers.delete(handler);\n }\n }\n\n /**\n * Show the widget\n */\n public show(): void {\n if (this.iframe) {\n this.iframe.style.display = \"block\";\n }\n }\n\n /**\n * Hide the widget\n */\n public hide(): void {\n if (this.iframe) {\n this.iframe.style.display = \"none\";\n }\n }\n\n /**\n * Destroy the widget and cleanup resources\n * Waits 300ms after sending destroy message to give iframe time to cleanup\n */\n public async destroy(): Promise<void> {\n this.onDestroy();\n\n // Wait 300ms to give iframe time to process the destroy event and cleanup\n await new Promise((resolve) => setTimeout(resolve, 300));\n\n // Remove message listener\n if (this.messageHandler) {\n window.removeEventListener(\"message\", this.messageHandler);\n this.messageHandler = null;\n }\n\n // Remove iframe\n if (this.iframe && this.iframe.parentNode) {\n this.iframe.parentNode.removeChild(this.iframe);\n this.iframe = null;\n }\n\n // Clear event listeners\n this.eventListeners.clear();\n\n // Reset state\n this.config = null;\n this.container = null;\n this.isInitialized = false;\n this.iframeOrigin = null;\n this.sessionData = null;\n this.apiService = null;\n }\n\n /**\n * Check if widget is initialized\n */\n public isReady(): boolean {\n return this.isInitialized && this.iframe !== null;\n }\n\n /**\n * Validate configuration\n */\n private validateConfig(config: SophiConfig): void {\n if (!config.apiKey || typeof config.apiKey !== \"string\") {\n throw new Error(ERROR_MESSAGES.MISSING_API_KEY);\n }\n\n if (!config.clientId || typeof config.clientId !== \"string\") {\n throw new Error(ERROR_MESSAGES.MISSING_CLIENT_ID);\n }\n\n if (!config.clientId || typeof config.clientId !== \"string\") {\n throw new Error(ERROR_MESSAGES.MISSING_USER_ID);\n }\n\n if (!config.container) {\n throw new Error(ERROR_MESSAGES.MISSING_CONTAINER);\n }\n }\n\n /**\n * Create authentication session with the API\n */\n private async createAuthSession(): Promise<void> {\n if (!this.config || !this.apiService) {\n throw new Error(ERROR_MESSAGES.NOT_INITIALIZED);\n }\n\n try {\n this.sessionData = await this.apiService.createSession({\n externalUserId: this.config.clientId,\n metadata: this.config.metadata,\n });\n\n // Validate session data\n if (!this.apiService.validateSessionData(this.sessionData)) {\n throw new Error(ERROR_MESSAGES.INVALID_SESSION_DATA);\n }\n } catch (error) {\n const err = error instanceof Error ? error : new Error(ERROR_MESSAGES.SESSION_CREATION_FAILED);\n throw err;\n }\n }\n\n /**\n * Send session data to iframe\n */\n private sendSessionData(): void {\n if (!this.sessionData) {\n console.warn(\"Session data not available\");\n return;\n }\n\n const message: OutgoingMessage = {\n type: OutgoingMessageTypes.SESSION_DATA,\n data: this.sessionData,\n };\n\n this.postMessage(message);\n }\n\n /**\n * Resolve container element from selector or element\n */\n private resolveContainer(container: string | HTMLElement): HTMLElement | null {\n if (typeof container === \"string\") {\n return document.querySelector(container);\n }\n return container;\n }\n\n /**\n * Create and mount the iframe\n */\n private createIframe(): void {\n if (!this.container || !this.config) {\n return;\n }\n\n const iframe = document.createElement(\"iframe\");\n\n // Set iframe attributes\n iframe.src = this.buildIframeUrl();\n iframe.style.width = this.config.width || \"100%\";\n iframe.style.height = this.config.height || \"600px\";\n iframe.style.border = \"none\";\n iframe.title = \"Sophi AI Widget\";\n iframe.allow = \"microphone; camera\";\n\n // Append to container\n this.container.appendChild(iframe);\n this.iframe = iframe;\n }\n\n /**\n * Build iframe URL with query parameters\n */\n private buildIframeUrl(): string {\n if (!this.config) {\n return \"\";\n }\n const url = new URL(this.getIframeUrl());\n\n return url.toString();\n }\n\n private getIframeUrl(): string {\n if (!this.config) {\n return \"\";\n }\n if (this.config.environment === \"production\") {\n return \"http://app.usesophi.com\";\n }\n return \"http://10.0.2.127:80\";\n }\n\n /**\n * Setup postMessage listener for iframe communication\n */\n private setupMessageListener(): void {\n this.messageHandler = (event: MessageEvent) => {\n // Validate origin for security\n if (this.iframeOrigin && event.origin !== this.iframeOrigin) {\n console.warn(`Received message from untrusted origin: ${event.origin}`);\n return;\n }\n\n try {\n // Parse message\n const message: IncomingMessage = typeof event.data === \"string\" ? JSON.parse(event.data) : event.data;\n\n // Handle different message types\n this.handleIncomingMessage(message);\n } catch (error) {\n console.error(\"Error processing message from iframe:\", error);\n }\n };\n\n window.addEventListener(\"message\", this.messageHandler);\n }\n\n /**\n * Handle incoming messages from iframe\n */\n private handleIncomingMessage(message: IncomingMessage): void {\n switch (message.type) {\n case \"add_to_cart\":\n if (this.isValidAddToCartMessage(message.data)) {\n this.emit(\"add_to_cart\", message.data);\n } else {\n console.error(\"Invalid add_to_cart message structure:\", message.data);\n this.emitError(new Error(\"Invalid add_to_cart message format\"));\n }\n break;\n\n case \"send_to_checkout\":\n this.emit(\"send_to_checkout\", undefined);\n break;\n case \"ready\":\n this.emit(\"ready\", undefined);\n break;\n\n case \"error\":\n const error = message.data instanceof Error ? message.data : new Error(String(message.data));\n this.emitError(error);\n break;\n\n default:\n console.warn(\"Unknown message type:\", message);\n }\n }\n\n /**\n * Validate add_to_cart message structure\n */\n private isValidAddToCartMessage(data: unknown): data is AddToCartEvent {\n if (!data || typeof data !== \"object\") {\n return false;\n }\n\n const event = data as Record<string, unknown>;\n\n // Check products array exists and is an array\n if (!Array.isArray(event.products)) {\n return false;\n }\n\n // Validate each product in the array\n return event.products.every((product) => {\n if (!product || typeof product !== \"object\") {\n return false;\n }\n const prod = product as Record<string, unknown>;\n\n // productId is required and must be a string\n if (typeof prod.productId !== \"string\") {\n return false;\n }\n\n // variantId is optional, but if present must be a string\n if (prod.variantId !== undefined && typeof prod.variantId !== \"string\") {\n return false;\n }\n\n return true;\n });\n }\n\n /**\n * Post message to iframe\n */\n private postMessage(message: OutgoingMessage): void {\n if (!this.iframe || !this.iframe.contentWindow || !this.iframeOrigin) {\n console.warn(\"Cannot send message: iframe not ready\");\n return;\n }\n\n try {\n console.log(\"Posting message to iframe:\", message.type, \"to origin:\", this.iframeOrigin);\n this.iframe.contentWindow.postMessage(message, this.iframeOrigin);\n console.log(\"Message posted successfully\");\n } catch (error) {\n console.error(\"Error sending message to iframe:\", error);\n this.emitError(error instanceof Error ? error : new Error(String(error)));\n }\n }\n\n /**\n * Emit event to registered listeners\n */\n private emit<K extends EventName>(event: K, data: SophiEventMap[K]): void {\n const handlers = this.eventListeners.get(event);\n if (handlers) {\n handlers.forEach((handler) => {\n try {\n handler(data);\n } catch (error) {\n console.error(`Error in ${event} event handler:`, error);\n }\n });\n }\n }\n\n /**\n * Emit error event\n */\n private emitError(error: Error): void {\n this.emit(\"error\", error);\n\n // Also call onError callback if provided\n if (this.config?.onError) {\n this.config.onError(error);\n }\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/types.ts","../src/services/api.service.ts","../src/constants/config.ts","../src/SophiWidget.ts"],"names":["IncomingMessageTypes","OutgoingMessageTypes","ApiService","baseUrl","apiKey","clientId","request","url","response","errorText","data","error","sessionData","DEFAULT_CONFIG","API_BASE_URLS","API_ENDPOINTS","ERROR_MESSAGES","SophiWidget","config","err","message","cart","guestId","userId","apiService","mergeData","environment","apiBaseUrl","event","handler","handlers","resolve","container","iframe","product","prod"],"mappings":"aA6HO,IAAMA,EAAuB,CAClC,WAAA,CAAa,cACb,KAAA,CAAO,OAAA,CACP,MAAO,OAAA,CACP,gBAAA,CAAkB,kBACpB,CAAA,CAYaC,CAAAA,CAAuB,CAClC,SAAA,CAAW,WAAA,CACX,OAAQ,QAAA,CACR,gBAAA,CAAkB,mBAClB,OAAA,CAAS,SAAA,CACT,YAAA,CAAc,cAAA,CACd,kBAAmB,mBACrB,MChJaC,CAAAA,CAAN,KAAiB,CAKtB,WAAA,CAAYC,CAAAA,CAAiBC,EAAgBC,CAAAA,CAAkB,CAC7D,KAAK,OAAA,CAAUF,CAAAA,CACf,KAAK,MAAA,CAASC,CAAAA,CACd,KAAK,QAAA,CAAWC,EAClB,CAOA,MAAM,cAAcC,CAAAA,CAAmD,CACrE,IAAMC,CAAAA,CAAM,CAAA,EAAG,KAAK,OAAO,CAAA,2BAAA,CAAA,CAE3B,GAAI,CACF,IAAMC,EAAW,MAAM,KAAA,CAAMD,EAAK,CAChC,MAAA,CAAQ,OACR,OAAA,CAAS,CACP,cAAA,CAAgB,kBAAA,CAChB,OAAU,kBAAA,CACV,WAAA,CAAa,KAAK,MAAA,CAClB,mBAAA,CAAqB,KAAK,QAC5B,CAAA,CACA,KAAM,IAAA,CAAK,SAAA,CAAUD,CAAO,CAC9B,CAAC,EAED,GAAI,CAACE,EAAS,EAAA,CAAI,CAChB,IAAMC,CAAAA,CAAY,MAAMD,CAAAA,CAAS,IAAA,GACjC,MAAM,IAAI,MACR,CAAA,0BAAA,EAA6BA,CAAAA,CAAS,MAAM,CAAA,CAAA,EAAIA,CAAAA,CAAS,UAAU,CAAA,EAAA,EAAKC,CAAS,EACnF,CACF,CAEA,IAAMC,CAAAA,CAA4B,MAAMF,CAAAA,CAAS,IAAA,GAEjD,GAAI,CAACE,EAAK,OAAA,CACR,MAAM,IAAI,KAAA,CAAM,CAAA,yBAAA,EAA4BA,EAAK,OAAA,EAAW,eAAe,EAAE,CAAA,CAG/E,GAAI,CAACA,CAAAA,CAAK,IAAA,CACR,MAAM,IAAI,KAAA,CAAM,uCAAuC,CAAA,CAGzD,OAAOA,CAAAA,CAAK,IACd,OAASC,CAAAA,CAAO,CACd,MAAIA,CAAAA,YAAiB,KAAA,CACbA,EAEF,IAAI,KAAA,CAAM,6CAA6C,MAAA,CAAOA,CAAK,CAAC,CAAA,CAAE,CAC9E,CACF,CAKA,mBAAA,CAAoBC,CAAAA,CAAmC,CACrD,OAAO,CAAC,EACNA,EAAY,WAAA,EACZA,CAAAA,CAAY,WACZA,CAAAA,CAAY,QAAA,EACZA,EAAY,WAAA,EACZA,CAAAA,CAAY,UAEhB,CAOA,MAAM,UAAUN,CAAAA,CAAmD,CACjE,IAAMC,CAAAA,CAAM,CAAA,EAAG,IAAA,CAAK,OAAO,2BAE3B,GAAI,CACF,IAAMC,CAAAA,CAAW,MAAM,MAAMD,CAAAA,CAAK,CAChC,OAAQ,OAAA,CACR,OAAA,CAAS,CACP,cAAA,CAAgB,kBAAA,CAChB,OAAU,kBAAA,CACV,WAAA,CAAa,KAAK,MAAA,CAClB,mBAAA,CAAqBD,CAAAA,CAAQ,OAC/B,EACA,IAAA,CAAM,IAAA,CAAK,UAAU,CACnB,cAAA,CAAgBA,EAAQ,MAC1B,CAAC,CACH,CAAC,CAAA,CAED,GAAI,CAACE,CAAAA,CAAS,GAAI,CAChB,IAAMC,EAAY,MAAMD,CAAAA,CAAS,IAAA,EAAK,CACtC,MAAM,IAAI,KAAA,CACR,yBAAyBA,CAAAA,CAAS,MAAM,IAAIA,CAAAA,CAAS,UAAU,KAAKC,CAAS,CAAA,CAC/E,CACF,CAEA,IAAMC,EAA0B,MAAMF,CAAAA,CAAS,MAAK,CAEpD,GAAI,CAACE,CAAAA,CAAK,QACR,MAAM,IAAI,MAAM,CAAA,mBAAA,EAAsBA,CAAAA,CAAK,SAAW,eAAe,CAAA,CAAE,EAGzE,GAAI,CAACA,EAAK,IAAA,CACR,MAAM,IAAI,KAAA,CAAM,qCAAqC,EAGvD,OAAOA,CAAAA,CAAK,IACd,CAAA,MAASC,EAAO,CACd,MAAIA,aAAiB,KAAA,CACbA,CAAAA,CAEF,IAAI,KAAA,CAAM,CAAA,oCAAA,EAAuC,OAAOA,CAAK,CAAC,EAAE,CACxE,CACF,CACF,ECvHO,IAAME,EAAiB,CAI5B,KAAA,CAAO,MAAA,CAKP,MAAA,CAAQ,OACV,CAAA,CAYaC,CAAAA,CAAgB,CAC3B,IAAA,CAAM,sBAAA,CACN,WAAY,0BACd,CAAA,CAKaC,EAAgB,CAI3B,cAAA,CAAgB,6BAClB,CAAA,CAKaC,CAAAA,CAAiB,CAC5B,eAAA,CAAiB,0CAAA,CACjB,kBAAmB,4CAAA,CACnB,eAAA,CAAiB,0CAAA,CACjB,iBAAA,CAAmB,wBACnB,kBAAA,CAAoB,6CAAA,CACpB,mBAAoB,oBAAA,CACpB,mBAAA,CAAqB,yEACrB,mBAAA,CAAqB,6BAAA,CACrB,gBAAiB,4CAAA,CACjB,mBAAA,CAAqB,6EACrB,uBAAA,CAAyB,yCAAA,CACzB,qBAAsB,wCAAA,CACtB,iCAAA,CAAmC,kFACrC,ECjDO,IAAMC,CAAAA,CAAN,KAAkB,CAAlB,WAAA,EAAA,CACL,IAAA,CAAQ,OAA6B,IAAA,CACrC,IAAA,CAAQ,OAAmC,IAAA,CAC3C,IAAA,CAAQ,UAAgC,IAAA,CACxC,IAAA,CAAQ,eAAyD,IAAI,GAAA,CACrE,KAAQ,cAAA,CAAyD,IAAA,CACjE,KAAQ,aAAA,CAAgB,KAAA,CACxB,IAAA,CAAQ,YAAA,CAA8B,KACtC,IAAA,CAAQ,WAAA,CAAkC,KAC1C,IAAA,CAAQ,UAAA,CAAgC,KACxC,IAAA,CAAQ,UAAA,CAA4B,MAMpC,MAAa,IAAA,CAAKC,EAAoC,CACpD,GAAI,KAAK,aAAA,CAAe,CACtB,QAAQ,IAAA,CAAKF,CAAAA,CAAe,mBAAmB,CAAA,CAC/C,MACF,CAEA,GAAI,CAEF,IAAA,CAAK,cAAA,CAAeE,CAAM,CAAA,CAE1B,IAAA,CAAK,OAASA,CAAAA,CAGd,IAAA,CAAK,WAAaJ,CAAAA,CAAcI,CAAAA,CAAO,aAAe,YAAY,CAAA,CAGlE,KAAK,UAAA,CAAa,IAAIhB,CAAAA,CAAW,IAAA,CAAK,WAAYgB,CAAAA,CAAO,MAAA,CAAQA,EAAO,QAAQ,CAAA,CAGhF,MAAM,IAAA,CAAK,iBAAA,GAEX,IAAA,CAAK,aAAA,CAAgB,GAGrB,GAAI,CACF,IAAMX,CAAAA,CAAM,IAAI,IAAI,IAAA,CAAK,YAAA,EAAc,CAAA,CACvC,KAAK,YAAA,CAAeA,CAAAA,CAAI,OAC1B,CAAA,KAAgB,CACd,IAAMY,CAAAA,CAAM,IAAI,MAAM,CAAA,EAAGH,CAAAA,CAAe,kBAAkB,CAAA,EAAA,EAAK,IAAA,CAAK,cAAc,CAAA,CAAE,EACpF,MAAA,IAAA,CAAK,SAAA,CAAUG,CAAG,CAAA,CACZA,CACR,CAKA,GAFA,KAAK,SAAA,CAAY,IAAA,CAAK,iBAAiBD,CAAAA,CAAO,SAAS,EAEnD,CAAC,IAAA,CAAK,UAAW,CACnB,IAAMC,EAAM,IAAI,KAAA,CAAMH,EAAe,mBAAmB,CAAA,CACxD,MAAA,IAAA,CAAK,SAAA,CAAUG,CAAG,CAAA,CACZA,CACR,CAGA,IAAA,CAAK,YAAA,GAGL,IAAA,CAAK,oBAAA,GAGL,IAAA,CAAK,MAAA,EAAQ,iBAAiB,MAAA,CAAQ,IAAM,CAC1C,OAAA,CAAQ,GAAA,CAAI,wCAAwC,CAAA,CAEpD,UAAA,CAAW,IAAM,CACf,KAAK,eAAA,GACP,EAAG,GAAG,EACR,CAAC,CAAA,CAGGD,CAAAA,CAAO,SACTA,CAAAA,CAAO,OAAA,GAEX,CAAA,MAASP,CAAAA,CAAO,CAEd,IAAA,CAAK,aAAA,CAAgB,MACrB,IAAA,CAAK,MAAA,CAAS,IAAA,CACd,IAAA,CAAK,WAAa,IAAA,CAClB,IAAA,CAAK,YAAc,IAAA,CAEnB,IAAMQ,EAAMR,CAAAA,YAAiB,KAAA,CAAQA,EAAQ,IAAI,KAAA,CAAM,OAAOA,CAAK,CAAC,EACpE,MAAA,IAAA,CAAK,SAAA,CAAUQ,CAAG,CAAA,CACZA,CACR,CACF,CAMO,aAAaT,CAAAA,CAAsB,CACxC,GAAI,CAAC,IAAA,CAAK,eAAiB,CAAC,IAAA,CAAK,OAAQ,CACvC,OAAA,CAAQ,KAAK,4CAA4C,CAAA,CACzD,MACF,CAEA,IAAMU,EAA2B,CAC/B,IAAA,CAAMnB,CAAAA,CAAqB,SAAA,CAC3B,KAAAS,CACF,CAAA,CAEA,KAAK,WAAA,CAAYU,CAAO,EAC1B,CAEO,cAAA,CAAeC,EAAuB,CAC3C,GAAI,CAAC,IAAA,CAAK,aAAA,EAAiB,CAAC,IAAA,CAAK,MAAA,CAAQ,CACvC,OAAA,CAAQ,IAAA,CAAK,4CAA4C,CAAA,CACzD,MACF,CAEA,IAAMD,EAA2B,CAC/B,IAAA,CAAMnB,EAAqB,gBAAA,CAC3B,IAAA,CAAMoB,CACR,CAAA,CAEA,IAAA,CAAK,YAAYD,CAAO,EAC1B,CAUA,MAAa,SAAA,CAAUE,EAAiBC,CAAAA,CAAgBnB,CAAAA,CAAyC,CAC/F,GAAI,CACF,IAAIoB,CAAAA,CACJ,GAAI,CAAC,IAAA,CAAK,WACR,MAAM,IAAI,MAAMR,CAAAA,CAAe,iCAAiC,EAGlE,GAAI,IAAA,CAAK,WACPQ,CAAAA,CAAa,IAAA,CAAK,gBACb,CAEL,GAAI,CAACpB,CAAAA,CACH,MAAM,IAAI,KAAA,CAAM,oDAAoD,CAAA,CAEtEoB,CAAAA,CAAa,IAAItB,CAAAA,CAAW,IAAA,CAAK,WAAYE,CAAAA,CAAQkB,CAAO,EAC9D,CAEA,IAAMG,EAAY,MAAMD,CAAAA,CAAW,UAAU,CAAE,OAAA,CAAAF,CAAAA,CAAS,MAAA,CAAAC,CAAO,CAAC,CAAA,CAIhE,GAAI,IAAA,CAAK,aAAA,EAAiB,KAAK,MAAA,CAAQ,CACrC,IAAMH,CAAAA,CAA2B,CAC/B,KAAMnB,CAAAA,CAAqB,iBAAA,CAC3B,KAAM,CACJ,QAAA,CAAUwB,EAAU,QAAA,CACpB,cAAA,CAAgBA,CAAAA,CAAU,cAC5B,CACF,CAAA,CACA,IAAA,CAAK,YAAYL,CAAO,EAC1B,CAEA,OAAOK,CACT,OAASd,CAAAA,CAAO,CACd,IAAMQ,CAAAA,CAAMR,CAAAA,YAAiB,MAAQA,CAAAA,CAAQ,IAAI,MAAM,MAAA,CAAOA,CAAK,CAAC,CAAA,CAEpE,MAAI,KAAK,aAAA,EACP,IAAA,CAAK,UAAUQ,CAAG,CAAA,CAEdA,CACR,CACF,CAUA,aAAoB,SAAA,CAClBG,CAAAA,CACAC,EACAnB,CAAAA,CACA,CACE,YAAAsB,CAAAA,CAAc,MAChB,EAEI,EAAC,CACmB,CACxB,IAAMC,EAAab,CAAAA,CAAcY,CAAW,EAE5C,OAAO,MADY,IAAIxB,CAAAA,CAAWyB,CAAAA,CAAYvB,EAAQkB,CAAO,CAAA,CACrC,UAAU,CAAE,OAAA,CAAAA,EAAS,MAAA,CAAAC,CAAO,CAAC,CACvD,CAEQ,SAAA,EAAkB,CACxB,GAAI,CAAC,IAAA,CAAK,eAAiB,CAAC,IAAA,CAAK,OAAQ,CACvC,OAAA,CAAQ,KAAK,4CAA4C,CAAA,CACzD,MACF,CAEA,IAAMH,EAA2B,CAC/B,IAAA,CAAMnB,EAAqB,OAC7B,CAAA,CAEA,IAAA,CAAK,WAAA,CAAYmB,CAAO,EAC1B,CAOO,GAAwBQ,CAAAA,CAAUC,CAAAA,CAA+C,CACjF,IAAA,CAAK,cAAA,CAAe,IAAID,CAAK,CAAA,EAChC,KAAK,cAAA,CAAe,GAAA,CAAIA,EAAO,IAAI,GAAK,EAE1C,IAAA,CAAK,cAAA,CAAe,GAAA,CAAIA,CAAK,EAAG,GAAA,CAAIC,CAAO,EAC7C,CAOO,GAAA,CAAyBD,EAAUC,CAAAA,CAA+C,CACvF,IAAMC,CAAAA,CAAW,IAAA,CAAK,eAAe,GAAA,CAAIF,CAAK,EAC1CE,CAAAA,EACFA,CAAAA,CAAS,OAAOD,CAAO,EAE3B,CAKO,IAAA,EAAa,CACd,IAAA,CAAK,MAAA,GACP,KAAK,MAAA,CAAO,KAAA,CAAM,QAAU,OAAA,EAEhC,CAKO,MAAa,CACd,IAAA,CAAK,SACP,IAAA,CAAK,MAAA,CAAO,MAAM,OAAA,CAAU,MAAA,EAEhC,CAMA,MAAa,OAAA,EAAyB,CACpC,IAAA,CAAK,WAAU,CAGf,MAAM,IAAI,OAAA,CAASE,CAAAA,EAAY,WAAWA,CAAAA,CAAS,GAAG,CAAC,CAAA,CAGnD,IAAA,CAAK,iBACP,MAAA,CAAO,mBAAA,CAAoB,UAAW,IAAA,CAAK,cAAc,EACzD,IAAA,CAAK,cAAA,CAAiB,IAAA,CAAA,CAIpB,IAAA,CAAK,QAAU,IAAA,CAAK,MAAA,CAAO,aAC7B,IAAA,CAAK,MAAA,CAAO,WAAW,WAAA,CAAY,IAAA,CAAK,MAAM,CAAA,CAC9C,IAAA,CAAK,OAAS,IAAA,CAAA,CAIhB,IAAA,CAAK,eAAe,KAAA,EAAM,CAG1B,KAAK,MAAA,CAAS,IAAA,CACd,IAAA,CAAK,SAAA,CAAY,KACjB,IAAA,CAAK,aAAA,CAAgB,MACrB,IAAA,CAAK,YAAA,CAAe,KACpB,IAAA,CAAK,WAAA,CAAc,KACnB,IAAA,CAAK,UAAA,CAAa,KACpB,CAKO,OAAA,EAAmB,CACxB,OAAO,IAAA,CAAK,eAAiB,IAAA,CAAK,MAAA,GAAW,IAC/C,CAKQ,eAAeb,CAAAA,CAA2B,CAChD,GAAI,CAACA,CAAAA,CAAO,QAAU,OAAOA,CAAAA,CAAO,QAAW,QAAA,CAC7C,MAAM,IAAI,KAAA,CAAMF,CAAAA,CAAe,eAAe,CAAA,CAGhD,GAAI,CAACE,CAAAA,CAAO,QAAA,EAAY,OAAOA,CAAAA,CAAO,UAAa,QAAA,CACjD,MAAM,IAAI,KAAA,CAAMF,CAAAA,CAAe,iBAAiB,CAAA,CAGlD,GAAI,CAACE,CAAAA,CAAO,QAAA,EAAY,OAAOA,CAAAA,CAAO,QAAA,EAAa,SACjD,MAAM,IAAI,MAAMF,CAAAA,CAAe,eAAe,CAAA,CAGhD,GAAI,CAACE,CAAAA,CAAO,SAAA,CACV,MAAM,IAAI,KAAA,CAAMF,EAAe,iBAAiB,CAEpD,CAKA,MAAc,iBAAA,EAAmC,CAC/C,GAAI,CAAC,KAAK,MAAA,EAAU,CAAC,KAAK,UAAA,CACxB,MAAM,IAAI,KAAA,CAAMA,EAAe,eAAe,CAAA,CAGhD,GAAI,CAOF,GANA,KAAK,WAAA,CAAc,MAAM,KAAK,UAAA,CAAW,aAAA,CAAc,CACrD,cAAA,CAAgB,IAAA,CAAK,OAAO,QAAA,CAC5B,QAAA,CAAU,KAAK,MAAA,CAAO,QACxB,CAAC,CAAA,CAGG,CAAC,IAAA,CAAK,UAAA,CAAW,oBAAoB,IAAA,CAAK,WAAW,EACvD,MAAM,IAAI,MAAMA,CAAAA,CAAe,oBAAoB,CAEvD,CAAA,MAASL,CAAAA,CAAO,CAEd,MADYA,CAAAA,YAAiB,MAAQA,CAAAA,CAAQ,IAAI,KAAA,CAAMK,CAAAA,CAAe,uBAAuB,CAE/F,CACF,CAKQ,eAAA,EAAwB,CAC9B,GAAI,CAAC,IAAA,CAAK,YAAa,CACrB,OAAA,CAAQ,KAAK,4BAA4B,CAAA,CACzC,MACF,CAEA,IAAMI,EAA2B,CAC/B,IAAA,CAAMnB,CAAAA,CAAqB,YAAA,CAC3B,KAAM,IAAA,CAAK,WACb,EAEA,IAAA,CAAK,WAAA,CAAYmB,CAAO,EAC1B,CAKQ,iBAAiBY,CAAAA,CAAqD,CAC5E,OAAI,OAAOA,CAAAA,EAAc,SAChB,QAAA,CAAS,aAAA,CAAcA,CAAS,CAAA,CAElCA,CACT,CAKQ,YAAA,EAAqB,CAC3B,GAAI,CAAC,KAAK,SAAA,EAAa,CAAC,KAAK,MAAA,CAC3B,OAGF,IAAMC,CAAAA,CAAS,QAAA,CAAS,cAAc,QAAQ,CAAA,CAG9CA,EAAO,GAAA,CAAM,IAAA,CAAK,gBAAe,CACjCA,CAAAA,CAAO,KAAA,CAAM,KAAA,CAAQ,KAAK,MAAA,CAAO,KAAA,EAAS,OAC1CA,CAAAA,CAAO,KAAA,CAAM,OAAS,IAAA,CAAK,MAAA,CAAO,QAAU,OAAA,CAC5CA,CAAAA,CAAO,MAAM,MAAA,CAAS,MAAA,CACtBA,EAAO,KAAA,CAAQ,iBAAA,CACfA,EAAO,KAAA,CAAQ,oBAAA,CAGf,IAAA,CAAK,SAAA,CAAU,YAAYA,CAAM,CAAA,CACjC,KAAK,MAAA,CAASA,EAChB,CAKQ,cAAA,EAAyB,CAC/B,OAAK,IAAA,CAAK,MAAA,CAGE,IAAI,GAAA,CAAI,IAAA,CAAK,cAAc,CAAA,CAE5B,UAAS,CAJX,EAKX,CAEQ,YAAA,EAAuB,CAC7B,OAAK,IAAA,CAAK,OAGN,IAAA,CAAK,MAAA,CAAO,cAAgB,YAAA,CACvB,yBAAA,CAEF,uBALE,EAMX,CAKQ,sBAA6B,CACnC,IAAA,CAAK,eAAkBL,CAAAA,EAAwB,CAE7C,GAAI,IAAA,CAAK,YAAA,EAAgBA,CAAAA,CAAM,MAAA,GAAW,KAAK,YAAA,CAAc,CAC3D,QAAQ,IAAA,CAAK,CAAA,wCAAA,EAA2CA,EAAM,MAAM,CAAA,CAAE,EACtE,MACF,CAEA,GAAI,CAEF,IAAMR,EAA2B,OAAOQ,CAAAA,CAAM,MAAS,QAAA,CAAW,IAAA,CAAK,KAAA,CAAMA,CAAAA,CAAM,IAAI,CAAA,CAAIA,CAAAA,CAAM,KAGjG,IAAA,CAAK,qBAAA,CAAsBR,CAAO,EACpC,CAAA,MAAST,EAAO,CACd,OAAA,CAAQ,MAAM,uCAAA,CAAyCA,CAAK,EAC9D,CACF,CAAA,CAEA,OAAO,gBAAA,CAAiB,SAAA,CAAW,IAAA,CAAK,cAAc,EACxD,CAKQ,qBAAA,CAAsBS,EAAgC,CAC5D,OAAQA,EAAQ,IAAA,EACd,KAAK,aAAA,CACC,IAAA,CAAK,wBAAwBA,CAAAA,CAAQ,IAAI,EAC3C,IAAA,CAAK,IAAA,CAAK,cAAeA,CAAAA,CAAQ,IAAI,CAAA,EAErC,OAAA,CAAQ,MAAM,wCAAA,CAA0CA,CAAAA,CAAQ,IAAI,CAAA,CACpE,IAAA,CAAK,UAAU,IAAI,KAAA,CAAM,oCAAoC,CAAC,CAAA,CAAA,CAEhE,MAEF,KAAK,kBAAA,CACH,KAAK,IAAA,CAAK,kBAAA,CAAoB,MAAS,CAAA,CACvC,MACF,KAAK,OAAA,CACH,KAAK,IAAA,CAAK,OAAA,CAAS,MAAS,CAAA,CAC5B,MAEF,KAAK,OAAA,CACH,IAAMT,EAAQS,CAAAA,CAAQ,IAAA,YAAgB,MAAQA,CAAAA,CAAQ,IAAA,CAAO,IAAI,KAAA,CAAM,MAAA,CAAOA,EAAQ,IAAI,CAAC,CAAA,CAC3F,IAAA,CAAK,UAAUT,CAAK,CAAA,CACpB,MAEF,QACE,OAAA,CAAQ,KAAK,uBAAA,CAAyBS,CAAO,EACjD,CACF,CAKQ,wBAAwBV,CAAAA,CAAuC,CACrE,GAAI,CAACA,CAAAA,EAAQ,OAAOA,CAAAA,EAAS,QAAA,CAC3B,OAAO,MAAA,CAGT,IAAMkB,CAAAA,CAAQlB,CAAAA,CAGd,OAAK,KAAA,CAAM,OAAA,CAAQkB,EAAM,QAAQ,CAAA,CAK1BA,EAAM,QAAA,CAAS,KAAA,CAAOM,GAAY,CACvC,GAAI,CAACA,CAAAA,EAAW,OAAOA,GAAY,QAAA,CACjC,OAAO,MAAA,CAET,IAAMC,EAAOD,CAAAA,CAQb,OALI,SAAOC,CAAAA,CAAK,SAAA,EAAc,UAK1BA,CAAAA,CAAK,SAAA,GAAc,QAAa,OAAOA,CAAAA,CAAK,WAAc,QAAA,CAKhE,CAAC,EArBQ,KAsBX,CAKQ,YAAYf,CAAAA,CAAgC,CAClD,GAAI,CAAC,KAAK,MAAA,EAAU,CAAC,KAAK,MAAA,CAAO,aAAA,EAAiB,CAAC,IAAA,CAAK,YAAA,CAAc,CACpE,OAAA,CAAQ,IAAA,CAAK,uCAAuC,CAAA,CACpD,MACF,CAEA,GAAI,CACF,QAAQ,GAAA,CAAI,4BAAA,CAA8BA,CAAAA,CAAQ,IAAA,CAAM,aAAc,IAAA,CAAK,YAAY,EACvF,IAAA,CAAK,MAAA,CAAO,cAAc,WAAA,CAAYA,CAAAA,CAAS,KAAK,YAAY,CAAA,CAChE,QAAQ,GAAA,CAAI,6BAA6B,EAC3C,CAAA,MAAST,CAAAA,CAAO,CACd,OAAA,CAAQ,KAAA,CAAM,kCAAA,CAAoCA,CAAK,EACvD,IAAA,CAAK,SAAA,CAAUA,aAAiB,KAAA,CAAQA,CAAAA,CAAQ,IAAI,KAAA,CAAM,MAAA,CAAOA,CAAK,CAAC,CAAC,EAC1E,CACF,CAKQ,KAA0BiB,CAAAA,CAAUlB,CAAAA,CAA8B,CACxE,IAAMoB,CAAAA,CAAW,IAAA,CAAK,cAAA,CAAe,IAAIF,CAAK,CAAA,CAC1CE,GACFA,CAAAA,CAAS,OAAA,CAASD,GAAY,CAC5B,GAAI,CACFA,CAAAA,CAAQnB,CAAI,EACd,CAAA,MAASC,CAAAA,CAAO,CACd,OAAA,CAAQ,KAAA,CAAM,YAAYiB,CAAK,CAAA,eAAA,CAAA,CAAmBjB,CAAK,EACzD,CACF,CAAC,EAEL,CAKQ,UAAUA,CAAAA,CAAoB,CACpC,KAAK,IAAA,CAAK,OAAA,CAASA,CAAK,CAAA,CAGpB,IAAA,CAAK,QAAQ,OAAA,EACf,IAAA,CAAK,OAAO,OAAA,CAAQA,CAAK,EAE7B,CACF","file":"index.cjs","sourcesContent":["/**\n * Configuration for initializing the Sophi Widget\n */\nexport interface SophiConfig {\n /**\n * API key for authentication with Sophi services (x-api-key header)\n * This comes from the parent application\n */\n apiKey: string;\n\n /**\n * Client ID for authentication (x-sophi-client-id header)\n * This comes from the parent application\n */\n clientId: string;\n\n /**\n * Container element selector (e.g., '#widget') or HTMLElement where the widget will be mounted\n */\n container: string | HTMLElement;\n\n /**\n * Optional metadata to send with the session request\n */\n metadata?: Record<string, unknown>;\n\n /**\n * Width of the widget iframe\n * @default '100%'\n */\n width?: string;\n\n /**\n * Height of the widget iframe\n * @default '600px'\n */\n height?: string;\n\n /**\n * Callback fired when the widget is ready\n */\n onReady?: () => void;\n\n /**\n * Callback fired when an error occurs\n */\n onError?: (error: Error) => void;\n\n /**\n * Environment to use for the API\n */\n environment?: \"test\" | \"production\";\n}\n\n/**\n * Product information for add to cart events\n */\nexport interface Product {\n /**\n * Unique product identifier\n */\n productId: string;\n\n /**\n * Optional variant identifier\n */\n variantId?: string;\n}\n\n/**\n * Add to cart event data\n */\nexport interface AddToCartEvent {\n /**\n * List of products to add to cart\n */\n products: Product[];\n}\n\n/**\n * User data that can be sent to the widget\n */\nexport interface UserData {\n /**\n * User ID\n */\n userId?: string;\n\n /**\n * User email\n */\n email?: string;\n\n /**\n * User name\n */\n name?: string;\n\n /**\n * Additional custom data\n */\n [key: string]: unknown;\n}\n\nexport interface IUserCartItem {\n productId: string;\n variantId: string;\n quantity?: number;\n}\n\nexport interface IUserCart {\n cardId: string;\n items: IUserCartItem[];\n}\n\n/**\n * Message types that can be received from the iframe\n */\nexport type IncomingMessageType = \"add_to_cart\" | \"send_to_checkout\" | \"ready\" | \"error\";\n\n/**\n * Incoming message type constants for easy access\n * @example\n * widget.on(IncomingMessageTypes.ADD_TO_CART, (data) => {...})\n */\nexport const IncomingMessageTypes = {\n ADD_TO_CART: \"add_to_cart\",\n READY: \"ready\",\n ERROR: \"error\",\n SEND_TO_CHECKOUT: \"send_to_checkout\",\n} as const;\n\n/**\n * Message types that can be sent to the iframe\n */\nexport type OutgoingMessageType = \"user_data\" | \"config\" | \"update_user_cart\" | \"destroy\" | \"session_data\" | \"client_id_updated\";\n\n/**\n * Outgoing message type constants for easy access\n * @example\n * widget.sendMessage({ type: OutgoingMessageTypes.USER_DATA, data: {...} })\n */\nexport const OutgoingMessageTypes = {\n USER_DATA: \"user_data\",\n CONFIG: \"config\",\n UPDATE_USER_CART: \"update_user_cart\",\n DESTROY: \"destroy\",\n SESSION_DATA: \"session_data\",\n CLIENT_ID_UPDATED: \"client_id_updated\",\n} as const;\n\n/**\n * Incoming message structure from iframe\n */\nexport interface IncomingMessage {\n type: IncomingMessageType;\n data?: unknown;\n}\n\n/**\n * Outgoing message structure to iframe\n */\nexport interface OutgoingMessage {\n type: OutgoingMessageType;\n data?: unknown;\n}\n\n/**\n * Event map for type-safe event handling\n */\nexport interface SophiEventMap {\n /**\n * Fired when products should be added to cart\n */\n add_to_cart: AddToCartEvent;\n\n /**\n * Fired when the widget is ready\n */\n ready: void;\n\n /**\n * Fired when an error occurs\n */\n error: Error;\n\n /**\n * Fired when the user should be sent to checkout\n */\n send_to_checkout: void;\n}\n\n/**\n * Event handler type\n */\nexport type EventHandler<T> = (data: T) => void;\n\n/**\n * Event name type\n */\nexport type EventName = keyof SophiEventMap;\n\n/**\n * Authentication session request payload\n */\nexport interface AuthSessionRequest {\n /**\n * External user ID from the parent application\n */\n externalUserId: string;\n\n /**\n * Optional metadata to include with the session\n */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Session data returned from the authentication API\n */\nexport interface SessionData {\n /**\n * JWT access token for authenticating requests\n */\n accessToken: string;\n\n /**\n * Unique session identifier\n */\n sessionId: string;\n\n /**\n * Client ID\n */\n clientId: string;\n\n /**\n * Company code\n */\n companyCode: string;\n\n /**\n * Session expiration timestamp\n */\n expiresAt: string;\n}\n\n/**\n * Authentication session API response\n */\nexport interface AuthSessionResponse {\n /**\n * Whether the request was successful\n */\n success: boolean;\n\n /**\n * Response message\n */\n message: string;\n\n /**\n * Session data\n */\n data?: SessionData;\n}\n\n/**\n * Merge user request payload\n */\nexport interface MergeUserRequest {\n /**\n * The guest client ID to merge from\n */\n guestId: string;\n\n /**\n * The logged-in user ID to merge to\n */\n userId: string;\n}\n\n/**\n * Merge user data returned from the API\n */\nexport interface MergeUserData {\n /**\n * The resulting client ID after merge\n */\n clientId: string;\n\n /**\n * The external user ID\n */\n externalUserId: string;\n}\n\n/**\n * Merge user API response\n */\nexport interface MergeUserResponse {\n /**\n * Whether the request was successful\n */\n success: boolean;\n\n /**\n * Response message\n */\n message: string;\n\n /**\n * Merge result data\n */\n data?: MergeUserData;\n}\n","import type { AuthSessionRequest, AuthSessionResponse, SessionData, MergeUserRequest, MergeUserResponse, MergeUserData } from \"../types\";\n\n/**\n * API Service for Sophi authentication and session management\n */\nexport class ApiService {\n private baseUrl: string;\n private apiKey: string;\n private clientId: string;\n\n constructor(baseUrl: string, apiKey: string, clientId: string) {\n this.baseUrl = baseUrl;\n this.apiKey = apiKey;\n this.clientId = clientId;\n }\n\n /**\n * Create a client session by calling the authentication endpoint\n * @param request - Session request with externalUserId and optional metadata\n * @returns Session data including accessToken, sessionId, etc.\n */\n async createSession(request: AuthSessionRequest): Promise<SessionData> {\n const url = `${this.baseUrl}/api/v1/auth/client/session`;\n\n try {\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\",\n \"x-api-key\": this.apiKey,\n \"x-sophi-client-id\": this.clientId,\n },\n body: JSON.stringify(request),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Failed to create session: ${response.status} ${response.statusText}. ${errorText}`\n );\n }\n\n const data: AuthSessionResponse = await response.json();\n\n if (!data.success) {\n throw new Error(`Session creation failed: ${data.message || \"Unknown error\"}`);\n }\n\n if (!data.data) {\n throw new Error(\"Session data is missing from response\");\n }\n\n return data.data;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(`Unexpected error during session creation: ${String(error)}`);\n }\n }\n\n /**\n * Validate session data\n */\n validateSessionData(sessionData: SessionData): boolean {\n return !!(\n sessionData.accessToken &&\n sessionData.sessionId &&\n sessionData.clientId &&\n sessionData.companyCode &&\n sessionData.expiresAt\n );\n }\n\n /**\n * Merge guest user with logged-in user\n * @param request - Merge request with guestId and userId\n * @returns Merge result data including the resulting clientId\n */\n async mergeUser(request: MergeUserRequest): Promise<MergeUserData> {\n const url = `${this.baseUrl}/api/v1/auth/client/info`;\n\n try {\n const response = await fetch(url, {\n method: \"PATCH\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\",\n \"x-api-key\": this.apiKey,\n \"x-sophi-client-id\": request.guestId,\n },\n body: JSON.stringify({\n externalUserId: request.userId,\n }),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Failed to merge user: ${response.status} ${response.statusText}. ${errorText}`\n );\n }\n\n const data: MergeUserResponse = await response.json();\n\n if (!data.success) {\n throw new Error(`User merge failed: ${data.message || \"Unknown error\"}`);\n }\n\n if (!data.data) {\n throw new Error(\"Merge data is missing from response\");\n }\n\n return data.data;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(`Unexpected error during user merge: ${String(error)}`);\n }\n }\n}\n","/**\n * Default configuration constants for Sophi Widget\n */\nexport const DEFAULT_CONFIG = {\n /**\n * Default width of the widget iframe\n */\n WIDTH: \"100%\",\n\n /**\n * Default height of the widget iframe\n */\n HEIGHT: \"600px\",\n} as const;\n\n/**\n * API base URLs for different environments\n *\n * Note: These URLs will be visible in the client-side bundle.\n * This is normal and safe - the real security is in your API key validation.\n *\n * To use different URLs, modify these values before building the SDK.\n *\n * Production API URL - update before building for production\n */\nexport const API_BASE_URLS = {\n test: \"http://10.0.2.127:80\",\n production: \"https://api.usesophi.com\",\n} as const;\n\n/**\n * API endpoints configuration\n */\nexport const API_ENDPOINTS = {\n /**\n * Session creation endpoint path\n */\n CREATE_SESSION: \"/api/v1/auth/client/session\",\n} as const;\n\n/**\n * Error messages\n */\nexport const ERROR_MESSAGES = {\n MISSING_API_KEY: \"API key is required and must be a string\",\n MISSING_CLIENT_ID: \"Client ID is required and must be a string\",\n MISSING_USER_ID: \"User ID is required and must be a string\",\n MISSING_CONTAINER: \"Container is required\",\n MISSING_IFRAME_URL: \"Iframe URL is required and must be a string\",\n INVALID_IFRAME_URL: \"Invalid iframe URL\",\n INVALID_ENVIRONMENT: \"Invalid environment. Must be 'dev', 'test', 'staging', or 'production'\",\n CONTAINER_NOT_FOUND: \"Container element not found\",\n NOT_INITIALIZED: \"Widget not initialized. Call init() first.\",\n ALREADY_INITIALIZED: \"Sophi Widget is already initialized. Call destroy() first to reinitialize.\",\n SESSION_CREATION_FAILED: \"Failed to create authentication session\",\n INVALID_SESSION_DATA: \"Invalid session data received from API\",\n YOU_NEED_TO_INIT_THE_WIDGET_FIRST: \"You need to initialize the widget first. Call init() before calling this method.\",\n} as const;\n","import { type SophiConfig, type UserData, type EventHandler, type EventName, type SophiEventMap, type IncomingMessage, type OutgoingMessage, type AddToCartEvent, type IUserCart, type SessionData, type MergeUserRequest, type MergeUserData, OutgoingMessageTypes } from \"./types\";\nimport { ApiService } from \"./services/api.service\";\nimport { DEFAULT_CONFIG, ERROR_MESSAGES, API_BASE_URLS } from \"./constants/config\";\n\n/**\n * Main Sophi Widget SDK class\n * Provides a framework-agnostic way to embed and interact with the Sophi AI widget\n */\nexport class SophiWidget {\n private config: SophiConfig | null = null;\n private iframe: HTMLIFrameElement | null = null;\n private container: HTMLElement | null = null;\n private eventListeners: Map<EventName, Set<EventHandler<any>>> = new Map();\n private messageHandler: ((event: MessageEvent) => void) | null = null;\n private isInitialized = false;\n private iframeOrigin: string | null = null;\n private sessionData: SessionData | null = null;\n private apiService: ApiService | null = null;\n private apiBaseUrl: string | null = null;\n\n /**\n * Initialize the widget with configuration\n * @param config - Widget configuration\n */\n public async init(config: SophiConfig): Promise<void> {\n if (this.isInitialized) {\n console.warn(ERROR_MESSAGES.ALREADY_INITIALIZED);\n return;\n }\n\n try {\n // Validate configuration\n this.validateConfig(config);\n\n this.config = config;\n\n // Determine API URL based on environment\n this.apiBaseUrl = API_BASE_URLS[config.environment || \"production\"];\n\n // Initialize API service\n this.apiService = new ApiService(this.apiBaseUrl, config.apiKey, config.clientId);\n\n // Create authentication session\n await this.createAuthSession();\n\n this.isInitialized = true;\n\n // Extract origin from iframe URL for security\n try {\n const url = new URL(this.getIframeUrl());\n this.iframeOrigin = url.origin;\n } catch (error) {\n const err = new Error(`${ERROR_MESSAGES.INVALID_IFRAME_URL}: ${this.getIframeUrl()}`);\n this.emitError(err);\n throw err;\n }\n\n // Resolve container element\n this.container = this.resolveContainer(config.container);\n\n if (!this.container) {\n const err = new Error(ERROR_MESSAGES.CONTAINER_NOT_FOUND);\n this.emitError(err);\n throw err;\n }\n\n // Create and mount iframe\n this.createIframe();\n\n // Setup postMessage listener\n this.setupMessageListener();\n\n // Send session data to iframe after it's loaded\n this.iframe?.addEventListener(\"load\", () => {\n console.log(\"Iframe loaded, sending session data...\");\n // Small delay to ensure iframe is fully ready\n setTimeout(() => {\n this.sendSessionData();\n }, 100);\n });\n\n // Call onReady callback if provided\n if (config.onReady) {\n config.onReady();\n }\n } catch (error) {\n // Reset state on error\n this.isInitialized = false;\n this.config = null;\n this.apiService = null;\n this.sessionData = null;\n\n const err = error instanceof Error ? error : new Error(String(error));\n this.emitError(err);\n throw err;\n }\n }\n\n /**\n * Send user data to the widget\n * @param data - User data object\n */\n public sendUserData(data: UserData): void {\n if (!this.isInitialized || !this.iframe) {\n console.warn(\"Widget not initialized. Call init() first.\");\n return;\n }\n\n const message: OutgoingMessage = {\n type: OutgoingMessageTypes.USER_DATA,\n data,\n };\n\n this.postMessage(message);\n }\n\n public updateUserCart(cart: IUserCart): void {\n if (!this.isInitialized || !this.iframe) {\n console.warn(\"Widget not initialized. Call init() first.\");\n return;\n }\n\n const message: OutgoingMessage = {\n type: OutgoingMessageTypes.UPDATE_USER_CART,\n data: cart,\n };\n\n this.postMessage(message);\n }\n\n /**\n * Merge guest user with logged-in user\n * This can be called anytime, even before widget initialization\n * @param guestId - The guest client ID (previous clientId)\n * @param userId - The logged-in user ID (new externalUserId)\n * @param apiKey - API key for authentication (required if widget not initialized)\n * @returns Promise with merge result data\n */\n public async mergeUser(guestId: string, userId: string, apiKey?: string): Promise<MergeUserData> {\n try {\n let apiService: ApiService;\n if (!this.apiBaseUrl) {\n throw new Error(ERROR_MESSAGES.YOU_NEED_TO_INIT_THE_WIDGET_FIRST);\n }\n // Use existing API service if widget is initialized, otherwise create a temporary one\n if (this.apiService) {\n apiService = this.apiService;\n } else {\n // Widget not initialized - create temporary API service\n if (!apiKey) {\n throw new Error(\"API key is required when widget is not initialized\");\n }\n apiService = new ApiService(this.apiBaseUrl, apiKey, guestId);\n }\n\n const mergeData = await apiService.mergeUser({ guestId, userId });\n\n // If iframe exists, send the updated clientId to it\n // This allows the iframe to update its own stores automatically\n if (this.isInitialized && this.iframe) {\n const message: OutgoingMessage = {\n type: OutgoingMessageTypes.CLIENT_ID_UPDATED,\n data: {\n clientId: mergeData.clientId,\n externalUserId: mergeData.externalUserId,\n },\n };\n this.postMessage(message);\n }\n\n return mergeData;\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n // Only emit error if widget is initialized\n if (this.isInitialized) {\n this.emitError(err);\n }\n throw err;\n }\n }\n\n /**\n * Static method to merge guest user with logged-in user\n * Use this when you don't have a widget instance or want to merge before initialization\n * @param guestId - The guest client ID (previous clientId)\n * @param userId - The logged-in user ID (new externalUserId)\n * @param apiKey - API key for authentication\n * @returns Promise with merge result data\n */\n public static async mergeUser(\n guestId: string,\n userId: string,\n apiKey: string,\n {\n environment = \"test\",\n }: {\n environment?: SophiConfig[\"environment\"];\n } = {}\n ): Promise<MergeUserData> {\n const apiBaseUrl = API_BASE_URLS[environment];\n const apiService = new ApiService(apiBaseUrl, apiKey, guestId);\n return await apiService.mergeUser({ guestId, userId });\n }\n\n private onDestroy(): void {\n if (!this.isInitialized || !this.iframe) {\n console.warn(\"Widget not initialized. Call init() first.\");\n return;\n }\n\n const message: OutgoingMessage = {\n type: OutgoingMessageTypes.DESTROY,\n };\n\n this.postMessage(message);\n }\n\n /**\n * Register an event listener\n * @param event - Event name\n * @param handler - Event handler function\n */\n public on<K extends EventName>(event: K, handler: EventHandler<SophiEventMap[K]>): void {\n if (!this.eventListeners.has(event)) {\n this.eventListeners.set(event, new Set());\n }\n this.eventListeners.get(event)!.add(handler);\n }\n\n /**\n * Unregister an event listener\n * @param event - Event name\n * @param handler - Event handler function to remove\n */\n public off<K extends EventName>(event: K, handler: EventHandler<SophiEventMap[K]>): void {\n const handlers = this.eventListeners.get(event);\n if (handlers) {\n handlers.delete(handler);\n }\n }\n\n /**\n * Show the widget\n */\n public show(): void {\n if (this.iframe) {\n this.iframe.style.display = \"block\";\n }\n }\n\n /**\n * Hide the widget\n */\n public hide(): void {\n if (this.iframe) {\n this.iframe.style.display = \"none\";\n }\n }\n\n /**\n * Destroy the widget and cleanup resources\n * Waits 300ms after sending destroy message to give iframe time to cleanup\n */\n public async destroy(): Promise<void> {\n this.onDestroy();\n\n // Wait 300ms to give iframe time to process the destroy event and cleanup\n await new Promise((resolve) => setTimeout(resolve, 300));\n\n // Remove message listener\n if (this.messageHandler) {\n window.removeEventListener(\"message\", this.messageHandler);\n this.messageHandler = null;\n }\n\n // Remove iframe\n if (this.iframe && this.iframe.parentNode) {\n this.iframe.parentNode.removeChild(this.iframe);\n this.iframe = null;\n }\n\n // Clear event listeners\n this.eventListeners.clear();\n\n // Reset state\n this.config = null;\n this.container = null;\n this.isInitialized = false;\n this.iframeOrigin = null;\n this.sessionData = null;\n this.apiService = null;\n }\n\n /**\n * Check if widget is initialized\n */\n public isReady(): boolean {\n return this.isInitialized && this.iframe !== null;\n }\n\n /**\n * Validate configuration\n */\n private validateConfig(config: SophiConfig): void {\n if (!config.apiKey || typeof config.apiKey !== \"string\") {\n throw new Error(ERROR_MESSAGES.MISSING_API_KEY);\n }\n\n if (!config.clientId || typeof config.clientId !== \"string\") {\n throw new Error(ERROR_MESSAGES.MISSING_CLIENT_ID);\n }\n\n if (!config.clientId || typeof config.clientId !== \"string\") {\n throw new Error(ERROR_MESSAGES.MISSING_USER_ID);\n }\n\n if (!config.container) {\n throw new Error(ERROR_MESSAGES.MISSING_CONTAINER);\n }\n }\n\n /**\n * Create authentication session with the API\n */\n private async createAuthSession(): Promise<void> {\n if (!this.config || !this.apiService) {\n throw new Error(ERROR_MESSAGES.NOT_INITIALIZED);\n }\n\n try {\n this.sessionData = await this.apiService.createSession({\n externalUserId: this.config.clientId,\n metadata: this.config.metadata,\n });\n\n // Validate session data\n if (!this.apiService.validateSessionData(this.sessionData)) {\n throw new Error(ERROR_MESSAGES.INVALID_SESSION_DATA);\n }\n } catch (error) {\n const err = error instanceof Error ? error : new Error(ERROR_MESSAGES.SESSION_CREATION_FAILED);\n throw err;\n }\n }\n\n /**\n * Send session data to iframe\n */\n private sendSessionData(): void {\n if (!this.sessionData) {\n console.warn(\"Session data not available\");\n return;\n }\n\n const message: OutgoingMessage = {\n type: OutgoingMessageTypes.SESSION_DATA,\n data: this.sessionData,\n };\n\n this.postMessage(message);\n }\n\n /**\n * Resolve container element from selector or element\n */\n private resolveContainer(container: string | HTMLElement): HTMLElement | null {\n if (typeof container === \"string\") {\n return document.querySelector(container);\n }\n return container;\n }\n\n /**\n * Create and mount the iframe\n */\n private createIframe(): void {\n if (!this.container || !this.config) {\n return;\n }\n\n const iframe = document.createElement(\"iframe\");\n\n // Set iframe attributes\n iframe.src = this.buildIframeUrl();\n iframe.style.width = this.config.width || \"100%\";\n iframe.style.height = this.config.height || \"600px\";\n iframe.style.border = \"none\";\n iframe.title = \"Sophi AI Widget\";\n iframe.allow = \"microphone; camera\";\n\n // Append to container\n this.container.appendChild(iframe);\n this.iframe = iframe;\n }\n\n /**\n * Build iframe URL with query parameters\n */\n private buildIframeUrl(): string {\n if (!this.config) {\n return \"\";\n }\n const url = new URL(this.getIframeUrl());\n\n return url.toString();\n }\n\n private getIframeUrl(): string {\n if (!this.config) {\n return \"\";\n }\n if (this.config.environment === \"production\") {\n return \"http://app.usesophi.com\";\n }\n return \"http://10.0.2.127:80\";\n }\n\n /**\n * Setup postMessage listener for iframe communication\n */\n private setupMessageListener(): void {\n this.messageHandler = (event: MessageEvent) => {\n // Validate origin for security\n if (this.iframeOrigin && event.origin !== this.iframeOrigin) {\n console.warn(`Received message from untrusted origin: ${event.origin}`);\n return;\n }\n\n try {\n // Parse message\n const message: IncomingMessage = typeof event.data === \"string\" ? JSON.parse(event.data) : event.data;\n\n // Handle different message types\n this.handleIncomingMessage(message);\n } catch (error) {\n console.error(\"Error processing message from iframe:\", error);\n }\n };\n\n window.addEventListener(\"message\", this.messageHandler);\n }\n\n /**\n * Handle incoming messages from iframe\n */\n private handleIncomingMessage(message: IncomingMessage): void {\n switch (message.type) {\n case \"add_to_cart\":\n if (this.isValidAddToCartMessage(message.data)) {\n this.emit(\"add_to_cart\", message.data);\n } else {\n console.error(\"Invalid add_to_cart message structure:\", message.data);\n this.emitError(new Error(\"Invalid add_to_cart message format\"));\n }\n break;\n\n case \"send_to_checkout\":\n this.emit(\"send_to_checkout\", undefined);\n break;\n case \"ready\":\n this.emit(\"ready\", undefined);\n break;\n\n case \"error\":\n const error = message.data instanceof Error ? message.data : new Error(String(message.data));\n this.emitError(error);\n break;\n\n default:\n console.warn(\"Unknown message type:\", message);\n }\n }\n\n /**\n * Validate add_to_cart message structure\n */\n private isValidAddToCartMessage(data: unknown): data is AddToCartEvent {\n if (!data || typeof data !== \"object\") {\n return false;\n }\n\n const event = data as Record<string, unknown>;\n\n // Check products array exists and is an array\n if (!Array.isArray(event.products)) {\n return false;\n }\n\n // Validate each product in the array\n return event.products.every((product) => {\n if (!product || typeof product !== \"object\") {\n return false;\n }\n const prod = product as Record<string, unknown>;\n\n // productId is required and must be a string\n if (typeof prod.productId !== \"string\") {\n return false;\n }\n\n // variantId is optional, but if present must be a string\n if (prod.variantId !== undefined && typeof prod.variantId !== \"string\") {\n return false;\n }\n\n return true;\n });\n }\n\n /**\n * Post message to iframe\n */\n private postMessage(message: OutgoingMessage): void {\n if (!this.iframe || !this.iframe.contentWindow || !this.iframeOrigin) {\n console.warn(\"Cannot send message: iframe not ready\");\n return;\n }\n\n try {\n console.log(\"Posting message to iframe:\", message.type, \"to origin:\", this.iframeOrigin);\n this.iframe.contentWindow.postMessage(message, this.iframeOrigin);\n console.log(\"Message posted successfully\");\n } catch (error) {\n console.error(\"Error sending message to iframe:\", error);\n this.emitError(error instanceof Error ? error : new Error(String(error)));\n }\n }\n\n /**\n * Emit event to registered listeners\n */\n private emit<K extends EventName>(event: K, data: SophiEventMap[K]): void {\n const handlers = this.eventListeners.get(event);\n if (handlers) {\n handlers.forEach((handler) => {\n try {\n handler(data);\n } catch (error) {\n console.error(`Error in ${event} event handler:`, error);\n }\n });\n }\n }\n\n /**\n * Emit error event\n */\n private emitError(error: Error): void {\n this.emit(\"error\", error);\n\n // Also call onError callback if provided\n if (this.config?.onError) {\n this.config.onError(error);\n }\n }\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -447,7 +447,7 @@ declare const DEFAULT_CONFIG: {
447
447
  */
448
448
  declare const API_BASE_URLS: {
449
449
  readonly test: "http://10.0.2.127:80";
450
- readonly production: "https://api.usesophi.ai";
450
+ readonly production: "https://api.usesophi.com";
451
451
  };
452
452
  /**
453
453
  * API endpoints configuration
package/dist/index.d.ts CHANGED
@@ -447,7 +447,7 @@ declare const DEFAULT_CONFIG: {
447
447
  */
448
448
  declare const API_BASE_URLS: {
449
449
  readonly test: "http://10.0.2.127:80";
450
- readonly production: "https://api.usesophi.ai";
450
+ readonly production: "https://api.usesophi.com";
451
451
  };
452
452
  /**
453
453
  * API endpoints configuration
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- var g={ADD_TO_CART:"add_to_cart",READY:"ready",ERROR:"error",SEND_TO_CHECKOUT:"send_to_checkout"},a={USER_DATA:"user_data",CONFIG:"config",UPDATE_USER_CART:"update_user_cart",DESTROY:"destroy",SESSION_DATA:"session_data",CLIENT_ID_UPDATED:"client_id_updated"};var o=class{constructor(e,t,r){this.baseUrl=e,this.apiKey=t,this.clientId=r;}async createSession(e){let t=`${this.baseUrl}/api/v1/auth/client/session`;try{let r=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json","x-api-key":this.apiKey,"x-sophi-client-id":this.clientId},body:JSON.stringify(e)});if(!r.ok){let s=await r.text();throw new Error(`Failed to create session: ${r.status} ${r.statusText}. ${s}`)}let i=await r.json();if(!i.success)throw new Error(`Session creation failed: ${i.message||"Unknown error"}`);if(!i.data)throw new Error("Session data is missing from response");return i.data}catch(r){throw r instanceof Error?r:new Error(`Unexpected error during session creation: ${String(r)}`)}}validateSessionData(e){return !!(e.accessToken&&e.sessionId&&e.clientId&&e.companyCode&&e.expiresAt)}async mergeUser(e){let t=`${this.baseUrl}/api/v1/auth/client/info`;try{let r=await fetch(t,{method:"PATCH",headers:{"Content-Type":"application/json",Accept:"application/json","x-api-key":this.apiKey,"x-sophi-client-id":e.guestId},body:JSON.stringify({externalUserId:e.userId})});if(!r.ok){let s=await r.text();throw new Error(`Failed to merge user: ${r.status} ${r.statusText}. ${s}`)}let i=await r.json();if(!i.success)throw new Error(`User merge failed: ${i.message||"Unknown error"}`);if(!i.data)throw new Error("Merge data is missing from response");return i.data}catch(r){throw r instanceof Error?r:new Error(`Unexpected error during user merge: ${String(r)}`)}}};var h={WIDTH:"100%",HEIGHT:"600px"},d={test:"http://10.0.2.127:80",production:"https://api.usesophi.ai"},u={CREATE_SESSION:"/api/v1/auth/client/session"},n={MISSING_API_KEY:"API key is required and must be a string",MISSING_CLIENT_ID:"Client ID is required and must be a string",MISSING_USER_ID:"User ID is required and must be a string",MISSING_CONTAINER:"Container is required",MISSING_IFRAME_URL:"Iframe URL is required and must be a string",INVALID_IFRAME_URL:"Invalid iframe URL",INVALID_ENVIRONMENT:"Invalid environment. Must be 'dev', 'test', 'staging', or 'production'",CONTAINER_NOT_FOUND:"Container element not found",NOT_INITIALIZED:"Widget not initialized. Call init() first.",ALREADY_INITIALIZED:"Sophi Widget is already initialized. Call destroy() first to reinitialize.",SESSION_CREATION_FAILED:"Failed to create authentication session",INVALID_SESSION_DATA:"Invalid session data received from API",YOU_NEED_TO_INIT_THE_WIDGET_FIRST:"You need to initialize the widget first. Call init() before calling this method."};var c=class{constructor(){this.config=null;this.iframe=null;this.container=null;this.eventListeners=new Map;this.messageHandler=null;this.isInitialized=false;this.iframeOrigin=null;this.sessionData=null;this.apiService=null;this.apiBaseUrl=null;}async init(e){if(this.isInitialized){console.warn(n.ALREADY_INITIALIZED);return}try{this.validateConfig(e),this.config=e,this.apiBaseUrl=d[e.environment||"production"],this.apiService=new o(this.apiBaseUrl,e.apiKey,e.clientId),await this.createAuthSession(),this.isInitialized=!0;try{let t=new URL(this.getIframeUrl());this.iframeOrigin=t.origin;}catch{let r=new Error(`${n.INVALID_IFRAME_URL}: ${this.getIframeUrl()}`);throw this.emitError(r),r}if(this.container=this.resolveContainer(e.container),!this.container){let t=new Error(n.CONTAINER_NOT_FOUND);throw this.emitError(t),t}this.createIframe(),this.setupMessageListener(),this.iframe?.addEventListener("load",()=>{console.log("Iframe loaded, sending session data..."),setTimeout(()=>{this.sendSessionData();},100);}),e.onReady&&e.onReady();}catch(t){this.isInitialized=false,this.config=null,this.apiService=null,this.sessionData=null;let r=t instanceof Error?t:new Error(String(t));throw this.emitError(r),r}}sendUserData(e){if(!this.isInitialized||!this.iframe){console.warn("Widget not initialized. Call init() first.");return}let t={type:a.USER_DATA,data:e};this.postMessage(t);}updateUserCart(e){if(!this.isInitialized||!this.iframe){console.warn("Widget not initialized. Call init() first.");return}let t={type:a.UPDATE_USER_CART,data:e};this.postMessage(t);}async mergeUser(e,t,r){try{let i;if(!this.apiBaseUrl)throw new Error(n.YOU_NEED_TO_INIT_THE_WIDGET_FIRST);if(this.apiService)i=this.apiService;else {if(!r)throw new Error("API key is required when widget is not initialized");i=new o(this.apiBaseUrl,r,e);}let s=await i.mergeUser({guestId:e,userId:t});if(this.isInitialized&&this.iframe){let p={type:a.CLIENT_ID_UPDATED,data:{clientId:s.clientId,externalUserId:s.externalUserId}};this.postMessage(p);}return s}catch(i){let s=i instanceof Error?i:new Error(String(i));throw this.isInitialized&&this.emitError(s),s}}static async mergeUser(e,t,r,{environment:i="test"}={}){let s=d[i];return await new o(s,r,e).mergeUser({guestId:e,userId:t})}onDestroy(){if(!this.isInitialized||!this.iframe){console.warn("Widget not initialized. Call init() first.");return}let e={type:a.DESTROY};this.postMessage(e);}on(e,t){this.eventListeners.has(e)||this.eventListeners.set(e,new Set),this.eventListeners.get(e).add(t);}off(e,t){let r=this.eventListeners.get(e);r&&r.delete(t);}show(){this.iframe&&(this.iframe.style.display="block");}hide(){this.iframe&&(this.iframe.style.display="none");}async destroy(){this.onDestroy(),await new Promise(e=>setTimeout(e,300)),this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.iframe&&this.iframe.parentNode&&(this.iframe.parentNode.removeChild(this.iframe),this.iframe=null),this.eventListeners.clear(),this.config=null,this.container=null,this.isInitialized=false,this.iframeOrigin=null,this.sessionData=null,this.apiService=null;}isReady(){return this.isInitialized&&this.iframe!==null}validateConfig(e){if(!e.apiKey||typeof e.apiKey!="string")throw new Error(n.MISSING_API_KEY);if(!e.clientId||typeof e.clientId!="string")throw new Error(n.MISSING_CLIENT_ID);if(!e.clientId||typeof e.clientId!="string")throw new Error(n.MISSING_USER_ID);if(!e.container)throw new Error(n.MISSING_CONTAINER)}async createAuthSession(){if(!this.config||!this.apiService)throw new Error(n.NOT_INITIALIZED);try{if(this.sessionData=await this.apiService.createSession({externalUserId:this.config.clientId,metadata:this.config.metadata}),!this.apiService.validateSessionData(this.sessionData))throw new Error(n.INVALID_SESSION_DATA)}catch(e){throw e instanceof Error?e:new Error(n.SESSION_CREATION_FAILED)}}sendSessionData(){if(!this.sessionData){console.warn("Session data not available");return}let e={type:a.SESSION_DATA,data:this.sessionData};this.postMessage(e);}resolveContainer(e){return typeof e=="string"?document.querySelector(e):e}createIframe(){if(!this.container||!this.config)return;let e=document.createElement("iframe");e.src=this.buildIframeUrl(),e.style.width=this.config.width||"100%",e.style.height=this.config.height||"600px",e.style.border="none",e.title="Sophi AI Widget",e.allow="microphone; camera",this.container.appendChild(e),this.iframe=e;}buildIframeUrl(){return this.config?new URL(this.getIframeUrl()).toString():""}getIframeUrl(){return this.config?this.config.environment==="production"?"http://app.usesophi.com":"http://10.0.2.127:80":""}setupMessageListener(){this.messageHandler=e=>{if(this.iframeOrigin&&e.origin!==this.iframeOrigin){console.warn(`Received message from untrusted origin: ${e.origin}`);return}try{let t=typeof e.data=="string"?JSON.parse(e.data):e.data;this.handleIncomingMessage(t);}catch(t){console.error("Error processing message from iframe:",t);}},window.addEventListener("message",this.messageHandler);}handleIncomingMessage(e){switch(e.type){case "add_to_cart":this.isValidAddToCartMessage(e.data)?this.emit("add_to_cart",e.data):(console.error("Invalid add_to_cart message structure:",e.data),this.emitError(new Error("Invalid add_to_cart message format")));break;case "send_to_checkout":this.emit("send_to_checkout",void 0);break;case "ready":this.emit("ready",void 0);break;case "error":let t=e.data instanceof Error?e.data:new Error(String(e.data));this.emitError(t);break;default:console.warn("Unknown message type:",e);}}isValidAddToCartMessage(e){if(!e||typeof e!="object")return false;let t=e;return Array.isArray(t.products)?t.products.every(r=>{if(!r||typeof r!="object")return false;let i=r;return !(typeof i.productId!="string"||i.variantId!==void 0&&typeof i.variantId!="string")}):false}postMessage(e){if(!this.iframe||!this.iframe.contentWindow||!this.iframeOrigin){console.warn("Cannot send message: iframe not ready");return}try{console.log("Posting message to iframe:",e.type,"to origin:",this.iframeOrigin),this.iframe.contentWindow.postMessage(e,this.iframeOrigin),console.log("Message posted successfully");}catch(t){console.error("Error sending message to iframe:",t),this.emitError(t instanceof Error?t:new Error(String(t)));}}emit(e,t){let r=this.eventListeners.get(e);r&&r.forEach(i=>{try{i(t);}catch(s){console.error(`Error in ${e} event handler:`,s);}});}emitError(e){this.emit("error",e),this.config?.onError&&this.config.onError(e);}};
1
+ var g={ADD_TO_CART:"add_to_cart",READY:"ready",ERROR:"error",SEND_TO_CHECKOUT:"send_to_checkout"},a={USER_DATA:"user_data",CONFIG:"config",UPDATE_USER_CART:"update_user_cart",DESTROY:"destroy",SESSION_DATA:"session_data",CLIENT_ID_UPDATED:"client_id_updated"};var o=class{constructor(e,t,r){this.baseUrl=e,this.apiKey=t,this.clientId=r;}async createSession(e){let t=`${this.baseUrl}/api/v1/auth/client/session`;try{let r=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json","x-api-key":this.apiKey,"x-sophi-client-id":this.clientId},body:JSON.stringify(e)});if(!r.ok){let s=await r.text();throw new Error(`Failed to create session: ${r.status} ${r.statusText}. ${s}`)}let i=await r.json();if(!i.success)throw new Error(`Session creation failed: ${i.message||"Unknown error"}`);if(!i.data)throw new Error("Session data is missing from response");return i.data}catch(r){throw r instanceof Error?r:new Error(`Unexpected error during session creation: ${String(r)}`)}}validateSessionData(e){return !!(e.accessToken&&e.sessionId&&e.clientId&&e.companyCode&&e.expiresAt)}async mergeUser(e){let t=`${this.baseUrl}/api/v1/auth/client/info`;try{let r=await fetch(t,{method:"PATCH",headers:{"Content-Type":"application/json",Accept:"application/json","x-api-key":this.apiKey,"x-sophi-client-id":e.guestId},body:JSON.stringify({externalUserId:e.userId})});if(!r.ok){let s=await r.text();throw new Error(`Failed to merge user: ${r.status} ${r.statusText}. ${s}`)}let i=await r.json();if(!i.success)throw new Error(`User merge failed: ${i.message||"Unknown error"}`);if(!i.data)throw new Error("Merge data is missing from response");return i.data}catch(r){throw r instanceof Error?r:new Error(`Unexpected error during user merge: ${String(r)}`)}}};var h={WIDTH:"100%",HEIGHT:"600px"},d={test:"http://10.0.2.127:80",production:"https://api.usesophi.com"},u={CREATE_SESSION:"/api/v1/auth/client/session"},n={MISSING_API_KEY:"API key is required and must be a string",MISSING_CLIENT_ID:"Client ID is required and must be a string",MISSING_USER_ID:"User ID is required and must be a string",MISSING_CONTAINER:"Container is required",MISSING_IFRAME_URL:"Iframe URL is required and must be a string",INVALID_IFRAME_URL:"Invalid iframe URL",INVALID_ENVIRONMENT:"Invalid environment. Must be 'dev', 'test', 'staging', or 'production'",CONTAINER_NOT_FOUND:"Container element not found",NOT_INITIALIZED:"Widget not initialized. Call init() first.",ALREADY_INITIALIZED:"Sophi Widget is already initialized. Call destroy() first to reinitialize.",SESSION_CREATION_FAILED:"Failed to create authentication session",INVALID_SESSION_DATA:"Invalid session data received from API",YOU_NEED_TO_INIT_THE_WIDGET_FIRST:"You need to initialize the widget first. Call init() before calling this method."};var c=class{constructor(){this.config=null;this.iframe=null;this.container=null;this.eventListeners=new Map;this.messageHandler=null;this.isInitialized=false;this.iframeOrigin=null;this.sessionData=null;this.apiService=null;this.apiBaseUrl=null;}async init(e){if(this.isInitialized){console.warn(n.ALREADY_INITIALIZED);return}try{this.validateConfig(e),this.config=e,this.apiBaseUrl=d[e.environment||"production"],this.apiService=new o(this.apiBaseUrl,e.apiKey,e.clientId),await this.createAuthSession(),this.isInitialized=!0;try{let t=new URL(this.getIframeUrl());this.iframeOrigin=t.origin;}catch{let r=new Error(`${n.INVALID_IFRAME_URL}: ${this.getIframeUrl()}`);throw this.emitError(r),r}if(this.container=this.resolveContainer(e.container),!this.container){let t=new Error(n.CONTAINER_NOT_FOUND);throw this.emitError(t),t}this.createIframe(),this.setupMessageListener(),this.iframe?.addEventListener("load",()=>{console.log("Iframe loaded, sending session data..."),setTimeout(()=>{this.sendSessionData();},100);}),e.onReady&&e.onReady();}catch(t){this.isInitialized=false,this.config=null,this.apiService=null,this.sessionData=null;let r=t instanceof Error?t:new Error(String(t));throw this.emitError(r),r}}sendUserData(e){if(!this.isInitialized||!this.iframe){console.warn("Widget not initialized. Call init() first.");return}let t={type:a.USER_DATA,data:e};this.postMessage(t);}updateUserCart(e){if(!this.isInitialized||!this.iframe){console.warn("Widget not initialized. Call init() first.");return}let t={type:a.UPDATE_USER_CART,data:e};this.postMessage(t);}async mergeUser(e,t,r){try{let i;if(!this.apiBaseUrl)throw new Error(n.YOU_NEED_TO_INIT_THE_WIDGET_FIRST);if(this.apiService)i=this.apiService;else {if(!r)throw new Error("API key is required when widget is not initialized");i=new o(this.apiBaseUrl,r,e);}let s=await i.mergeUser({guestId:e,userId:t});if(this.isInitialized&&this.iframe){let p={type:a.CLIENT_ID_UPDATED,data:{clientId:s.clientId,externalUserId:s.externalUserId}};this.postMessage(p);}return s}catch(i){let s=i instanceof Error?i:new Error(String(i));throw this.isInitialized&&this.emitError(s),s}}static async mergeUser(e,t,r,{environment:i="test"}={}){let s=d[i];return await new o(s,r,e).mergeUser({guestId:e,userId:t})}onDestroy(){if(!this.isInitialized||!this.iframe){console.warn("Widget not initialized. Call init() first.");return}let e={type:a.DESTROY};this.postMessage(e);}on(e,t){this.eventListeners.has(e)||this.eventListeners.set(e,new Set),this.eventListeners.get(e).add(t);}off(e,t){let r=this.eventListeners.get(e);r&&r.delete(t);}show(){this.iframe&&(this.iframe.style.display="block");}hide(){this.iframe&&(this.iframe.style.display="none");}async destroy(){this.onDestroy(),await new Promise(e=>setTimeout(e,300)),this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.iframe&&this.iframe.parentNode&&(this.iframe.parentNode.removeChild(this.iframe),this.iframe=null),this.eventListeners.clear(),this.config=null,this.container=null,this.isInitialized=false,this.iframeOrigin=null,this.sessionData=null,this.apiService=null;}isReady(){return this.isInitialized&&this.iframe!==null}validateConfig(e){if(!e.apiKey||typeof e.apiKey!="string")throw new Error(n.MISSING_API_KEY);if(!e.clientId||typeof e.clientId!="string")throw new Error(n.MISSING_CLIENT_ID);if(!e.clientId||typeof e.clientId!="string")throw new Error(n.MISSING_USER_ID);if(!e.container)throw new Error(n.MISSING_CONTAINER)}async createAuthSession(){if(!this.config||!this.apiService)throw new Error(n.NOT_INITIALIZED);try{if(this.sessionData=await this.apiService.createSession({externalUserId:this.config.clientId,metadata:this.config.metadata}),!this.apiService.validateSessionData(this.sessionData))throw new Error(n.INVALID_SESSION_DATA)}catch(e){throw e instanceof Error?e:new Error(n.SESSION_CREATION_FAILED)}}sendSessionData(){if(!this.sessionData){console.warn("Session data not available");return}let e={type:a.SESSION_DATA,data:this.sessionData};this.postMessage(e);}resolveContainer(e){return typeof e=="string"?document.querySelector(e):e}createIframe(){if(!this.container||!this.config)return;let e=document.createElement("iframe");e.src=this.buildIframeUrl(),e.style.width=this.config.width||"100%",e.style.height=this.config.height||"600px",e.style.border="none",e.title="Sophi AI Widget",e.allow="microphone; camera",this.container.appendChild(e),this.iframe=e;}buildIframeUrl(){return this.config?new URL(this.getIframeUrl()).toString():""}getIframeUrl(){return this.config?this.config.environment==="production"?"http://app.usesophi.com":"http://10.0.2.127:80":""}setupMessageListener(){this.messageHandler=e=>{if(this.iframeOrigin&&e.origin!==this.iframeOrigin){console.warn(`Received message from untrusted origin: ${e.origin}`);return}try{let t=typeof e.data=="string"?JSON.parse(e.data):e.data;this.handleIncomingMessage(t);}catch(t){console.error("Error processing message from iframe:",t);}},window.addEventListener("message",this.messageHandler);}handleIncomingMessage(e){switch(e.type){case "add_to_cart":this.isValidAddToCartMessage(e.data)?this.emit("add_to_cart",e.data):(console.error("Invalid add_to_cart message structure:",e.data),this.emitError(new Error("Invalid add_to_cart message format")));break;case "send_to_checkout":this.emit("send_to_checkout",void 0);break;case "ready":this.emit("ready",void 0);break;case "error":let t=e.data instanceof Error?e.data:new Error(String(e.data));this.emitError(t);break;default:console.warn("Unknown message type:",e);}}isValidAddToCartMessage(e){if(!e||typeof e!="object")return false;let t=e;return Array.isArray(t.products)?t.products.every(r=>{if(!r||typeof r!="object")return false;let i=r;return !(typeof i.productId!="string"||i.variantId!==void 0&&typeof i.variantId!="string")}):false}postMessage(e){if(!this.iframe||!this.iframe.contentWindow||!this.iframeOrigin){console.warn("Cannot send message: iframe not ready");return}try{console.log("Posting message to iframe:",e.type,"to origin:",this.iframeOrigin),this.iframe.contentWindow.postMessage(e,this.iframeOrigin),console.log("Message posted successfully");}catch(t){console.error("Error sending message to iframe:",t),this.emitError(t instanceof Error?t:new Error(String(t)));}}emit(e,t){let r=this.eventListeners.get(e);r&&r.forEach(i=>{try{i(t);}catch(s){console.error(`Error in ${e} event handler:`,s);}});}emitError(e){this.emit("error",e),this.config?.onError&&this.config.onError(e);}};
2
2
  export{d as API_BASE_URLS,u as API_ENDPOINTS,o as ApiService,h as DEFAULT_CONFIG,n as ERROR_MESSAGES,g as IncomingMessageTypes,a as OutgoingMessageTypes,c as SophiWidget};//# sourceMappingURL=index.js.map
3
3
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts","../src/services/api.service.ts","../src/constants/config.ts","../src/SophiWidget.ts"],"names":["IncomingMessageTypes","OutgoingMessageTypes","ApiService","baseUrl","apiKey","clientId","request","url","response","errorText","data","error","sessionData","DEFAULT_CONFIG","API_BASE_URLS","API_ENDPOINTS","ERROR_MESSAGES","SophiWidget","config","err","message","cart","guestId","userId","apiService","mergeData","environment","apiBaseUrl","event","handler","handlers","resolve","container","iframe","product","prod"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Configuration for initializing the Sophi Widget\n */\nexport interface SophiConfig {\n /**\n * API key for authentication with Sophi services (x-api-key header)\n * This comes from the parent application\n */\n apiKey: string;\n\n /**\n * Client ID for authentication (x-sophi-client-id header)\n * This comes from the parent application\n */\n clientId: string;\n\n /**\n * Container element selector (e.g., '#widget') or HTMLElement where the widget will be mounted\n */\n container: string | HTMLElement;\n\n /**\n * Optional metadata to send with the session request\n */\n metadata?: Record<string, unknown>;\n\n /**\n * Width of the widget iframe\n * @default '100%'\n */\n width?: string;\n\n /**\n * Height of the widget iframe\n * @default '600px'\n */\n height?: string;\n\n /**\n * Callback fired when the widget is ready\n */\n onReady?: () => void;\n\n /**\n * Callback fired when an error occurs\n */\n onError?: (error: Error) => void;\n\n /**\n * Environment to use for the API\n */\n environment?: \"test\" | \"production\";\n}\n\n/**\n * Product information for add to cart events\n */\nexport interface Product {\n /**\n * Unique product identifier\n */\n productId: string;\n\n /**\n * Optional variant identifier\n */\n variantId?: string;\n}\n\n/**\n * Add to cart event data\n */\nexport interface AddToCartEvent {\n /**\n * List of products to add to cart\n */\n products: Product[];\n}\n\n/**\n * User data that can be sent to the widget\n */\nexport interface UserData {\n /**\n * User ID\n */\n userId?: string;\n\n /**\n * User email\n */\n email?: string;\n\n /**\n * User name\n */\n name?: string;\n\n /**\n * Additional custom data\n */\n [key: string]: unknown;\n}\n\nexport interface IUserCartItem {\n productId: string;\n variantId: string;\n quantity?: number;\n}\n\nexport interface IUserCart {\n cardId: string;\n items: IUserCartItem[];\n}\n\n/**\n * Message types that can be received from the iframe\n */\nexport type IncomingMessageType = \"add_to_cart\" | \"send_to_checkout\" | \"ready\" | \"error\";\n\n/**\n * Incoming message type constants for easy access\n * @example\n * widget.on(IncomingMessageTypes.ADD_TO_CART, (data) => {...})\n */\nexport const IncomingMessageTypes = {\n ADD_TO_CART: \"add_to_cart\",\n READY: \"ready\",\n ERROR: \"error\",\n SEND_TO_CHECKOUT: \"send_to_checkout\",\n} as const;\n\n/**\n * Message types that can be sent to the iframe\n */\nexport type OutgoingMessageType = \"user_data\" | \"config\" | \"update_user_cart\" | \"destroy\" | \"session_data\" | \"client_id_updated\";\n\n/**\n * Outgoing message type constants for easy access\n * @example\n * widget.sendMessage({ type: OutgoingMessageTypes.USER_DATA, data: {...} })\n */\nexport const OutgoingMessageTypes = {\n USER_DATA: \"user_data\",\n CONFIG: \"config\",\n UPDATE_USER_CART: \"update_user_cart\",\n DESTROY: \"destroy\",\n SESSION_DATA: \"session_data\",\n CLIENT_ID_UPDATED: \"client_id_updated\",\n} as const;\n\n/**\n * Incoming message structure from iframe\n */\nexport interface IncomingMessage {\n type: IncomingMessageType;\n data?: unknown;\n}\n\n/**\n * Outgoing message structure to iframe\n */\nexport interface OutgoingMessage {\n type: OutgoingMessageType;\n data?: unknown;\n}\n\n/**\n * Event map for type-safe event handling\n */\nexport interface SophiEventMap {\n /**\n * Fired when products should be added to cart\n */\n add_to_cart: AddToCartEvent;\n\n /**\n * Fired when the widget is ready\n */\n ready: void;\n\n /**\n * Fired when an error occurs\n */\n error: Error;\n\n /**\n * Fired when the user should be sent to checkout\n */\n send_to_checkout: void;\n}\n\n/**\n * Event handler type\n */\nexport type EventHandler<T> = (data: T) => void;\n\n/**\n * Event name type\n */\nexport type EventName = keyof SophiEventMap;\n\n/**\n * Authentication session request payload\n */\nexport interface AuthSessionRequest {\n /**\n * External user ID from the parent application\n */\n externalUserId: string;\n\n /**\n * Optional metadata to include with the session\n */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Session data returned from the authentication API\n */\nexport interface SessionData {\n /**\n * JWT access token for authenticating requests\n */\n accessToken: string;\n\n /**\n * Unique session identifier\n */\n sessionId: string;\n\n /**\n * Client ID\n */\n clientId: string;\n\n /**\n * Company code\n */\n companyCode: string;\n\n /**\n * Session expiration timestamp\n */\n expiresAt: string;\n}\n\n/**\n * Authentication session API response\n */\nexport interface AuthSessionResponse {\n /**\n * Whether the request was successful\n */\n success: boolean;\n\n /**\n * Response message\n */\n message: string;\n\n /**\n * Session data\n */\n data?: SessionData;\n}\n\n/**\n * Merge user request payload\n */\nexport interface MergeUserRequest {\n /**\n * The guest client ID to merge from\n */\n guestId: string;\n\n /**\n * The logged-in user ID to merge to\n */\n userId: string;\n}\n\n/**\n * Merge user data returned from the API\n */\nexport interface MergeUserData {\n /**\n * The resulting client ID after merge\n */\n clientId: string;\n\n /**\n * The external user ID\n */\n externalUserId: string;\n}\n\n/**\n * Merge user API response\n */\nexport interface MergeUserResponse {\n /**\n * Whether the request was successful\n */\n success: boolean;\n\n /**\n * Response message\n */\n message: string;\n\n /**\n * Merge result data\n */\n data?: MergeUserData;\n}\n","import type { AuthSessionRequest, AuthSessionResponse, SessionData, MergeUserRequest, MergeUserResponse, MergeUserData } from \"../types\";\n\n/**\n * API Service for Sophi authentication and session management\n */\nexport class ApiService {\n private baseUrl: string;\n private apiKey: string;\n private clientId: string;\n\n constructor(baseUrl: string, apiKey: string, clientId: string) {\n this.baseUrl = baseUrl;\n this.apiKey = apiKey;\n this.clientId = clientId;\n }\n\n /**\n * Create a client session by calling the authentication endpoint\n * @param request - Session request with externalUserId and optional metadata\n * @returns Session data including accessToken, sessionId, etc.\n */\n async createSession(request: AuthSessionRequest): Promise<SessionData> {\n const url = `${this.baseUrl}/api/v1/auth/client/session`;\n\n try {\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\",\n \"x-api-key\": this.apiKey,\n \"x-sophi-client-id\": this.clientId,\n },\n body: JSON.stringify(request),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Failed to create session: ${response.status} ${response.statusText}. ${errorText}`\n );\n }\n\n const data: AuthSessionResponse = await response.json();\n\n if (!data.success) {\n throw new Error(`Session creation failed: ${data.message || \"Unknown error\"}`);\n }\n\n if (!data.data) {\n throw new Error(\"Session data is missing from response\");\n }\n\n return data.data;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(`Unexpected error during session creation: ${String(error)}`);\n }\n }\n\n /**\n * Validate session data\n */\n validateSessionData(sessionData: SessionData): boolean {\n return !!(\n sessionData.accessToken &&\n sessionData.sessionId &&\n sessionData.clientId &&\n sessionData.companyCode &&\n sessionData.expiresAt\n );\n }\n\n /**\n * Merge guest user with logged-in user\n * @param request - Merge request with guestId and userId\n * @returns Merge result data including the resulting clientId\n */\n async mergeUser(request: MergeUserRequest): Promise<MergeUserData> {\n const url = `${this.baseUrl}/api/v1/auth/client/info`;\n\n try {\n const response = await fetch(url, {\n method: \"PATCH\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\",\n \"x-api-key\": this.apiKey,\n \"x-sophi-client-id\": request.guestId,\n },\n body: JSON.stringify({\n externalUserId: request.userId,\n }),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Failed to merge user: ${response.status} ${response.statusText}. ${errorText}`\n );\n }\n\n const data: MergeUserResponse = await response.json();\n\n if (!data.success) {\n throw new Error(`User merge failed: ${data.message || \"Unknown error\"}`);\n }\n\n if (!data.data) {\n throw new Error(\"Merge data is missing from response\");\n }\n\n return data.data;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(`Unexpected error during user merge: ${String(error)}`);\n }\n }\n}\n","/**\n * Default configuration constants for Sophi Widget\n */\nexport const DEFAULT_CONFIG = {\n /**\n * Default width of the widget iframe\n */\n WIDTH: \"100%\",\n\n /**\n * Default height of the widget iframe\n */\n HEIGHT: \"600px\",\n} as const;\n\n/**\n * API base URLs for different environments\n *\n * Note: These URLs will be visible in the client-side bundle.\n * This is normal and safe - the real security is in your API key validation.\n *\n * To use different URLs, modify these values before building the SDK.\n *\n * Production API URL - update before building for production\n */\nexport const API_BASE_URLS = {\n test: \"http://10.0.2.127:80\",\n production: \"https://api.usesophi.ai\",\n} as const;\n\n/**\n * API endpoints configuration\n */\nexport const API_ENDPOINTS = {\n /**\n * Session creation endpoint path\n */\n CREATE_SESSION: \"/api/v1/auth/client/session\",\n} as const;\n\n/**\n * Error messages\n */\nexport const ERROR_MESSAGES = {\n MISSING_API_KEY: \"API key is required and must be a string\",\n MISSING_CLIENT_ID: \"Client ID is required and must be a string\",\n MISSING_USER_ID: \"User ID is required and must be a string\",\n MISSING_CONTAINER: \"Container is required\",\n MISSING_IFRAME_URL: \"Iframe URL is required and must be a string\",\n INVALID_IFRAME_URL: \"Invalid iframe URL\",\n INVALID_ENVIRONMENT: \"Invalid environment. Must be 'dev', 'test', 'staging', or 'production'\",\n CONTAINER_NOT_FOUND: \"Container element not found\",\n NOT_INITIALIZED: \"Widget not initialized. Call init() first.\",\n ALREADY_INITIALIZED: \"Sophi Widget is already initialized. Call destroy() first to reinitialize.\",\n SESSION_CREATION_FAILED: \"Failed to create authentication session\",\n INVALID_SESSION_DATA: \"Invalid session data received from API\",\n YOU_NEED_TO_INIT_THE_WIDGET_FIRST: \"You need to initialize the widget first. Call init() before calling this method.\",\n} as const;\n","import { type SophiConfig, type UserData, type EventHandler, type EventName, type SophiEventMap, type IncomingMessage, type OutgoingMessage, type AddToCartEvent, type IUserCart, type SessionData, type MergeUserRequest, type MergeUserData, OutgoingMessageTypes } from \"./types\";\nimport { ApiService } from \"./services/api.service\";\nimport { DEFAULT_CONFIG, ERROR_MESSAGES, API_BASE_URLS } from \"./constants/config\";\n\n/**\n * Main Sophi Widget SDK class\n * Provides a framework-agnostic way to embed and interact with the Sophi AI widget\n */\nexport class SophiWidget {\n private config: SophiConfig | null = null;\n private iframe: HTMLIFrameElement | null = null;\n private container: HTMLElement | null = null;\n private eventListeners: Map<EventName, Set<EventHandler<any>>> = new Map();\n private messageHandler: ((event: MessageEvent) => void) | null = null;\n private isInitialized = false;\n private iframeOrigin: string | null = null;\n private sessionData: SessionData | null = null;\n private apiService: ApiService | null = null;\n private apiBaseUrl: string | null = null;\n\n /**\n * Initialize the widget with configuration\n * @param config - Widget configuration\n */\n public async init(config: SophiConfig): Promise<void> {\n if (this.isInitialized) {\n console.warn(ERROR_MESSAGES.ALREADY_INITIALIZED);\n return;\n }\n\n try {\n // Validate configuration\n this.validateConfig(config);\n\n this.config = config;\n\n // Determine API URL based on environment\n this.apiBaseUrl = API_BASE_URLS[config.environment || \"production\"];\n\n // Initialize API service\n this.apiService = new ApiService(this.apiBaseUrl, config.apiKey, config.clientId);\n\n // Create authentication session\n await this.createAuthSession();\n\n this.isInitialized = true;\n\n // Extract origin from iframe URL for security\n try {\n const url = new URL(this.getIframeUrl());\n this.iframeOrigin = url.origin;\n } catch (error) {\n const err = new Error(`${ERROR_MESSAGES.INVALID_IFRAME_URL}: ${this.getIframeUrl()}`);\n this.emitError(err);\n throw err;\n }\n\n // Resolve container element\n this.container = this.resolveContainer(config.container);\n\n if (!this.container) {\n const err = new Error(ERROR_MESSAGES.CONTAINER_NOT_FOUND);\n this.emitError(err);\n throw err;\n }\n\n // Create and mount iframe\n this.createIframe();\n\n // Setup postMessage listener\n this.setupMessageListener();\n\n // Send session data to iframe after it's loaded\n this.iframe?.addEventListener(\"load\", () => {\n console.log(\"Iframe loaded, sending session data...\");\n // Small delay to ensure iframe is fully ready\n setTimeout(() => {\n this.sendSessionData();\n }, 100);\n });\n\n // Call onReady callback if provided\n if (config.onReady) {\n config.onReady();\n }\n } catch (error) {\n // Reset state on error\n this.isInitialized = false;\n this.config = null;\n this.apiService = null;\n this.sessionData = null;\n\n const err = error instanceof Error ? error : new Error(String(error));\n this.emitError(err);\n throw err;\n }\n }\n\n /**\n * Send user data to the widget\n * @param data - User data object\n */\n public sendUserData(data: UserData): void {\n if (!this.isInitialized || !this.iframe) {\n console.warn(\"Widget not initialized. Call init() first.\");\n return;\n }\n\n const message: OutgoingMessage = {\n type: OutgoingMessageTypes.USER_DATA,\n data,\n };\n\n this.postMessage(message);\n }\n\n public updateUserCart(cart: IUserCart): void {\n if (!this.isInitialized || !this.iframe) {\n console.warn(\"Widget not initialized. Call init() first.\");\n return;\n }\n\n const message: OutgoingMessage = {\n type: OutgoingMessageTypes.UPDATE_USER_CART,\n data: cart,\n };\n\n this.postMessage(message);\n }\n\n /**\n * Merge guest user with logged-in user\n * This can be called anytime, even before widget initialization\n * @param guestId - The guest client ID (previous clientId)\n * @param userId - The logged-in user ID (new externalUserId)\n * @param apiKey - API key for authentication (required if widget not initialized)\n * @returns Promise with merge result data\n */\n public async mergeUser(guestId: string, userId: string, apiKey?: string): Promise<MergeUserData> {\n try {\n let apiService: ApiService;\n if (!this.apiBaseUrl) {\n throw new Error(ERROR_MESSAGES.YOU_NEED_TO_INIT_THE_WIDGET_FIRST);\n }\n // Use existing API service if widget is initialized, otherwise create a temporary one\n if (this.apiService) {\n apiService = this.apiService;\n } else {\n // Widget not initialized - create temporary API service\n if (!apiKey) {\n throw new Error(\"API key is required when widget is not initialized\");\n }\n apiService = new ApiService(this.apiBaseUrl, apiKey, guestId);\n }\n\n const mergeData = await apiService.mergeUser({ guestId, userId });\n\n // If iframe exists, send the updated clientId to it\n // This allows the iframe to update its own stores automatically\n if (this.isInitialized && this.iframe) {\n const message: OutgoingMessage = {\n type: OutgoingMessageTypes.CLIENT_ID_UPDATED,\n data: {\n clientId: mergeData.clientId,\n externalUserId: mergeData.externalUserId,\n },\n };\n this.postMessage(message);\n }\n\n return mergeData;\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n // Only emit error if widget is initialized\n if (this.isInitialized) {\n this.emitError(err);\n }\n throw err;\n }\n }\n\n /**\n * Static method to merge guest user with logged-in user\n * Use this when you don't have a widget instance or want to merge before initialization\n * @param guestId - The guest client ID (previous clientId)\n * @param userId - The logged-in user ID (new externalUserId)\n * @param apiKey - API key for authentication\n * @returns Promise with merge result data\n */\n public static async mergeUser(\n guestId: string,\n userId: string,\n apiKey: string,\n {\n environment = \"test\",\n }: {\n environment?: SophiConfig[\"environment\"];\n } = {}\n ): Promise<MergeUserData> {\n const apiBaseUrl = API_BASE_URLS[environment];\n const apiService = new ApiService(apiBaseUrl, apiKey, guestId);\n return await apiService.mergeUser({ guestId, userId });\n }\n\n private onDestroy(): void {\n if (!this.isInitialized || !this.iframe) {\n console.warn(\"Widget not initialized. Call init() first.\");\n return;\n }\n\n const message: OutgoingMessage = {\n type: OutgoingMessageTypes.DESTROY,\n };\n\n this.postMessage(message);\n }\n\n /**\n * Register an event listener\n * @param event - Event name\n * @param handler - Event handler function\n */\n public on<K extends EventName>(event: K, handler: EventHandler<SophiEventMap[K]>): void {\n if (!this.eventListeners.has(event)) {\n this.eventListeners.set(event, new Set());\n }\n this.eventListeners.get(event)!.add(handler);\n }\n\n /**\n * Unregister an event listener\n * @param event - Event name\n * @param handler - Event handler function to remove\n */\n public off<K extends EventName>(event: K, handler: EventHandler<SophiEventMap[K]>): void {\n const handlers = this.eventListeners.get(event);\n if (handlers) {\n handlers.delete(handler);\n }\n }\n\n /**\n * Show the widget\n */\n public show(): void {\n if (this.iframe) {\n this.iframe.style.display = \"block\";\n }\n }\n\n /**\n * Hide the widget\n */\n public hide(): void {\n if (this.iframe) {\n this.iframe.style.display = \"none\";\n }\n }\n\n /**\n * Destroy the widget and cleanup resources\n * Waits 300ms after sending destroy message to give iframe time to cleanup\n */\n public async destroy(): Promise<void> {\n this.onDestroy();\n\n // Wait 300ms to give iframe time to process the destroy event and cleanup\n await new Promise((resolve) => setTimeout(resolve, 300));\n\n // Remove message listener\n if (this.messageHandler) {\n window.removeEventListener(\"message\", this.messageHandler);\n this.messageHandler = null;\n }\n\n // Remove iframe\n if (this.iframe && this.iframe.parentNode) {\n this.iframe.parentNode.removeChild(this.iframe);\n this.iframe = null;\n }\n\n // Clear event listeners\n this.eventListeners.clear();\n\n // Reset state\n this.config = null;\n this.container = null;\n this.isInitialized = false;\n this.iframeOrigin = null;\n this.sessionData = null;\n this.apiService = null;\n }\n\n /**\n * Check if widget is initialized\n */\n public isReady(): boolean {\n return this.isInitialized && this.iframe !== null;\n }\n\n /**\n * Validate configuration\n */\n private validateConfig(config: SophiConfig): void {\n if (!config.apiKey || typeof config.apiKey !== \"string\") {\n throw new Error(ERROR_MESSAGES.MISSING_API_KEY);\n }\n\n if (!config.clientId || typeof config.clientId !== \"string\") {\n throw new Error(ERROR_MESSAGES.MISSING_CLIENT_ID);\n }\n\n if (!config.clientId || typeof config.clientId !== \"string\") {\n throw new Error(ERROR_MESSAGES.MISSING_USER_ID);\n }\n\n if (!config.container) {\n throw new Error(ERROR_MESSAGES.MISSING_CONTAINER);\n }\n }\n\n /**\n * Create authentication session with the API\n */\n private async createAuthSession(): Promise<void> {\n if (!this.config || !this.apiService) {\n throw new Error(ERROR_MESSAGES.NOT_INITIALIZED);\n }\n\n try {\n this.sessionData = await this.apiService.createSession({\n externalUserId: this.config.clientId,\n metadata: this.config.metadata,\n });\n\n // Validate session data\n if (!this.apiService.validateSessionData(this.sessionData)) {\n throw new Error(ERROR_MESSAGES.INVALID_SESSION_DATA);\n }\n } catch (error) {\n const err = error instanceof Error ? error : new Error(ERROR_MESSAGES.SESSION_CREATION_FAILED);\n throw err;\n }\n }\n\n /**\n * Send session data to iframe\n */\n private sendSessionData(): void {\n if (!this.sessionData) {\n console.warn(\"Session data not available\");\n return;\n }\n\n const message: OutgoingMessage = {\n type: OutgoingMessageTypes.SESSION_DATA,\n data: this.sessionData,\n };\n\n this.postMessage(message);\n }\n\n /**\n * Resolve container element from selector or element\n */\n private resolveContainer(container: string | HTMLElement): HTMLElement | null {\n if (typeof container === \"string\") {\n return document.querySelector(container);\n }\n return container;\n }\n\n /**\n * Create and mount the iframe\n */\n private createIframe(): void {\n if (!this.container || !this.config) {\n return;\n }\n\n const iframe = document.createElement(\"iframe\");\n\n // Set iframe attributes\n iframe.src = this.buildIframeUrl();\n iframe.style.width = this.config.width || \"100%\";\n iframe.style.height = this.config.height || \"600px\";\n iframe.style.border = \"none\";\n iframe.title = \"Sophi AI Widget\";\n iframe.allow = \"microphone; camera\";\n\n // Append to container\n this.container.appendChild(iframe);\n this.iframe = iframe;\n }\n\n /**\n * Build iframe URL with query parameters\n */\n private buildIframeUrl(): string {\n if (!this.config) {\n return \"\";\n }\n const url = new URL(this.getIframeUrl());\n\n return url.toString();\n }\n\n private getIframeUrl(): string {\n if (!this.config) {\n return \"\";\n }\n if (this.config.environment === \"production\") {\n return \"http://app.usesophi.com\";\n }\n return \"http://10.0.2.127:80\";\n }\n\n /**\n * Setup postMessage listener for iframe communication\n */\n private setupMessageListener(): void {\n this.messageHandler = (event: MessageEvent) => {\n // Validate origin for security\n if (this.iframeOrigin && event.origin !== this.iframeOrigin) {\n console.warn(`Received message from untrusted origin: ${event.origin}`);\n return;\n }\n\n try {\n // Parse message\n const message: IncomingMessage = typeof event.data === \"string\" ? JSON.parse(event.data) : event.data;\n\n // Handle different message types\n this.handleIncomingMessage(message);\n } catch (error) {\n console.error(\"Error processing message from iframe:\", error);\n }\n };\n\n window.addEventListener(\"message\", this.messageHandler);\n }\n\n /**\n * Handle incoming messages from iframe\n */\n private handleIncomingMessage(message: IncomingMessage): void {\n switch (message.type) {\n case \"add_to_cart\":\n if (this.isValidAddToCartMessage(message.data)) {\n this.emit(\"add_to_cart\", message.data);\n } else {\n console.error(\"Invalid add_to_cart message structure:\", message.data);\n this.emitError(new Error(\"Invalid add_to_cart message format\"));\n }\n break;\n\n case \"send_to_checkout\":\n this.emit(\"send_to_checkout\", undefined);\n break;\n case \"ready\":\n this.emit(\"ready\", undefined);\n break;\n\n case \"error\":\n const error = message.data instanceof Error ? message.data : new Error(String(message.data));\n this.emitError(error);\n break;\n\n default:\n console.warn(\"Unknown message type:\", message);\n }\n }\n\n /**\n * Validate add_to_cart message structure\n */\n private isValidAddToCartMessage(data: unknown): data is AddToCartEvent {\n if (!data || typeof data !== \"object\") {\n return false;\n }\n\n const event = data as Record<string, unknown>;\n\n // Check products array exists and is an array\n if (!Array.isArray(event.products)) {\n return false;\n }\n\n // Validate each product in the array\n return event.products.every((product) => {\n if (!product || typeof product !== \"object\") {\n return false;\n }\n const prod = product as Record<string, unknown>;\n\n // productId is required and must be a string\n if (typeof prod.productId !== \"string\") {\n return false;\n }\n\n // variantId is optional, but if present must be a string\n if (prod.variantId !== undefined && typeof prod.variantId !== \"string\") {\n return false;\n }\n\n return true;\n });\n }\n\n /**\n * Post message to iframe\n */\n private postMessage(message: OutgoingMessage): void {\n if (!this.iframe || !this.iframe.contentWindow || !this.iframeOrigin) {\n console.warn(\"Cannot send message: iframe not ready\");\n return;\n }\n\n try {\n console.log(\"Posting message to iframe:\", message.type, \"to origin:\", this.iframeOrigin);\n this.iframe.contentWindow.postMessage(message, this.iframeOrigin);\n console.log(\"Message posted successfully\");\n } catch (error) {\n console.error(\"Error sending message to iframe:\", error);\n this.emitError(error instanceof Error ? error : new Error(String(error)));\n }\n }\n\n /**\n * Emit event to registered listeners\n */\n private emit<K extends EventName>(event: K, data: SophiEventMap[K]): void {\n const handlers = this.eventListeners.get(event);\n if (handlers) {\n handlers.forEach((handler) => {\n try {\n handler(data);\n } catch (error) {\n console.error(`Error in ${event} event handler:`, error);\n }\n });\n }\n }\n\n /**\n * Emit error event\n */\n private emitError(error: Error): void {\n this.emit(\"error\", error);\n\n // Also call onError callback if provided\n if (this.config?.onError) {\n this.config.onError(error);\n }\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/types.ts","../src/services/api.service.ts","../src/constants/config.ts","../src/SophiWidget.ts"],"names":["IncomingMessageTypes","OutgoingMessageTypes","ApiService","baseUrl","apiKey","clientId","request","url","response","errorText","data","error","sessionData","DEFAULT_CONFIG","API_BASE_URLS","API_ENDPOINTS","ERROR_MESSAGES","SophiWidget","config","err","message","cart","guestId","userId","apiService","mergeData","environment","apiBaseUrl","event","handler","handlers","resolve","container","iframe","product","prod"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Configuration for initializing the Sophi Widget\n */\nexport interface SophiConfig {\n /**\n * API key for authentication with Sophi services (x-api-key header)\n * This comes from the parent application\n */\n apiKey: string;\n\n /**\n * Client ID for authentication (x-sophi-client-id header)\n * This comes from the parent application\n */\n clientId: string;\n\n /**\n * Container element selector (e.g., '#widget') or HTMLElement where the widget will be mounted\n */\n container: string | HTMLElement;\n\n /**\n * Optional metadata to send with the session request\n */\n metadata?: Record<string, unknown>;\n\n /**\n * Width of the widget iframe\n * @default '100%'\n */\n width?: string;\n\n /**\n * Height of the widget iframe\n * @default '600px'\n */\n height?: string;\n\n /**\n * Callback fired when the widget is ready\n */\n onReady?: () => void;\n\n /**\n * Callback fired when an error occurs\n */\n onError?: (error: Error) => void;\n\n /**\n * Environment to use for the API\n */\n environment?: \"test\" | \"production\";\n}\n\n/**\n * Product information for add to cart events\n */\nexport interface Product {\n /**\n * Unique product identifier\n */\n productId: string;\n\n /**\n * Optional variant identifier\n */\n variantId?: string;\n}\n\n/**\n * Add to cart event data\n */\nexport interface AddToCartEvent {\n /**\n * List of products to add to cart\n */\n products: Product[];\n}\n\n/**\n * User data that can be sent to the widget\n */\nexport interface UserData {\n /**\n * User ID\n */\n userId?: string;\n\n /**\n * User email\n */\n email?: string;\n\n /**\n * User name\n */\n name?: string;\n\n /**\n * Additional custom data\n */\n [key: string]: unknown;\n}\n\nexport interface IUserCartItem {\n productId: string;\n variantId: string;\n quantity?: number;\n}\n\nexport interface IUserCart {\n cardId: string;\n items: IUserCartItem[];\n}\n\n/**\n * Message types that can be received from the iframe\n */\nexport type IncomingMessageType = \"add_to_cart\" | \"send_to_checkout\" | \"ready\" | \"error\";\n\n/**\n * Incoming message type constants for easy access\n * @example\n * widget.on(IncomingMessageTypes.ADD_TO_CART, (data) => {...})\n */\nexport const IncomingMessageTypes = {\n ADD_TO_CART: \"add_to_cart\",\n READY: \"ready\",\n ERROR: \"error\",\n SEND_TO_CHECKOUT: \"send_to_checkout\",\n} as const;\n\n/**\n * Message types that can be sent to the iframe\n */\nexport type OutgoingMessageType = \"user_data\" | \"config\" | \"update_user_cart\" | \"destroy\" | \"session_data\" | \"client_id_updated\";\n\n/**\n * Outgoing message type constants for easy access\n * @example\n * widget.sendMessage({ type: OutgoingMessageTypes.USER_DATA, data: {...} })\n */\nexport const OutgoingMessageTypes = {\n USER_DATA: \"user_data\",\n CONFIG: \"config\",\n UPDATE_USER_CART: \"update_user_cart\",\n DESTROY: \"destroy\",\n SESSION_DATA: \"session_data\",\n CLIENT_ID_UPDATED: \"client_id_updated\",\n} as const;\n\n/**\n * Incoming message structure from iframe\n */\nexport interface IncomingMessage {\n type: IncomingMessageType;\n data?: unknown;\n}\n\n/**\n * Outgoing message structure to iframe\n */\nexport interface OutgoingMessage {\n type: OutgoingMessageType;\n data?: unknown;\n}\n\n/**\n * Event map for type-safe event handling\n */\nexport interface SophiEventMap {\n /**\n * Fired when products should be added to cart\n */\n add_to_cart: AddToCartEvent;\n\n /**\n * Fired when the widget is ready\n */\n ready: void;\n\n /**\n * Fired when an error occurs\n */\n error: Error;\n\n /**\n * Fired when the user should be sent to checkout\n */\n send_to_checkout: void;\n}\n\n/**\n * Event handler type\n */\nexport type EventHandler<T> = (data: T) => void;\n\n/**\n * Event name type\n */\nexport type EventName = keyof SophiEventMap;\n\n/**\n * Authentication session request payload\n */\nexport interface AuthSessionRequest {\n /**\n * External user ID from the parent application\n */\n externalUserId: string;\n\n /**\n * Optional metadata to include with the session\n */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Session data returned from the authentication API\n */\nexport interface SessionData {\n /**\n * JWT access token for authenticating requests\n */\n accessToken: string;\n\n /**\n * Unique session identifier\n */\n sessionId: string;\n\n /**\n * Client ID\n */\n clientId: string;\n\n /**\n * Company code\n */\n companyCode: string;\n\n /**\n * Session expiration timestamp\n */\n expiresAt: string;\n}\n\n/**\n * Authentication session API response\n */\nexport interface AuthSessionResponse {\n /**\n * Whether the request was successful\n */\n success: boolean;\n\n /**\n * Response message\n */\n message: string;\n\n /**\n * Session data\n */\n data?: SessionData;\n}\n\n/**\n * Merge user request payload\n */\nexport interface MergeUserRequest {\n /**\n * The guest client ID to merge from\n */\n guestId: string;\n\n /**\n * The logged-in user ID to merge to\n */\n userId: string;\n}\n\n/**\n * Merge user data returned from the API\n */\nexport interface MergeUserData {\n /**\n * The resulting client ID after merge\n */\n clientId: string;\n\n /**\n * The external user ID\n */\n externalUserId: string;\n}\n\n/**\n * Merge user API response\n */\nexport interface MergeUserResponse {\n /**\n * Whether the request was successful\n */\n success: boolean;\n\n /**\n * Response message\n */\n message: string;\n\n /**\n * Merge result data\n */\n data?: MergeUserData;\n}\n","import type { AuthSessionRequest, AuthSessionResponse, SessionData, MergeUserRequest, MergeUserResponse, MergeUserData } from \"../types\";\n\n/**\n * API Service for Sophi authentication and session management\n */\nexport class ApiService {\n private baseUrl: string;\n private apiKey: string;\n private clientId: string;\n\n constructor(baseUrl: string, apiKey: string, clientId: string) {\n this.baseUrl = baseUrl;\n this.apiKey = apiKey;\n this.clientId = clientId;\n }\n\n /**\n * Create a client session by calling the authentication endpoint\n * @param request - Session request with externalUserId and optional metadata\n * @returns Session data including accessToken, sessionId, etc.\n */\n async createSession(request: AuthSessionRequest): Promise<SessionData> {\n const url = `${this.baseUrl}/api/v1/auth/client/session`;\n\n try {\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\",\n \"x-api-key\": this.apiKey,\n \"x-sophi-client-id\": this.clientId,\n },\n body: JSON.stringify(request),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Failed to create session: ${response.status} ${response.statusText}. ${errorText}`\n );\n }\n\n const data: AuthSessionResponse = await response.json();\n\n if (!data.success) {\n throw new Error(`Session creation failed: ${data.message || \"Unknown error\"}`);\n }\n\n if (!data.data) {\n throw new Error(\"Session data is missing from response\");\n }\n\n return data.data;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(`Unexpected error during session creation: ${String(error)}`);\n }\n }\n\n /**\n * Validate session data\n */\n validateSessionData(sessionData: SessionData): boolean {\n return !!(\n sessionData.accessToken &&\n sessionData.sessionId &&\n sessionData.clientId &&\n sessionData.companyCode &&\n sessionData.expiresAt\n );\n }\n\n /**\n * Merge guest user with logged-in user\n * @param request - Merge request with guestId and userId\n * @returns Merge result data including the resulting clientId\n */\n async mergeUser(request: MergeUserRequest): Promise<MergeUserData> {\n const url = `${this.baseUrl}/api/v1/auth/client/info`;\n\n try {\n const response = await fetch(url, {\n method: \"PATCH\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\",\n \"x-api-key\": this.apiKey,\n \"x-sophi-client-id\": request.guestId,\n },\n body: JSON.stringify({\n externalUserId: request.userId,\n }),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Failed to merge user: ${response.status} ${response.statusText}. ${errorText}`\n );\n }\n\n const data: MergeUserResponse = await response.json();\n\n if (!data.success) {\n throw new Error(`User merge failed: ${data.message || \"Unknown error\"}`);\n }\n\n if (!data.data) {\n throw new Error(\"Merge data is missing from response\");\n }\n\n return data.data;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(`Unexpected error during user merge: ${String(error)}`);\n }\n }\n}\n","/**\n * Default configuration constants for Sophi Widget\n */\nexport const DEFAULT_CONFIG = {\n /**\n * Default width of the widget iframe\n */\n WIDTH: \"100%\",\n\n /**\n * Default height of the widget iframe\n */\n HEIGHT: \"600px\",\n} as const;\n\n/**\n * API base URLs for different environments\n *\n * Note: These URLs will be visible in the client-side bundle.\n * This is normal and safe - the real security is in your API key validation.\n *\n * To use different URLs, modify these values before building the SDK.\n *\n * Production API URL - update before building for production\n */\nexport const API_BASE_URLS = {\n test: \"http://10.0.2.127:80\",\n production: \"https://api.usesophi.com\",\n} as const;\n\n/**\n * API endpoints configuration\n */\nexport const API_ENDPOINTS = {\n /**\n * Session creation endpoint path\n */\n CREATE_SESSION: \"/api/v1/auth/client/session\",\n} as const;\n\n/**\n * Error messages\n */\nexport const ERROR_MESSAGES = {\n MISSING_API_KEY: \"API key is required and must be a string\",\n MISSING_CLIENT_ID: \"Client ID is required and must be a string\",\n MISSING_USER_ID: \"User ID is required and must be a string\",\n MISSING_CONTAINER: \"Container is required\",\n MISSING_IFRAME_URL: \"Iframe URL is required and must be a string\",\n INVALID_IFRAME_URL: \"Invalid iframe URL\",\n INVALID_ENVIRONMENT: \"Invalid environment. Must be 'dev', 'test', 'staging', or 'production'\",\n CONTAINER_NOT_FOUND: \"Container element not found\",\n NOT_INITIALIZED: \"Widget not initialized. Call init() first.\",\n ALREADY_INITIALIZED: \"Sophi Widget is already initialized. Call destroy() first to reinitialize.\",\n SESSION_CREATION_FAILED: \"Failed to create authentication session\",\n INVALID_SESSION_DATA: \"Invalid session data received from API\",\n YOU_NEED_TO_INIT_THE_WIDGET_FIRST: \"You need to initialize the widget first. Call init() before calling this method.\",\n} as const;\n","import { type SophiConfig, type UserData, type EventHandler, type EventName, type SophiEventMap, type IncomingMessage, type OutgoingMessage, type AddToCartEvent, type IUserCart, type SessionData, type MergeUserRequest, type MergeUserData, OutgoingMessageTypes } from \"./types\";\nimport { ApiService } from \"./services/api.service\";\nimport { DEFAULT_CONFIG, ERROR_MESSAGES, API_BASE_URLS } from \"./constants/config\";\n\n/**\n * Main Sophi Widget SDK class\n * Provides a framework-agnostic way to embed and interact with the Sophi AI widget\n */\nexport class SophiWidget {\n private config: SophiConfig | null = null;\n private iframe: HTMLIFrameElement | null = null;\n private container: HTMLElement | null = null;\n private eventListeners: Map<EventName, Set<EventHandler<any>>> = new Map();\n private messageHandler: ((event: MessageEvent) => void) | null = null;\n private isInitialized = false;\n private iframeOrigin: string | null = null;\n private sessionData: SessionData | null = null;\n private apiService: ApiService | null = null;\n private apiBaseUrl: string | null = null;\n\n /**\n * Initialize the widget with configuration\n * @param config - Widget configuration\n */\n public async init(config: SophiConfig): Promise<void> {\n if (this.isInitialized) {\n console.warn(ERROR_MESSAGES.ALREADY_INITIALIZED);\n return;\n }\n\n try {\n // Validate configuration\n this.validateConfig(config);\n\n this.config = config;\n\n // Determine API URL based on environment\n this.apiBaseUrl = API_BASE_URLS[config.environment || \"production\"];\n\n // Initialize API service\n this.apiService = new ApiService(this.apiBaseUrl, config.apiKey, config.clientId);\n\n // Create authentication session\n await this.createAuthSession();\n\n this.isInitialized = true;\n\n // Extract origin from iframe URL for security\n try {\n const url = new URL(this.getIframeUrl());\n this.iframeOrigin = url.origin;\n } catch (error) {\n const err = new Error(`${ERROR_MESSAGES.INVALID_IFRAME_URL}: ${this.getIframeUrl()}`);\n this.emitError(err);\n throw err;\n }\n\n // Resolve container element\n this.container = this.resolveContainer(config.container);\n\n if (!this.container) {\n const err = new Error(ERROR_MESSAGES.CONTAINER_NOT_FOUND);\n this.emitError(err);\n throw err;\n }\n\n // Create and mount iframe\n this.createIframe();\n\n // Setup postMessage listener\n this.setupMessageListener();\n\n // Send session data to iframe after it's loaded\n this.iframe?.addEventListener(\"load\", () => {\n console.log(\"Iframe loaded, sending session data...\");\n // Small delay to ensure iframe is fully ready\n setTimeout(() => {\n this.sendSessionData();\n }, 100);\n });\n\n // Call onReady callback if provided\n if (config.onReady) {\n config.onReady();\n }\n } catch (error) {\n // Reset state on error\n this.isInitialized = false;\n this.config = null;\n this.apiService = null;\n this.sessionData = null;\n\n const err = error instanceof Error ? error : new Error(String(error));\n this.emitError(err);\n throw err;\n }\n }\n\n /**\n * Send user data to the widget\n * @param data - User data object\n */\n public sendUserData(data: UserData): void {\n if (!this.isInitialized || !this.iframe) {\n console.warn(\"Widget not initialized. Call init() first.\");\n return;\n }\n\n const message: OutgoingMessage = {\n type: OutgoingMessageTypes.USER_DATA,\n data,\n };\n\n this.postMessage(message);\n }\n\n public updateUserCart(cart: IUserCart): void {\n if (!this.isInitialized || !this.iframe) {\n console.warn(\"Widget not initialized. Call init() first.\");\n return;\n }\n\n const message: OutgoingMessage = {\n type: OutgoingMessageTypes.UPDATE_USER_CART,\n data: cart,\n };\n\n this.postMessage(message);\n }\n\n /**\n * Merge guest user with logged-in user\n * This can be called anytime, even before widget initialization\n * @param guestId - The guest client ID (previous clientId)\n * @param userId - The logged-in user ID (new externalUserId)\n * @param apiKey - API key for authentication (required if widget not initialized)\n * @returns Promise with merge result data\n */\n public async mergeUser(guestId: string, userId: string, apiKey?: string): Promise<MergeUserData> {\n try {\n let apiService: ApiService;\n if (!this.apiBaseUrl) {\n throw new Error(ERROR_MESSAGES.YOU_NEED_TO_INIT_THE_WIDGET_FIRST);\n }\n // Use existing API service if widget is initialized, otherwise create a temporary one\n if (this.apiService) {\n apiService = this.apiService;\n } else {\n // Widget not initialized - create temporary API service\n if (!apiKey) {\n throw new Error(\"API key is required when widget is not initialized\");\n }\n apiService = new ApiService(this.apiBaseUrl, apiKey, guestId);\n }\n\n const mergeData = await apiService.mergeUser({ guestId, userId });\n\n // If iframe exists, send the updated clientId to it\n // This allows the iframe to update its own stores automatically\n if (this.isInitialized && this.iframe) {\n const message: OutgoingMessage = {\n type: OutgoingMessageTypes.CLIENT_ID_UPDATED,\n data: {\n clientId: mergeData.clientId,\n externalUserId: mergeData.externalUserId,\n },\n };\n this.postMessage(message);\n }\n\n return mergeData;\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n // Only emit error if widget is initialized\n if (this.isInitialized) {\n this.emitError(err);\n }\n throw err;\n }\n }\n\n /**\n * Static method to merge guest user with logged-in user\n * Use this when you don't have a widget instance or want to merge before initialization\n * @param guestId - The guest client ID (previous clientId)\n * @param userId - The logged-in user ID (new externalUserId)\n * @param apiKey - API key for authentication\n * @returns Promise with merge result data\n */\n public static async mergeUser(\n guestId: string,\n userId: string,\n apiKey: string,\n {\n environment = \"test\",\n }: {\n environment?: SophiConfig[\"environment\"];\n } = {}\n ): Promise<MergeUserData> {\n const apiBaseUrl = API_BASE_URLS[environment];\n const apiService = new ApiService(apiBaseUrl, apiKey, guestId);\n return await apiService.mergeUser({ guestId, userId });\n }\n\n private onDestroy(): void {\n if (!this.isInitialized || !this.iframe) {\n console.warn(\"Widget not initialized. Call init() first.\");\n return;\n }\n\n const message: OutgoingMessage = {\n type: OutgoingMessageTypes.DESTROY,\n };\n\n this.postMessage(message);\n }\n\n /**\n * Register an event listener\n * @param event - Event name\n * @param handler - Event handler function\n */\n public on<K extends EventName>(event: K, handler: EventHandler<SophiEventMap[K]>): void {\n if (!this.eventListeners.has(event)) {\n this.eventListeners.set(event, new Set());\n }\n this.eventListeners.get(event)!.add(handler);\n }\n\n /**\n * Unregister an event listener\n * @param event - Event name\n * @param handler - Event handler function to remove\n */\n public off<K extends EventName>(event: K, handler: EventHandler<SophiEventMap[K]>): void {\n const handlers = this.eventListeners.get(event);\n if (handlers) {\n handlers.delete(handler);\n }\n }\n\n /**\n * Show the widget\n */\n public show(): void {\n if (this.iframe) {\n this.iframe.style.display = \"block\";\n }\n }\n\n /**\n * Hide the widget\n */\n public hide(): void {\n if (this.iframe) {\n this.iframe.style.display = \"none\";\n }\n }\n\n /**\n * Destroy the widget and cleanup resources\n * Waits 300ms after sending destroy message to give iframe time to cleanup\n */\n public async destroy(): Promise<void> {\n this.onDestroy();\n\n // Wait 300ms to give iframe time to process the destroy event and cleanup\n await new Promise((resolve) => setTimeout(resolve, 300));\n\n // Remove message listener\n if (this.messageHandler) {\n window.removeEventListener(\"message\", this.messageHandler);\n this.messageHandler = null;\n }\n\n // Remove iframe\n if (this.iframe && this.iframe.parentNode) {\n this.iframe.parentNode.removeChild(this.iframe);\n this.iframe = null;\n }\n\n // Clear event listeners\n this.eventListeners.clear();\n\n // Reset state\n this.config = null;\n this.container = null;\n this.isInitialized = false;\n this.iframeOrigin = null;\n this.sessionData = null;\n this.apiService = null;\n }\n\n /**\n * Check if widget is initialized\n */\n public isReady(): boolean {\n return this.isInitialized && this.iframe !== null;\n }\n\n /**\n * Validate configuration\n */\n private validateConfig(config: SophiConfig): void {\n if (!config.apiKey || typeof config.apiKey !== \"string\") {\n throw new Error(ERROR_MESSAGES.MISSING_API_KEY);\n }\n\n if (!config.clientId || typeof config.clientId !== \"string\") {\n throw new Error(ERROR_MESSAGES.MISSING_CLIENT_ID);\n }\n\n if (!config.clientId || typeof config.clientId !== \"string\") {\n throw new Error(ERROR_MESSAGES.MISSING_USER_ID);\n }\n\n if (!config.container) {\n throw new Error(ERROR_MESSAGES.MISSING_CONTAINER);\n }\n }\n\n /**\n * Create authentication session with the API\n */\n private async createAuthSession(): Promise<void> {\n if (!this.config || !this.apiService) {\n throw new Error(ERROR_MESSAGES.NOT_INITIALIZED);\n }\n\n try {\n this.sessionData = await this.apiService.createSession({\n externalUserId: this.config.clientId,\n metadata: this.config.metadata,\n });\n\n // Validate session data\n if (!this.apiService.validateSessionData(this.sessionData)) {\n throw new Error(ERROR_MESSAGES.INVALID_SESSION_DATA);\n }\n } catch (error) {\n const err = error instanceof Error ? error : new Error(ERROR_MESSAGES.SESSION_CREATION_FAILED);\n throw err;\n }\n }\n\n /**\n * Send session data to iframe\n */\n private sendSessionData(): void {\n if (!this.sessionData) {\n console.warn(\"Session data not available\");\n return;\n }\n\n const message: OutgoingMessage = {\n type: OutgoingMessageTypes.SESSION_DATA,\n data: this.sessionData,\n };\n\n this.postMessage(message);\n }\n\n /**\n * Resolve container element from selector or element\n */\n private resolveContainer(container: string | HTMLElement): HTMLElement | null {\n if (typeof container === \"string\") {\n return document.querySelector(container);\n }\n return container;\n }\n\n /**\n * Create and mount the iframe\n */\n private createIframe(): void {\n if (!this.container || !this.config) {\n return;\n }\n\n const iframe = document.createElement(\"iframe\");\n\n // Set iframe attributes\n iframe.src = this.buildIframeUrl();\n iframe.style.width = this.config.width || \"100%\";\n iframe.style.height = this.config.height || \"600px\";\n iframe.style.border = \"none\";\n iframe.title = \"Sophi AI Widget\";\n iframe.allow = \"microphone; camera\";\n\n // Append to container\n this.container.appendChild(iframe);\n this.iframe = iframe;\n }\n\n /**\n * Build iframe URL with query parameters\n */\n private buildIframeUrl(): string {\n if (!this.config) {\n return \"\";\n }\n const url = new URL(this.getIframeUrl());\n\n return url.toString();\n }\n\n private getIframeUrl(): string {\n if (!this.config) {\n return \"\";\n }\n if (this.config.environment === \"production\") {\n return \"http://app.usesophi.com\";\n }\n return \"http://10.0.2.127:80\";\n }\n\n /**\n * Setup postMessage listener for iframe communication\n */\n private setupMessageListener(): void {\n this.messageHandler = (event: MessageEvent) => {\n // Validate origin for security\n if (this.iframeOrigin && event.origin !== this.iframeOrigin) {\n console.warn(`Received message from untrusted origin: ${event.origin}`);\n return;\n }\n\n try {\n // Parse message\n const message: IncomingMessage = typeof event.data === \"string\" ? JSON.parse(event.data) : event.data;\n\n // Handle different message types\n this.handleIncomingMessage(message);\n } catch (error) {\n console.error(\"Error processing message from iframe:\", error);\n }\n };\n\n window.addEventListener(\"message\", this.messageHandler);\n }\n\n /**\n * Handle incoming messages from iframe\n */\n private handleIncomingMessage(message: IncomingMessage): void {\n switch (message.type) {\n case \"add_to_cart\":\n if (this.isValidAddToCartMessage(message.data)) {\n this.emit(\"add_to_cart\", message.data);\n } else {\n console.error(\"Invalid add_to_cart message structure:\", message.data);\n this.emitError(new Error(\"Invalid add_to_cart message format\"));\n }\n break;\n\n case \"send_to_checkout\":\n this.emit(\"send_to_checkout\", undefined);\n break;\n case \"ready\":\n this.emit(\"ready\", undefined);\n break;\n\n case \"error\":\n const error = message.data instanceof Error ? message.data : new Error(String(message.data));\n this.emitError(error);\n break;\n\n default:\n console.warn(\"Unknown message type:\", message);\n }\n }\n\n /**\n * Validate add_to_cart message structure\n */\n private isValidAddToCartMessage(data: unknown): data is AddToCartEvent {\n if (!data || typeof data !== \"object\") {\n return false;\n }\n\n const event = data as Record<string, unknown>;\n\n // Check products array exists and is an array\n if (!Array.isArray(event.products)) {\n return false;\n }\n\n // Validate each product in the array\n return event.products.every((product) => {\n if (!product || typeof product !== \"object\") {\n return false;\n }\n const prod = product as Record<string, unknown>;\n\n // productId is required and must be a string\n if (typeof prod.productId !== \"string\") {\n return false;\n }\n\n // variantId is optional, but if present must be a string\n if (prod.variantId !== undefined && typeof prod.variantId !== \"string\") {\n return false;\n }\n\n return true;\n });\n }\n\n /**\n * Post message to iframe\n */\n private postMessage(message: OutgoingMessage): void {\n if (!this.iframe || !this.iframe.contentWindow || !this.iframeOrigin) {\n console.warn(\"Cannot send message: iframe not ready\");\n return;\n }\n\n try {\n console.log(\"Posting message to iframe:\", message.type, \"to origin:\", this.iframeOrigin);\n this.iframe.contentWindow.postMessage(message, this.iframeOrigin);\n console.log(\"Message posted successfully\");\n } catch (error) {\n console.error(\"Error sending message to iframe:\", error);\n this.emitError(error instanceof Error ? error : new Error(String(error)));\n }\n }\n\n /**\n * Emit event to registered listeners\n */\n private emit<K extends EventName>(event: K, data: SophiEventMap[K]): void {\n const handlers = this.eventListeners.get(event);\n if (handlers) {\n handlers.forEach((handler) => {\n try {\n handler(data);\n } catch (error) {\n console.error(`Error in ${event} event handler:`, error);\n }\n });\n }\n }\n\n /**\n * Emit error event\n */\n private emitError(error: Error): void {\n this.emit(\"error\", error);\n\n // Also call onError callback if provided\n if (this.config?.onError) {\n this.config.onError(error);\n }\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usesophi/sophi-web-sdk",
3
- "version": "0.1.0-beta.3",
3
+ "version": "0.1.0-beta.4",
4
4
  "description": "Framework-agnostic SDK for embedding Sophi AI widget",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",