@usesophi/sophi-web-sdk 1.3.3 → 1.3.5-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +77 -3
- package/dist/index.d.ts +77 -3
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +2 -2
- package/dist/index.umd.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -22,6 +22,14 @@ interface SessionDataChatPayload {
|
|
|
22
22
|
interface SetLocalePayload {
|
|
23
23
|
locale: SophiLocale;
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Payload for `set_consent` — tell the iframe to switch its persistence at
|
|
27
|
+
* runtime. `storage: true` promotes the in-memory store to localStorage;
|
|
28
|
+
* `storage: false` demotes back to memory and clears persisted keys.
|
|
29
|
+
*/
|
|
30
|
+
interface SetConsentPayload {
|
|
31
|
+
storage: boolean;
|
|
32
|
+
}
|
|
25
33
|
/**
|
|
26
34
|
* Layout options sent to the iframe on init so the child app can adjust its layout.
|
|
27
35
|
*/
|
|
@@ -85,6 +93,14 @@ interface SophiConfig {
|
|
|
85
93
|
* Environment to use for the API
|
|
86
94
|
*/
|
|
87
95
|
environment?: "test" | "production";
|
|
96
|
+
/**
|
|
97
|
+
* Whether the SDK may persist session data to cookies. Defaults to `true`.
|
|
98
|
+
* Set to `false` to run in anonymous mode: no cookies are written or read,
|
|
99
|
+
* the session lives only in memory, and the user starts fresh on every load.
|
|
100
|
+
* Use {@link SophiWidget.setStorageConsent} to switch to persistent mode at
|
|
101
|
+
* runtime once the user grants cookie consent.
|
|
102
|
+
*/
|
|
103
|
+
storageConsent?: boolean;
|
|
88
104
|
/**
|
|
89
105
|
* Layout options sent to the iframe on init. The child app receives these via a
|
|
90
106
|
* postMessage and can show/hide header (and future layout options) accordingly.
|
|
@@ -197,7 +213,7 @@ declare const IncomingMessageTypes: {
|
|
|
197
213
|
/**
|
|
198
214
|
* Message types that can be sent to the iframe
|
|
199
215
|
*/
|
|
200
|
-
type OutgoingMessageType = "user_data" | "config" | "update_user_cart" | "destroy" | "session_data" | "set_locale" | "client_id_updated" | "layout_config" | "toggle_history" | "send_text_message" | "send_product_reply" | "send_outfit_reply" | "open_product_by_id" | "trigger_product_reply" | "update_favorite";
|
|
216
|
+
type OutgoingMessageType = "user_data" | "config" | "update_user_cart" | "destroy" | "session_data" | "set_locale" | "set_consent" | "client_id_updated" | "layout_config" | "toggle_history" | "send_text_message" | "send_product_reply" | "send_outfit_reply" | "open_product_by_id" | "trigger_product_reply" | "update_favorite";
|
|
201
217
|
/**
|
|
202
218
|
* Outgoing message type constants for easy access
|
|
203
219
|
* @example
|
|
@@ -210,6 +226,7 @@ declare const OutgoingMessageTypes: {
|
|
|
210
226
|
readonly DESTROY: "destroy";
|
|
211
227
|
readonly SESSION_DATA: "session_data";
|
|
212
228
|
readonly SET_LOCALE: "set_locale";
|
|
229
|
+
readonly SET_CONSENT: "set_consent";
|
|
213
230
|
readonly CLIENT_ID_UPDATED: "client_id_updated";
|
|
214
231
|
readonly LAYOUT_CONFIG: "layout_config";
|
|
215
232
|
readonly TOGGLE_HISTORY: "toggle_history";
|
|
@@ -568,6 +585,25 @@ declare class SophiWidget {
|
|
|
568
585
|
* Use after host-side session refresh or if the child app missed the initial payload.
|
|
569
586
|
*/
|
|
570
587
|
sendSessionToChat(): void;
|
|
588
|
+
/**
|
|
589
|
+
* Update storage (cookie) consent at runtime without losing the open chat.
|
|
590
|
+
*
|
|
591
|
+
* When granting consent, the current in-memory session is persisted to
|
|
592
|
+
* cookies. For a logged-in user (a userId was supplied at init), the guest
|
|
593
|
+
* session is merged into that user via `/auth/v1/client/update` and a fresh
|
|
594
|
+
* session is opened for the user so the SDK receives their auth data; the new
|
|
595
|
+
* session data is then re-posted to the iframe. The chat iframe is also told
|
|
596
|
+
* to promote its in-memory store to localStorage so the conversation survives
|
|
597
|
+
* the next reload. Withdrawing consent clears cookies and demotes the iframe
|
|
598
|
+
* back to memory-only.
|
|
599
|
+
*
|
|
600
|
+
* @param granted - `true` to enable persistence, `false` to withdraw it.
|
|
601
|
+
*/
|
|
602
|
+
setStorageConsent(granted: boolean): Promise<void>;
|
|
603
|
+
/**
|
|
604
|
+
* Tell the chat iframe to switch its persistence strategy at runtime.
|
|
605
|
+
*/
|
|
606
|
+
private postSetConsent;
|
|
571
607
|
/**
|
|
572
608
|
* Posts `set_locale` so the iframe can switch language without a full `session_data` round trip.
|
|
573
609
|
*
|
|
@@ -672,7 +708,23 @@ declare class ApiService {
|
|
|
672
708
|
private userId;
|
|
673
709
|
private storedUserId;
|
|
674
710
|
private cookieManager;
|
|
675
|
-
|
|
711
|
+
/**
|
|
712
|
+
* Whether the SDK is allowed to read/write cookies. When false the SDK runs
|
|
713
|
+
* in anonymous mode: the session lives only in memory and no cookies are
|
|
714
|
+
* touched, so the user starts fresh on every page load.
|
|
715
|
+
*/
|
|
716
|
+
private storageConsent;
|
|
717
|
+
/**
|
|
718
|
+
* In-memory access token. Always retained (even in anonymous mode where no
|
|
719
|
+
* cookie exists) so a merge can run when storage consent is granted later.
|
|
720
|
+
*/
|
|
721
|
+
private accessToken;
|
|
722
|
+
/**
|
|
723
|
+
* Host-provided userId held back while anonymous. Used to merge the guest
|
|
724
|
+
* session into the logged-in user once storage consent is granted.
|
|
725
|
+
*/
|
|
726
|
+
private pendingUserId;
|
|
727
|
+
constructor(baseUrl: string, apiKey: string, userId?: string, storageConsent?: boolean);
|
|
676
728
|
/**
|
|
677
729
|
* Create a client session by calling the authentication endpoint
|
|
678
730
|
* @param request - Session request with optional externalUserId and metadata
|
|
@@ -689,6 +741,28 @@ declare class ApiService {
|
|
|
689
741
|
* @returns Merge result data including the resulting clientId
|
|
690
742
|
*/
|
|
691
743
|
mergeUser(request: MergeUserRequest): Promise<MergeUserData>;
|
|
744
|
+
/**
|
|
745
|
+
* Grant storage consent at runtime (e.g. the user accepted cookies while the
|
|
746
|
+
* chat was open).
|
|
747
|
+
*
|
|
748
|
+
* - Guest: persists the existing in-memory anonymous session to cookies so it
|
|
749
|
+
* simply becomes persistent (same session continues).
|
|
750
|
+
* - Logged-in user: merges the anonymous guest session into the user AND opens
|
|
751
|
+
* a fresh session for that user (via {@link createSession}) so we receive the
|
|
752
|
+
* user's auth data (accessToken, sessionId, clientId, layout, ...) from the
|
|
753
|
+
* API — the same merge-then-new-session flow used on a normal login.
|
|
754
|
+
*
|
|
755
|
+
* @param request - Optional metadata for the new session (logged-in case).
|
|
756
|
+
* @returns the newly created {@link SessionData} when a logged-in session was
|
|
757
|
+
* opened, or null when nothing new was created (guest, or no active
|
|
758
|
+
* in-memory session yet).
|
|
759
|
+
*/
|
|
760
|
+
enableStorageConsent(request?: AuthSessionRequest): Promise<SessionData | null>;
|
|
761
|
+
/**
|
|
762
|
+
* Withdraw storage consent at runtime. Clears all cookies but keeps the
|
|
763
|
+
* in-memory session so the current chat keeps working until the page is left.
|
|
764
|
+
*/
|
|
765
|
+
disableStorageConsent(): void;
|
|
692
766
|
private persistClientInfo;
|
|
693
767
|
private restoreSessionFromCookies;
|
|
694
768
|
private clearStoredClientInfo;
|
|
@@ -743,4 +817,4 @@ declare const ERROR_MESSAGES: {
|
|
|
743
817
|
readonly YOU_NEED_TO_INIT_THE_WIDGET_FIRST: "You need to initialize the widget first. Call init() before calling this method.";
|
|
744
818
|
};
|
|
745
819
|
|
|
746
|
-
export { API_BASE_URLS, API_ENDPOINTS, type AddToCartEvent, type AddToFavoriteEvent, type AddToFavoritePayload, ApiService, type AuthSessionRequest, type AuthSessionResponse, DEFAULT_CONFIG, ERROR_MESSAGES, type EventHandler, type EventName, type FavoriteProduct, type IUserCart, type IUserCartItem, type IncomingMessage, type IncomingMessageType, IncomingMessageTypes, type LayoutConfig, type MergeUserData, type MergeUserRequest, type MergeUserResponse, type OpenProductByIdPayload, type OutfitItem, type OutfitResponse, type OutgoingMessage, type OutgoingMessageType, OutgoingMessageTypes, type Product, type ProductImage, type ProductOffer, type ProductReplyOptions, type ProductSummary, type SendOutfitReplyPayload, type SendProductReplyPayload, type SendTextMessagePayload, type SessionData, type SessionDataChatPayload, type SetLocalePayload, type SophiConfig, type SophiEventMap, type SophiLocale, SophiWidget, type TriggerProductReplyPayload, type UserData };
|
|
820
|
+
export { API_BASE_URLS, API_ENDPOINTS, type AddToCartEvent, type AddToFavoriteEvent, type AddToFavoritePayload, ApiService, type AuthSessionRequest, type AuthSessionResponse, DEFAULT_CONFIG, ERROR_MESSAGES, type EventHandler, type EventName, type FavoriteProduct, type IUserCart, type IUserCartItem, type IncomingMessage, type IncomingMessageType, IncomingMessageTypes, type LayoutConfig, type MergeUserData, type MergeUserRequest, type MergeUserResponse, type OpenProductByIdPayload, type OutfitItem, type OutfitResponse, type OutgoingMessage, type OutgoingMessageType, OutgoingMessageTypes, type Product, type ProductImage, type ProductOffer, type ProductReplyOptions, type ProductSummary, type SendOutfitReplyPayload, type SendProductReplyPayload, type SendTextMessagePayload, type SessionData, type SessionDataChatPayload, type SetConsentPayload, type SetLocalePayload, type SophiConfig, type SophiEventMap, type SophiLocale, SophiWidget, type TriggerProductReplyPayload, type UserData };
|
package/dist/index.d.ts
CHANGED
|
@@ -22,6 +22,14 @@ interface SessionDataChatPayload {
|
|
|
22
22
|
interface SetLocalePayload {
|
|
23
23
|
locale: SophiLocale;
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Payload for `set_consent` — tell the iframe to switch its persistence at
|
|
27
|
+
* runtime. `storage: true` promotes the in-memory store to localStorage;
|
|
28
|
+
* `storage: false` demotes back to memory and clears persisted keys.
|
|
29
|
+
*/
|
|
30
|
+
interface SetConsentPayload {
|
|
31
|
+
storage: boolean;
|
|
32
|
+
}
|
|
25
33
|
/**
|
|
26
34
|
* Layout options sent to the iframe on init so the child app can adjust its layout.
|
|
27
35
|
*/
|
|
@@ -85,6 +93,14 @@ interface SophiConfig {
|
|
|
85
93
|
* Environment to use for the API
|
|
86
94
|
*/
|
|
87
95
|
environment?: "test" | "production";
|
|
96
|
+
/**
|
|
97
|
+
* Whether the SDK may persist session data to cookies. Defaults to `true`.
|
|
98
|
+
* Set to `false` to run in anonymous mode: no cookies are written or read,
|
|
99
|
+
* the session lives only in memory, and the user starts fresh on every load.
|
|
100
|
+
* Use {@link SophiWidget.setStorageConsent} to switch to persistent mode at
|
|
101
|
+
* runtime once the user grants cookie consent.
|
|
102
|
+
*/
|
|
103
|
+
storageConsent?: boolean;
|
|
88
104
|
/**
|
|
89
105
|
* Layout options sent to the iframe on init. The child app receives these via a
|
|
90
106
|
* postMessage and can show/hide header (and future layout options) accordingly.
|
|
@@ -197,7 +213,7 @@ declare const IncomingMessageTypes: {
|
|
|
197
213
|
/**
|
|
198
214
|
* Message types that can be sent to the iframe
|
|
199
215
|
*/
|
|
200
|
-
type OutgoingMessageType = "user_data" | "config" | "update_user_cart" | "destroy" | "session_data" | "set_locale" | "client_id_updated" | "layout_config" | "toggle_history" | "send_text_message" | "send_product_reply" | "send_outfit_reply" | "open_product_by_id" | "trigger_product_reply" | "update_favorite";
|
|
216
|
+
type OutgoingMessageType = "user_data" | "config" | "update_user_cart" | "destroy" | "session_data" | "set_locale" | "set_consent" | "client_id_updated" | "layout_config" | "toggle_history" | "send_text_message" | "send_product_reply" | "send_outfit_reply" | "open_product_by_id" | "trigger_product_reply" | "update_favorite";
|
|
201
217
|
/**
|
|
202
218
|
* Outgoing message type constants for easy access
|
|
203
219
|
* @example
|
|
@@ -210,6 +226,7 @@ declare const OutgoingMessageTypes: {
|
|
|
210
226
|
readonly DESTROY: "destroy";
|
|
211
227
|
readonly SESSION_DATA: "session_data";
|
|
212
228
|
readonly SET_LOCALE: "set_locale";
|
|
229
|
+
readonly SET_CONSENT: "set_consent";
|
|
213
230
|
readonly CLIENT_ID_UPDATED: "client_id_updated";
|
|
214
231
|
readonly LAYOUT_CONFIG: "layout_config";
|
|
215
232
|
readonly TOGGLE_HISTORY: "toggle_history";
|
|
@@ -568,6 +585,25 @@ declare class SophiWidget {
|
|
|
568
585
|
* Use after host-side session refresh or if the child app missed the initial payload.
|
|
569
586
|
*/
|
|
570
587
|
sendSessionToChat(): void;
|
|
588
|
+
/**
|
|
589
|
+
* Update storage (cookie) consent at runtime without losing the open chat.
|
|
590
|
+
*
|
|
591
|
+
* When granting consent, the current in-memory session is persisted to
|
|
592
|
+
* cookies. For a logged-in user (a userId was supplied at init), the guest
|
|
593
|
+
* session is merged into that user via `/auth/v1/client/update` and a fresh
|
|
594
|
+
* session is opened for the user so the SDK receives their auth data; the new
|
|
595
|
+
* session data is then re-posted to the iframe. The chat iframe is also told
|
|
596
|
+
* to promote its in-memory store to localStorage so the conversation survives
|
|
597
|
+
* the next reload. Withdrawing consent clears cookies and demotes the iframe
|
|
598
|
+
* back to memory-only.
|
|
599
|
+
*
|
|
600
|
+
* @param granted - `true` to enable persistence, `false` to withdraw it.
|
|
601
|
+
*/
|
|
602
|
+
setStorageConsent(granted: boolean): Promise<void>;
|
|
603
|
+
/**
|
|
604
|
+
* Tell the chat iframe to switch its persistence strategy at runtime.
|
|
605
|
+
*/
|
|
606
|
+
private postSetConsent;
|
|
571
607
|
/**
|
|
572
608
|
* Posts `set_locale` so the iframe can switch language without a full `session_data` round trip.
|
|
573
609
|
*
|
|
@@ -672,7 +708,23 @@ declare class ApiService {
|
|
|
672
708
|
private userId;
|
|
673
709
|
private storedUserId;
|
|
674
710
|
private cookieManager;
|
|
675
|
-
|
|
711
|
+
/**
|
|
712
|
+
* Whether the SDK is allowed to read/write cookies. When false the SDK runs
|
|
713
|
+
* in anonymous mode: the session lives only in memory and no cookies are
|
|
714
|
+
* touched, so the user starts fresh on every page load.
|
|
715
|
+
*/
|
|
716
|
+
private storageConsent;
|
|
717
|
+
/**
|
|
718
|
+
* In-memory access token. Always retained (even in anonymous mode where no
|
|
719
|
+
* cookie exists) so a merge can run when storage consent is granted later.
|
|
720
|
+
*/
|
|
721
|
+
private accessToken;
|
|
722
|
+
/**
|
|
723
|
+
* Host-provided userId held back while anonymous. Used to merge the guest
|
|
724
|
+
* session into the logged-in user once storage consent is granted.
|
|
725
|
+
*/
|
|
726
|
+
private pendingUserId;
|
|
727
|
+
constructor(baseUrl: string, apiKey: string, userId?: string, storageConsent?: boolean);
|
|
676
728
|
/**
|
|
677
729
|
* Create a client session by calling the authentication endpoint
|
|
678
730
|
* @param request - Session request with optional externalUserId and metadata
|
|
@@ -689,6 +741,28 @@ declare class ApiService {
|
|
|
689
741
|
* @returns Merge result data including the resulting clientId
|
|
690
742
|
*/
|
|
691
743
|
mergeUser(request: MergeUserRequest): Promise<MergeUserData>;
|
|
744
|
+
/**
|
|
745
|
+
* Grant storage consent at runtime (e.g. the user accepted cookies while the
|
|
746
|
+
* chat was open).
|
|
747
|
+
*
|
|
748
|
+
* - Guest: persists the existing in-memory anonymous session to cookies so it
|
|
749
|
+
* simply becomes persistent (same session continues).
|
|
750
|
+
* - Logged-in user: merges the anonymous guest session into the user AND opens
|
|
751
|
+
* a fresh session for that user (via {@link createSession}) so we receive the
|
|
752
|
+
* user's auth data (accessToken, sessionId, clientId, layout, ...) from the
|
|
753
|
+
* API — the same merge-then-new-session flow used on a normal login.
|
|
754
|
+
*
|
|
755
|
+
* @param request - Optional metadata for the new session (logged-in case).
|
|
756
|
+
* @returns the newly created {@link SessionData} when a logged-in session was
|
|
757
|
+
* opened, or null when nothing new was created (guest, or no active
|
|
758
|
+
* in-memory session yet).
|
|
759
|
+
*/
|
|
760
|
+
enableStorageConsent(request?: AuthSessionRequest): Promise<SessionData | null>;
|
|
761
|
+
/**
|
|
762
|
+
* Withdraw storage consent at runtime. Clears all cookies but keeps the
|
|
763
|
+
* in-memory session so the current chat keeps working until the page is left.
|
|
764
|
+
*/
|
|
765
|
+
disableStorageConsent(): void;
|
|
692
766
|
private persistClientInfo;
|
|
693
767
|
private restoreSessionFromCookies;
|
|
694
768
|
private clearStoredClientInfo;
|
|
@@ -743,4 +817,4 @@ declare const ERROR_MESSAGES: {
|
|
|
743
817
|
readonly YOU_NEED_TO_INIT_THE_WIDGET_FIRST: "You need to initialize the widget first. Call init() before calling this method.";
|
|
744
818
|
};
|
|
745
819
|
|
|
746
|
-
export { API_BASE_URLS, API_ENDPOINTS, type AddToCartEvent, type AddToFavoriteEvent, type AddToFavoritePayload, ApiService, type AuthSessionRequest, type AuthSessionResponse, DEFAULT_CONFIG, ERROR_MESSAGES, type EventHandler, type EventName, type FavoriteProduct, type IUserCart, type IUserCartItem, type IncomingMessage, type IncomingMessageType, IncomingMessageTypes, type LayoutConfig, type MergeUserData, type MergeUserRequest, type MergeUserResponse, type OpenProductByIdPayload, type OutfitItem, type OutfitResponse, type OutgoingMessage, type OutgoingMessageType, OutgoingMessageTypes, type Product, type ProductImage, type ProductOffer, type ProductReplyOptions, type ProductSummary, type SendOutfitReplyPayload, type SendProductReplyPayload, type SendTextMessagePayload, type SessionData, type SessionDataChatPayload, type SetLocalePayload, type SophiConfig, type SophiEventMap, type SophiLocale, SophiWidget, type TriggerProductReplyPayload, type UserData };
|
|
820
|
+
export { API_BASE_URLS, API_ENDPOINTS, type AddToCartEvent, type AddToFavoriteEvent, type AddToFavoritePayload, ApiService, type AuthSessionRequest, type AuthSessionResponse, DEFAULT_CONFIG, ERROR_MESSAGES, type EventHandler, type EventName, type FavoriteProduct, type IUserCart, type IUserCartItem, type IncomingMessage, type IncomingMessageType, IncomingMessageTypes, type LayoutConfig, type MergeUserData, type MergeUserRequest, type MergeUserResponse, type OpenProductByIdPayload, type OutfitItem, type OutfitResponse, type OutgoingMessage, type OutgoingMessageType, OutgoingMessageTypes, type Product, type ProductImage, type ProductOffer, type ProductReplyOptions, type ProductSummary, type SendOutfitReplyPayload, type SendProductReplyPayload, type SendTextMessagePayload, type SessionData, type SessionDataChatPayload, type SetConsentPayload, type SetLocalePayload, type SophiConfig, type SophiEventMap, type SophiLocale, SophiWidget, type TriggerProductReplyPayload, type UserData };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var v={ADD_TO_CART:"add_to_cart",ADD_TO_FAVORITE:"add_to_favorite",READY:"ready",ERROR:"error",SEND_TO_CHECKOUT:"send_to_checkout"},o={USER_DATA:"user_data",CONFIG:"config",UPDATE_USER_CART:"update_user_cart",DESTROY:"destroy",SESSION_DATA:"session_data",SET_LOCALE:"set_locale",CLIENT_ID_UPDATED:"client_id_updated",LAYOUT_CONFIG:"layout_config",TOGGLE_HISTORY:"toggle_history",SEND_TEXT_MESSAGE:"send_text_message",SEND_PRODUCT_REPLY:"send_product_reply",SEND_OUTFIT_REPLY:"send_outfit_reply",OPEN_PRODUCT_BY_ID:"open_product_by_id",TRIGGER_PRODUCT_REPLY:"trigger_product_reply",UPDATE_FAVORITE:"update_favorite"};function E(s){let e=2166136261;for(let t=0;t<s.length;t++)e^=s.charCodeAt(t),e=e*16777619>>>0;return e.toString(16).padStart(8,"0")}var d=class d{constructor(e,t){this.namespace=d.sanitizeNamespace(e),this.maxAgeSeconds=t?.maxAgeSeconds??d.DEFAULT_MAX_AGE_SECONDS,this.keys={...d.DEFAULT_KEYS,...t?.keys??{}};}static buildNamespace(e,t){return `sophi-${E(t)}`}static canUseCookies(){return typeof document<"u"&&typeof document.cookie=="string"}set(e,t){d.canUseCookies()&&(document.cookie=`${this.formatName(e)}=${encodeURIComponent(t)}; path=/; max-age=${this.maxAgeSeconds}; samesite=lax`);}get(e){return d.canUseCookies()?d.readRawCookie(this.formatName(e)):null}delete(e){d.canUseCookies()&&(document.cookie=`${this.formatName(e)}=; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT; samesite=lax`);}clearAll(){Object.keys(this.keys).forEach(e=>{this.delete(e);});}formatName(e){return `${this.namespace}-${this.keys[e]}`}static readRawCookie(e){let t=document.cookie.split(";").map(r=>r.trim());for(let r of t){if(!r)continue;let[i,...n]=r.split("=");if(i===e)return decodeURIComponent(n.join("="))}return null}static sanitizeNamespace(e){let t=e.replace(/[^a-zA-Z0-9_-]/g,"");return t.length?t:"sophi-widget"}};d.DEFAULT_MAX_AGE_SECONDS=3600*24*30,d.DEFAULT_KEYS={clientId:"client-id",externalUserId:"external-user-id",accessToken:"access-token",sessionData:"session-data",metadataHash:"metadata-hash"};var u=d;var p=class{constructor(e,t,r){this.clientId=null;this.userId=void 0;this.storedUserId=null;this.baseUrl=e,this.apiKey=t,this.userId=r,this.cookieManager=new u(u.buildNamespace(e,t)),this.restoreSessionFromCookies();}async createSession(e){let t=`${this.baseUrl}/auth/v1/client/session`;try{let r={"Content-Type":"application/json",Accept:"application/json","x-api-key":this.apiKey};if(this.clientId){r["x-sophi-client-id"]=this.clientId;let c=this.storedUserId||null,l=this.userId||null;if(c!==l&&l)try{await this.mergeUser({userId:l}),this.storedUserId=l;}catch{this.clearStoredClientInfo(),delete r["x-sophi-client-id"],this.storedUserId=l;}}let i=await fetch(t,{method:"POST",headers:r,body:JSON.stringify({externalUserId:this.userId,metadata:e.metadata})});if(!i.ok){let c=await i.text();throw new Error(`Failed to create session: ${i.status} ${i.statusText}. ${c}`)}let n=await i.json();if(!n.success)throw new Error(`Session creation failed: ${n.message||"Unknown error"}`);if(!n.data)throw new Error("Session data is missing from response");return this.persistClientInfo(n.data.clientId,n.data.accessToken,this.userId),n.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}/auth/v1/client/update`;try{if(!this.clientId)throw new Error("Client ID is not set. Please create a session first.");let r=this.cookieManager.get("accessToken");if(!r)throw new Error("Access token is not available. Please create a session first.");let i=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json",Authorization:`Bearer ${r}`,"x-sophi-client-id":this.clientId},body:JSON.stringify({externalUserId:e.userId})});if(!i.ok){let c=await i.text();throw new Error(`Failed to merge user: ${i.status} ${i.statusText}. ${c}`)}let n=await i.json();if(!n.success)throw new Error(`User merge failed: ${n.message||"Unknown error"}`);if(!n.data)throw new Error("Merge data is missing from response");return n.data}catch(r){throw r instanceof Error?r:new Error(`Unexpected error during user merge: ${String(r)}`)}}persistClientInfo(e,t,r){this.clientId=e,this.userId=r,this.storedUserId=r||null,u.canUseCookies()&&(this.cookieManager.set("clientId",e),this.cookieManager.set("accessToken",t),this.cookieManager.set("externalUserId",r||""));}restoreSessionFromCookies(){if(!u.canUseCookies())return;let e=this.cookieManager.get("externalUserId"),t=this.cookieManager.get("clientId");if(this.storedUserId=e||null,!t)return;let r=e||null,i=this.userId||null;if(r===i){this.clientId=t,!this.userId&&e&&(this.userId=e);return}if(r&&!i){this.clearStoredClientInfo();return}if(r&&i&&r!==i){this.clearStoredClientInfo();return}if(!r&&i){this.clientId=t;return}}clearStoredClientInfo(){this.clientId=null,this.storedUserId=null,u.canUseCookies()&&this.cookieManager.clearAll();}};var _={WIDTH:"100%",HEIGHT:"600px"},g={test:"https://api-gateway.dev.usesophi.com",production:"https://api.usesophi.com"},f={test:"http://localhost:5173",production:"https://app.prod.usesophi.com"},T={CREATE_SESSION:"/auth/v1/client/session"},a={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."};function m(s){if(!s.apiKey||typeof s.apiKey!="string")throw new Error(a.MISSING_API_KEY);if(s.userId&&typeof s.userId!="string")throw new Error(a.MISSING_USER_ID);if(!s.container)throw new Error(a.MISSING_CONTAINER)}function y(s){if(!s||typeof s!="object")throw new Error("[Sophi] updateUserCart: cart must be a non-null object");if(!s.cartId||typeof s.cartId!="string")throw new Error("[Sophi] updateUserCart: cart.cartId is required and must be a string");if(!Array.isArray(s.items))throw new Error("[Sophi] updateUserCart: cart.items must be an array");s.items.forEach((e,t)=>{if(!e||typeof e!="object")throw new Error(`[Sophi] updateUserCart: cart.items[${t}] must be a non-null object`);if(!e.productId||typeof e.productId!="string")throw new Error(`[Sophi] updateUserCart: cart.items[${t}].productId is required and must be a string`);if(!e.variantId||typeof e.variantId!="string")throw new Error(`[Sophi] updateUserCart: cart.items[${t}].variantId is required and must be a string`);if(e.quantity!==void 0&&(typeof e.quantity!="number"||e.quantity<0))throw new Error(`[Sophi] updateUserCart: cart.items[${t}].quantity must be a non-negative number`)});}function I(s){if(!s||typeof s!="object")return false;let e=s;return Array.isArray(e.products)?e.products.every(t=>{if(!t||typeof t!="object")return false;let r=t;return !(typeof r.productId!="string"||r.variantId!==void 0&&typeof r.variantId!="string")}):false}function S(s){if(!s||typeof s!="object")return console.warn("[Sophi] add_to_favorite: invalid message data \u2014 ignoring"),null;let e=s,t=e.product;if(!t||typeof t!="object")return console.warn("[Sophi] add_to_favorite: data.product is missing or invalid \u2014 ignoring"),null;if(typeof e.isFavorite!="boolean")return console.warn("[Sophi] add_to_favorite: isFavorite must be a boolean \u2014 ignoring"),null;let r=t,i=r.externalId,n=r.id,c=r.productId,l=typeof i=="string"&&i.length>0?i:n!=null?String(n):typeof c=="string"&&c.length>0?c:null;return l==null?(console.warn("[Sophi] add_to_favorite: product has no externalId, id, or productId \u2014 ignoring"),null):{product:t,isFavorite:e.isFavorite,productIdentifier:l}}var h=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.currentLocale=void 0;this.apiService=null;this.apiBaseUrl=null;}async init(e){if(this.isInitialized){console.warn(a.ALREADY_INITIALIZED);return}try{m(e),this.config=this.formatConfig(e),this.currentLocale=this.config.locale;let t=g[e.environment||"production"]||g.production;this.apiBaseUrl=t,this.apiService=new p(this.apiBaseUrl,e.apiKey,e.userId),await this.createAuthSession(),this.isInitialized=!0;try{let r=new URL(this.getIframeUrl());this.iframeOrigin=r.origin;}catch{let i=new Error(`${a.INVALID_IFRAME_URL}: ${this.getIframeUrl()}`);throw this.emitError(i),i}if(this.container=this.resolveContainer(e.container),!this.container){let r=new Error(a.CONTAINER_NOT_FOUND);throw this.emitError(r),r}this.createIframe(),this.setupMessageListener(),this.iframe?.addEventListener("load",()=>{setTimeout(()=>{this.sendSessionData(),this.config?.layout&&this.sendLayoutConfig();},100);}),e.onReady&&e.onReady();}catch(t){this.isInitialized=false,this.config=null,this.apiService=null,this.sessionData=null,this.currentLocale=void 0;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:o.USER_DATA,data:e};this.postMessage(t);}sendToggleHistory(){if(!this.isInitialized||!this.iframe){console.warn("Widget not initialized. Call init() first.");return}let e={type:o.TOGGLE_HISTORY};this.postMessage(e);}sendTextMessage(e){if(!this.iframe||!this.iframe.contentWindow){console.warn("[Sophi] sendTextMessage: iframe is not ready.");return}let t={type:o.SEND_TEXT_MESSAGE,data:{query:e}};this.postMessage(t);}sendProductReply(e,t){if(!this.iframe||!this.iframe.contentWindow){console.warn("[Sophi] sendProductReply: iframe is not ready.");return}let r={type:o.SEND_PRODUCT_REPLY,data:{query:t?.query||void 0,externalProductId:e,...t?.isOutfit!==void 0&&{isOutfit:t.isOutfit}}};this.postMessage(r);}sendOutfitReply(e,t){if(!this.iframe||!this.iframe.contentWindow){console.warn("[Sophi] sendOutfitReply: iframe is not ready.");return}let r={type:o.SEND_OUTFIT_REPLY,data:{query:e,outfitId:t.recommendationId,outfit:t}};this.postMessage(r);}openProductById(e,t){if(!this.iframe||!this.iframe.contentWindow){console.warn("[Sophi] openProductById: iframe is not ready.");return}let r={type:o.OPEN_PRODUCT_BY_ID,data:{externalProductId:e,...t!==void 0&&{reason:t}}};this.postMessage(r);}updateFavorite(e,t){if(!this.iframe||!this.iframe.contentWindow){console.warn("[Sophi] updateFavorite: iframe is not ready.");return}let r={type:o.UPDATE_FAVORITE,data:{productId:e,isFavorite:t}};this.postMessage(r);}sendSessionToChat(){if(!this.isInitialized||!this.iframe){console.warn("[Sophi] sendSessionToChat: widget not initialized.");return}this.sendSessionData();}setLocale(e){if(e!=="tr"&&e!=="en"){console.warn(`[Sophi] setLocale: invalid locale "${e}" \u2014 expected "tr" or "en".`);return}if(this.currentLocale=e,!this.iframe?.contentWindow){console.warn("[Sophi] setLocale: iframe is not ready.");return}let t={type:o.SET_LOCALE,data:{locale:e}};this.postMessage(t);}updateUserCart(e){if(!this.isInitialized||!this.iframe){console.warn("Widget not initialized. Call init() first.");return}y(e);let t={type:o.UPDATE_USER_CART,data:e};this.postMessage(t);}onDestroy(){if(!this.isInitialized||!this.iframe){console.warn("Widget not initialized. Call init() first.");return}let e={type:o.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.currentLocale=void 0,this.apiService=null;}isReady(){return this.isInitialized&&this.iframe!==null}formatConfig(e){return {...e,userId:e.userId||void 0}}async createAuthSession(){if(!this.config||!this.apiService)throw new Error(a.NOT_INITIALIZED);try{if(this.sessionData=await this.apiService.createSession({externalUserId:this.config.userId,metadata:this.config.metadata}),!this.apiService.validateSessionData(this.sessionData))throw new Error(a.INVALID_SESSION_DATA)}catch(e){throw e instanceof Error?e:new Error(a.SESSION_CREATION_FAILED)}}resolveSessionUserName(e,t){let r=[e,t].find(i=>typeof i=="string"&&i.trim().length>0);return r?r.trim():""}buildChatSessionPayload(){if(!this.sessionData||!this.config)return null;let e=this.sessionData,t=e.layoutId,r=t!=null&&String(t).length>0?String(t):void 0,i=this.resolveSessionUserName(this.config.userName,e.userName);return {accessToken:e.accessToken,sessionId:e.sessionId,clientId:e.clientId,companyCode:e.companyCode,expiresAt:e.expiresAt,userName:i,...r!==void 0?{layoutId:r}:{},...this.currentLocale?{locale:this.currentLocale}:{}}}sendSessionData(){let e=this.buildChatSessionPayload();if(!e){console.warn("Session data not available");return}let t={type:o.SESSION_DATA,data:e};this.postMessage(t);}sendLayoutConfig(){if(!this.config?.layout)return;let e={type:o.LAYOUT_CONFIG,data:this.config.layout};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; clipboard-write",this.container.appendChild(e),this.iframe=e;}buildIframeUrl(){return this.config?new URL(this.getIframeUrl()).toString():""}getIframeUrl(){return this.config?this.config.environment==="production"?f.production:f.test:""}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":I(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 "add_to_favorite":{let r=S(e.data);r&&(console.log(`[Sophi] add_to_favorite fired: productIdentifier=${r.productIdentifier}, isFavorite=${r.isFavorite}`),this.emit("add_to_favorite",r));break}case "send_to_checkout":this.emit("send_to_checkout",void 0);break;case "ready":this.emit("ready",void 0),setTimeout(()=>{this.sendSessionData(),this.config?.layout&&this.sendLayoutConfig();},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);}}postMessage(e){if(!this.iframe||!this.iframe.contentWindow||!this.iframeOrigin){console.warn("Cannot send message: iframe not ready");return}try{this.iframe.contentWindow.postMessage(e,this.iframeOrigin);}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(n){console.error(`Error in ${e} event handler:`,n);}});}emitError(e){this.emit("error",e),this.config?.onError&&this.config.onError(e);}};
|
|
2
|
-
export{g as API_BASE_URLS,
|
|
1
|
+
var v={ADD_TO_CART:"add_to_cart",ADD_TO_FAVORITE:"add_to_favorite",READY:"ready",ERROR:"error",SEND_TO_CHECKOUT:"send_to_checkout"},o={USER_DATA:"user_data",CONFIG:"config",UPDATE_USER_CART:"update_user_cart",DESTROY:"destroy",SESSION_DATA:"session_data",SET_LOCALE:"set_locale",SET_CONSENT:"set_consent",CLIENT_ID_UPDATED:"client_id_updated",LAYOUT_CONFIG:"layout_config",TOGGLE_HISTORY:"toggle_history",SEND_TEXT_MESSAGE:"send_text_message",SEND_PRODUCT_REPLY:"send_product_reply",SEND_OUTFIT_REPLY:"send_outfit_reply",OPEN_PRODUCT_BY_ID:"open_product_by_id",TRIGGER_PRODUCT_REPLY:"trigger_product_reply",UPDATE_FAVORITE:"update_favorite"};function E(s){let e=2166136261;for(let t=0;t<s.length;t++)e^=s.charCodeAt(t),e=e*16777619>>>0;return e.toString(16).padStart(8,"0")}var d=class d{constructor(e,t){this.namespace=d.sanitizeNamespace(e),this.maxAgeSeconds=t?.maxAgeSeconds??d.DEFAULT_MAX_AGE_SECONDS,this.keys={...d.DEFAULT_KEYS,...t?.keys??{}};}static buildNamespace(e,t){return `sophi-${E(t)}`}static canUseCookies(){return typeof document<"u"&&typeof document.cookie=="string"}set(e,t){d.canUseCookies()&&(document.cookie=`${this.formatName(e)}=${encodeURIComponent(t)}; path=/; max-age=${this.maxAgeSeconds}; samesite=lax`);}get(e){return d.canUseCookies()?d.readRawCookie(this.formatName(e)):null}delete(e){d.canUseCookies()&&(document.cookie=`${this.formatName(e)}=; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT; samesite=lax`);}clearAll(){Object.keys(this.keys).forEach(e=>{this.delete(e);});}formatName(e){return `${this.namespace}-${this.keys[e]}`}static readRawCookie(e){let t=document.cookie.split(";").map(r=>r.trim());for(let r of t){if(!r)continue;let[i,...n]=r.split("=");if(i===e)return decodeURIComponent(n.join("="))}return null}static sanitizeNamespace(e){let t=e.replace(/[^a-zA-Z0-9_-]/g,"");return t.length?t:"sophi-widget"}};d.DEFAULT_MAX_AGE_SECONDS=3600*24*30,d.DEFAULT_KEYS={clientId:"client-id",externalUserId:"external-user-id",accessToken:"access-token",sessionData:"session-data",metadataHash:"metadata-hash"};var c=d;var p=class{constructor(e,t,r,i=true){this.clientId=null;this.userId=void 0;this.storedUserId=null;this.accessToken=null;this.pendingUserId=null;this.baseUrl=e,this.apiKey=t,this.storageConsent=i,this.cookieManager=new c(c.buildNamespace(e,t)),i?(this.userId=r,this.restoreSessionFromCookies()):(this.pendingUserId=r||null,this.userId=void 0);}async createSession(e){let t=`${this.baseUrl}/auth/v1/client/session`;try{let r={"Content-Type":"application/json",Accept:"application/json","x-api-key":this.apiKey};if(this.clientId){r["x-sophi-client-id"]=this.clientId;let l=this.storedUserId||null,u=this.userId||null;if(l!==u&&u)try{await this.mergeUser({userId:u}),this.storedUserId=u;}catch{this.clearStoredClientInfo(),delete r["x-sophi-client-id"],this.storedUserId=u;}}let i=await fetch(t,{method:"POST",headers:r,body:JSON.stringify({externalUserId:this.userId,metadata:e.metadata})});if(!i.ok){let l=await i.text();throw new Error(`Failed to create session: ${i.status} ${i.statusText}. ${l}`)}let n=await i.json();if(!n.success)throw new Error(`Session creation failed: ${n.message||"Unknown error"}`);if(!n.data)throw new Error("Session data is missing from response");return this.persistClientInfo(n.data.clientId,n.data.accessToken,this.userId),n.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}/auth/v1/client/update`;try{if(!this.clientId)throw new Error("Client ID is not set. Please create a session first.");let r=this.accessToken??this.cookieManager.get("accessToken");if(!r)throw new Error("Access token is not available. Please create a session first.");let i=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json",Authorization:`Bearer ${r}`,"x-sophi-client-id":this.clientId},body:JSON.stringify({externalUserId:e.userId})});if(!i.ok){let l=await i.text();throw new Error(`Failed to merge user: ${i.status} ${i.statusText}. ${l}`)}let n=await i.json();if(!n.success)throw new Error(`User merge failed: ${n.message||"Unknown error"}`);if(!n.data)throw new Error("Merge data is missing from response");return n.data}catch(r){throw r instanceof Error?r:new Error(`Unexpected error during user merge: ${String(r)}`)}}async enableStorageConsent(e={}){return this.storageConsent=true,!this.clientId||!this.accessToken?(this.pendingUserId&&!this.userId&&(this.userId=this.pendingUserId),this.pendingUserId=null,null):(c.canUseCookies()&&(this.cookieManager.set("clientId",this.clientId),this.cookieManager.set("accessToken",this.accessToken),this.cookieManager.set("externalUserId",this.userId||"")),this.pendingUserId?(this.userId=this.pendingUserId,this.pendingUserId=null,this.createSession(e)):null)}disableStorageConsent(){this.storageConsent=false,c.canUseCookies()&&this.cookieManager.clearAll();}persistClientInfo(e,t,r){this.clientId=e,this.accessToken=t,this.userId=r,this.storedUserId=r||null,!(!this.storageConsent||!c.canUseCookies())&&(this.cookieManager.set("clientId",e),this.cookieManager.set("accessToken",t),this.cookieManager.set("externalUserId",r||""));}restoreSessionFromCookies(){if(!this.storageConsent||!c.canUseCookies())return;let e=this.cookieManager.get("externalUserId"),t=this.cookieManager.get("clientId");if(this.storedUserId=e||null,!t)return;let r=e||null,i=this.userId||null;if(r===i){this.clientId=t,!this.userId&&e&&(this.userId=e);return}if(r&&!i){this.clearStoredClientInfo();return}if(r&&i&&r!==i){this.clearStoredClientInfo();return}if(!r&&i){this.clientId=t;return}}clearStoredClientInfo(){this.clientId=null,this.storedUserId=null,c.canUseCookies()&&this.cookieManager.clearAll();}};var _={WIDTH:"100%",HEIGHT:"600px"},g={test:"https://api-gateway.dev.usesophi.com",production:"https://api.usesophi.com"},h={test:"http://localhost:5173/",production:"https://app.prod.usesophi.com"},C={CREATE_SESSION:"/auth/v1/client/session"},a={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."};function m(s){if(!s.apiKey||typeof s.apiKey!="string")throw new Error(a.MISSING_API_KEY);if(s.userId&&typeof s.userId!="string")throw new Error(a.MISSING_USER_ID);if(!s.container)throw new Error(a.MISSING_CONTAINER)}function I(s){if(!s||typeof s!="object")throw new Error("[Sophi] updateUserCart: cart must be a non-null object");if(!s.cartId||typeof s.cartId!="string")throw new Error("[Sophi] updateUserCart: cart.cartId is required and must be a string");if(!Array.isArray(s.items))throw new Error("[Sophi] updateUserCart: cart.items must be an array");s.items.forEach((e,t)=>{if(!e||typeof e!="object")throw new Error(`[Sophi] updateUserCart: cart.items[${t}] must be a non-null object`);if(!e.productId||typeof e.productId!="string")throw new Error(`[Sophi] updateUserCart: cart.items[${t}].productId is required and must be a string`);if(!e.variantId||typeof e.variantId!="string")throw new Error(`[Sophi] updateUserCart: cart.items[${t}].variantId is required and must be a string`);if(e.quantity!==void 0&&(typeof e.quantity!="number"||e.quantity<0))throw new Error(`[Sophi] updateUserCart: cart.items[${t}].quantity must be a non-negative number`)});}function y(s){if(!s||typeof s!="object")return false;let e=s;return Array.isArray(e.products)?e.products.every(t=>{if(!t||typeof t!="object")return false;let r=t;return !(typeof r.productId!="string"||r.variantId!==void 0&&typeof r.variantId!="string")}):false}function S(s){if(!s||typeof s!="object")return console.warn("[Sophi] add_to_favorite: invalid message data \u2014 ignoring"),null;let e=s,t=e.product;if(!t||typeof t!="object")return console.warn("[Sophi] add_to_favorite: data.product is missing or invalid \u2014 ignoring"),null;if(typeof e.isFavorite!="boolean")return console.warn("[Sophi] add_to_favorite: isFavorite must be a boolean \u2014 ignoring"),null;let r=t,i=r.externalId,n=r.id,l=r.productId,u=typeof i=="string"&&i.length>0?i:n!=null?String(n):typeof l=="string"&&l.length>0?l:null;return u==null?(console.warn("[Sophi] add_to_favorite: product has no externalId, id, or productId \u2014 ignoring"),null):{product:t,isFavorite:e.isFavorite,productIdentifier:u}}var f=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.currentLocale=void 0;this.apiService=null;this.apiBaseUrl=null;}async init(e){if(this.isInitialized){console.warn(a.ALREADY_INITIALIZED);return}try{m(e),this.config=this.formatConfig(e),this.currentLocale=this.config.locale;let t=g[e.environment||"production"]||g.production;this.apiBaseUrl=t,this.apiService=new p(this.apiBaseUrl,e.apiKey,e.userId,e.storageConsent!==!1),await this.createAuthSession(),this.isInitialized=!0;try{let r=new URL(this.getIframeUrl());this.iframeOrigin=r.origin;}catch{let i=new Error(`${a.INVALID_IFRAME_URL}: ${this.getIframeUrl()}`);throw this.emitError(i),i}if(this.container=this.resolveContainer(e.container),!this.container){let r=new Error(a.CONTAINER_NOT_FOUND);throw this.emitError(r),r}this.createIframe(),this.setupMessageListener(),this.iframe?.addEventListener("load",()=>{setTimeout(()=>{this.sendSessionData(),this.config?.layout&&this.sendLayoutConfig();},100);}),e.onReady&&e.onReady();}catch(t){this.isInitialized=false,this.config=null,this.apiService=null,this.sessionData=null,this.currentLocale=void 0;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:o.USER_DATA,data:e};this.postMessage(t);}sendToggleHistory(){if(!this.isInitialized||!this.iframe){console.warn("Widget not initialized. Call init() first.");return}let e={type:o.TOGGLE_HISTORY};this.postMessage(e);}sendTextMessage(e){if(!this.iframe||!this.iframe.contentWindow){console.warn("[Sophi] sendTextMessage: iframe is not ready.");return}let t={type:o.SEND_TEXT_MESSAGE,data:{query:e}};this.postMessage(t);}sendProductReply(e,t){if(!this.iframe||!this.iframe.contentWindow){console.warn("[Sophi] sendProductReply: iframe is not ready.");return}let r={type:o.SEND_PRODUCT_REPLY,data:{query:t?.query||void 0,externalProductId:e,...t?.isOutfit!==void 0&&{isOutfit:t.isOutfit}}};this.postMessage(r);}sendOutfitReply(e,t){if(!this.iframe||!this.iframe.contentWindow){console.warn("[Sophi] sendOutfitReply: iframe is not ready.");return}let r={type:o.SEND_OUTFIT_REPLY,data:{query:e,outfitId:t.recommendationId,outfit:t}};this.postMessage(r);}openProductById(e,t){if(!this.iframe||!this.iframe.contentWindow){console.warn("[Sophi] openProductById: iframe is not ready.");return}let r={type:o.OPEN_PRODUCT_BY_ID,data:{externalProductId:e,...t!==void 0&&{reason:t}}};this.postMessage(r);}updateFavorite(e,t){if(!this.iframe||!this.iframe.contentWindow){console.warn("[Sophi] updateFavorite: iframe is not ready.");return}let r={type:o.UPDATE_FAVORITE,data:{productId:e,isFavorite:t}};this.postMessage(r);}sendSessionToChat(){if(!this.isInitialized||!this.iframe){console.warn("[Sophi] sendSessionToChat: widget not initialized.");return}this.sendSessionData();}async setStorageConsent(e){if(this.config&&(this.config.storageConsent=e),!this.apiService){console.warn("[Sophi] setStorageConsent: widget not initialized yet.");return}if(e){try{let t=await this.apiService.enableStorageConsent({metadata:this.config?.metadata});t&&(this.sessionData=t);}catch(t){this.emitError(t instanceof Error?t:new Error(String(t)));}this.postSetConsent(true),this.sendSessionData();}else this.apiService.disableStorageConsent(),this.postSetConsent(false);}postSetConsent(e){if(!this.iframe||!this.iframe.contentWindow)return;let t={type:o.SET_CONSENT,data:{storage:e}};this.postMessage(t);}setLocale(e){if(e!=="tr"&&e!=="en"){console.warn(`[Sophi] setLocale: invalid locale "${e}" \u2014 expected "tr" or "en".`);return}if(this.currentLocale=e,!this.iframe?.contentWindow){console.warn("[Sophi] setLocale: iframe is not ready.");return}let t={type:o.SET_LOCALE,data:{locale:e}};this.postMessage(t);}updateUserCart(e){if(!this.isInitialized||!this.iframe){console.warn("Widget not initialized. Call init() first.");return}I(e);let t={type:o.UPDATE_USER_CART,data:e};this.postMessage(t);}onDestroy(){if(!this.isInitialized||!this.iframe){console.warn("Widget not initialized. Call init() first.");return}let e={type:o.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.currentLocale=void 0,this.apiService=null;}isReady(){return this.isInitialized&&this.iframe!==null}formatConfig(e){return {...e,userId:e.userId||void 0}}async createAuthSession(){if(!this.config||!this.apiService)throw new Error(a.NOT_INITIALIZED);try{if(this.sessionData=await this.apiService.createSession({externalUserId:this.config.userId,metadata:this.config.metadata}),!this.apiService.validateSessionData(this.sessionData))throw new Error(a.INVALID_SESSION_DATA)}catch(e){throw e instanceof Error?e:new Error(a.SESSION_CREATION_FAILED)}}resolveSessionUserName(e,t){let r=[e,t].find(i=>typeof i=="string"&&i.trim().length>0);return r?r.trim():""}buildChatSessionPayload(){if(!this.sessionData||!this.config)return null;let e=this.sessionData,t=e.layoutId,r=t!=null&&String(t).length>0?String(t):void 0,i=this.resolveSessionUserName(this.config.userName,e.userName);return {accessToken:e.accessToken,sessionId:e.sessionId,clientId:e.clientId,companyCode:e.companyCode,expiresAt:e.expiresAt,userName:i,...r!==void 0?{layoutId:r}:{},...this.currentLocale?{locale:this.currentLocale}:{}}}sendSessionData(){let e=this.buildChatSessionPayload();if(!e){console.warn("Session data not available");return}let t={type:o.SESSION_DATA,data:e};this.postMessage(t);}sendLayoutConfig(){if(!this.config?.layout)return;let e={type:o.LAYOUT_CONFIG,data:this.config.layout};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; clipboard-write",this.container.appendChild(e),this.iframe=e;}buildIframeUrl(){if(!this.config)return "";let e=new URL(this.getIframeUrl());return this.config.storageConsent===false&&e.searchParams.set("storageConsent","0"),e.toString()}getIframeUrl(){return this.config?this.config.environment==="production"?h.production:h.test:""}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":y(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 "add_to_favorite":{let r=S(e.data);r&&(console.log(`[Sophi] add_to_favorite fired: productIdentifier=${r.productIdentifier}, isFavorite=${r.isFavorite}`),this.emit("add_to_favorite",r));break}case "send_to_checkout":this.emit("send_to_checkout",void 0);break;case "ready":this.emit("ready",void 0),setTimeout(()=>{this.sendSessionData(),this.config?.layout&&this.sendLayoutConfig();},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);}}postMessage(e){if(!this.iframe||!this.iframe.contentWindow||!this.iframeOrigin){console.warn("Cannot send message: iframe not ready");return}try{this.iframe.contentWindow.postMessage(e,this.iframeOrigin);}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(n){console.error(`Error in ${e} event handler:`,n);}});}emitError(e){this.emit("error",e),this.config?.onError&&this.config.onError(e);}};
|
|
2
|
+
export{g as API_BASE_URLS,C as API_ENDPOINTS,p as ApiService,_ as DEFAULT_CONFIG,a as ERROR_MESSAGES,v as IncomingMessageTypes,o as OutgoingMessageTypes,f as SophiWidget};//# sourceMappingURL=index.js.map
|
|
3
3
|
//# sourceMappingURL=index.js.map
|