@sendoracloud/sdk-web 2.2.0 → 2.5.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 CHANGED
@@ -1 +1 @@
1
- "use strict";var O=Object.defineProperty;var J=Object.getOwnPropertyDescriptor;var G=Object.getOwnPropertyNames;var Z=Object.prototype.hasOwnProperty;var tt=(r,t)=>{for(var e in t)O(r,e,{get:t[e],enumerable:!0})},et=(r,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of G(t))!Z.call(r,n)&&n!==e&&O(r,n,{get:()=>t[n],enumerable:!(s=J(t,n))||s.enumerable});return r};var st=r=>et(O({},"__esModule",{value:!0}),r);var wt={};tt(wt,{Analytics:()=>m,Auth:()=>k,AuthError:()=>c,Chatbot:()=>S,Consent:()=>A,EmailAlreadyTakenError:()=>I,FeatureFlags:()=>y,InAppMessaging:()=>w,SendoraCloud:()=>M,Surveys:()=>v,ValidationError:()=>l,default:()=>vt,safeUrl:()=>h,sanitizeHtml:()=>R});module.exports=st(wt);var H=/^[a-zA-Z][a-zA-Z0-9._:-]{0,127}$/;var rt=new Set(["__proto__","constructor","prototype"]),l=class extends Error{constructor(t){super(`[SendoraCloud] ${t}`),this.name="SendoraValidationError"}};function D(r){if(typeof r!="string"||!H.test(r))throw new l(`Invalid event name: must match ${H}, got "${String(r)}".`)}function L(r){if(r==null)return;if(typeof r!="object"||Array.isArray(r))throw new l("Properties must be a plain object, not an array or primitive.");let t=K(r);if(t>5)throw new l(`Properties nested too deeply (${t} levels, max 5).`);N(r);let e;try{e=JSON.stringify(r).length}catch{throw new l("Properties contain a circular reference or unserializable value.")}if(e>32768)throw new l(`Properties too large (${e} bytes, max 32768).`)}function K(r,t=0){if(t>6||r==null||typeof r!="object")return t;let e=t;for(let s of Object.keys(r)){let n=K(r[s],t+1);n>e&&(e=n)}return e}function N(r){if(!(r==null||typeof r!="object"))for(let t of Object.keys(r)){if(rt.has(t))throw new l(`Forbidden property key "${t}" (prototype pollution guard).`);N(r[t])}}var nt=/^pk_[A-Za-z0-9_-]{16,}$/;function j(r){if(!r||typeof r!="string")throw new l("apiKey is required.");if(r.startsWith("sk_")||r.startsWith("sendora_secret_"))throw new l("Secret keys must NEVER be used in browser SDK. Use a publishable key (pk_...).");if(!nt.test(r))throw new l("Invalid publishable key format. Expected pk_... (17+ chars).")}function z(r){let t=r.replace(/\/$/,"");if(!(t.startsWith("http://localhost")||t.startsWith("http://127.0.0.1"))&&!t.startsWith("https://"))throw new l("apiUrl must be https:// (only http://localhost is allowed for dev).")}var it="https://api.sendoracloud.com",ot=1e4,_=class{constructor(t){let e=(t.apiUrl??it).replace(/\/$/,"");z(e),this.apiKey=t.apiKey,this.baseUrl=e,this.debug=!!t.debug}get(t){return this.request("GET",t)}post(t,e){return this.request("POST",t,e)}async request(t,e,s){let n=`${this.baseUrl}/api/v1${e}`,i=typeof AbortController<"u"?new AbortController:null,o=i?setTimeout(()=>i.abort(),ot):null;try{let u=await fetch(n,{method:t,headers:{"Content-Type":"application/json","X-API-Key":this.apiKey},body:s?JSON.stringify(s):void 0,signal:i?i.signal:void 0});o&&clearTimeout(o);let a;try{a=await u.json()}catch{a={success:!1,error:{code:"PARSE_ERROR",message:`HTTP ${u.status}`}}}if(this.debug&&console.debug(`[SendoraCloud] ${t} ${e} \u2192 ${u.status}`),!u.ok)if(e.startsWith("/auth-service/")&&!this.debug)console.warn(`[SendoraCloud] ${t} ${e} \u2192 HTTP ${u.status}`);else{let f=a?.error?.code??"HTTP_ERROR",X=a?.error?.message??`HTTP ${u.status}`,$=a?.error?.details?.fieldErrors,W=$?` (${Object.entries($).map(([Y,B])=>`${Y}: ${(B??[]).join(", ")}`).join("; ")})`:"";console.warn(`[SendoraCloud] ${t} ${e} \u2192 HTTP ${u.status} \u2014 ${f}: ${X}${W}`)}return a}catch(u){o&&clearTimeout(o);let a=u instanceof Error&&u.name==="AbortError"?"timeout":"error";return console.warn(`[SendoraCloud] ${t} ${e} network ${a}`),{success:!1,error:{code:"NETWORK_ERROR",message:a}}}}};var C="sendora_",E=class{constructor(){this.memoryStore=new Map;this.useLocalStorage=this.checkLocalStorage()}get(t){let e=C+t;if(this.useLocalStorage)try{return localStorage.getItem(e)}catch{return this.memoryStore.get(e)??null}return this.memoryStore.get(e)??null}set(t,e){let s=C+t;if(this.useLocalStorage)try{localStorage.setItem(s,e);return}catch{}this.memoryStore.set(s,e)}remove(t){let e=C+t;if(this.useLocalStorage)try{localStorage.removeItem(e)}catch{}this.memoryStore.delete(e)}clearPrefix(t){let e=C+t;if(this.useLocalStorage)try{for(let s=localStorage.length-1;s>=0;s--){let n=localStorage.key(s);n&&n.startsWith(e)&&localStorage.removeItem(n)}}catch{}for(let s of Array.from(this.memoryStore.keys()))s.startsWith(e)&&this.memoryStore.delete(s)}checkLocalStorage(){try{if(typeof localStorage>"u")return!1;let t="__sendora_test__";return localStorage.setItem(t,"1"),localStorage.removeItem(t),!0}catch{return!1}}};function g(r=""){let t=new Uint8Array(16),e=typeof globalThis<"u"&&"crypto"in globalThis?globalThis.crypto:void 0;if(!e||typeof e.getRandomValues!="function")throw new Error("[SendoraCloud] crypto.getRandomValues unavailable \u2014 refusing to mint IDs without a CSPRNG");e.getRandomValues(t);let s="";for(let n of t)s+=n.toString(16).padStart(2,"0");return r+s}var Q="2.2.0",q=500,at=10,ut=6e4,m=class{constructor(t,e,s,n){this.queue=[];this.timer=null;this.userId=null;this.identityToken=null;this.consecutiveFailures=0;this.nextFlushAfter=0;this.http=e,this.storage=s,this.consent=n,this.projectId=t.projectId,this.flushInterval=t.flushInterval??5e3,this.maxQueueSize=t.maxQueueSize??20,this.persistUserId=!!t.persistUserId,this.stripQueryParams=t.stripQueryParams!==!1;let i=this.storage.get("anonymous_id");if(i?this.anonymousId=i:(this.anonymousId=g("anon_"),this.storage.set("anonymous_id",this.anonymousId)),this.persistUserId){let o=this.storage.get("user_id");o&&(this.userId=o)}this.timer=setInterval(()=>this.flush(),this.flushInterval),typeof window<"u"&&window.addEventListener("beforeunload",()=>this.flush()),this.consent.subscribe(o=>{o&&this.flush()})}track(t,e){D(t),e&&L(e),this.queue.length>=q&&this.queue.shift(),this.queue.push({eventType:t,properties:e??{},userId:this.userId??void 0,anonymousId:this.anonymousId,timestamp:new Date().toISOString(),identityToken:this.identityToken??void 0}),this.queue.length>=this.maxQueueSize&&this.flush()}page(t){if(typeof window>"u")return;let e=this.stripQueryParams?window.location.origin+window.location.pathname:window.location.href;this.track("page.viewed",{url:e,path:window.location.pathname,title:document.title,referrer:document.referrer||void 0,...t})}identify(t,e,s){if(typeof t!="string"||t.length===0||t.length>256)throw new Error("[SendoraCloud] userId must be a 1-256 char string.");this.userId=t,this.identityToken=s?.identityToken??null,this.persistUserId&&this.storage.set("user_id",t),this.track("user.identified",{...e})}reset(){this.userId=null,this.identityToken=null,this.storage.remove("user_id"),this.anonymousId=g("anon_"),this.storage.set("anonymous_id",this.anonymousId),this.storage.clearPrefix("iam_shown_"),this.storage.clearPrefix("survey_shown_")}dropQueue(){this.queue=[]}async flush(){if(this.queue.length===0||!this.consent.isGranted()||Date.now()<this.nextFlushAfter)return;let t=this.queue.splice(0,100),e=t.map(o=>({projectId:this.projectId,module:"custom",eventType:o.eventType,userId:o.userId,anonymousId:o.anonymousId,identityToken:o.identityToken,timestamp:o.timestamp??new Date().toISOString(),properties:o.properties??{},context:this.getContext(),consent:["analytics"]}));if((await this.http.post("/events/batch",{events:e})).success){this.consecutiveFailures=0;return}if(this.consecutiveFailures++,this.consecutiveFailures>at)return;let n=Math.min(ut,1e3*2**this.consecutiveFailures);this.nextFlushAfter=Date.now()+n;let i=Math.max(0,q-this.queue.length);i>0&&this.queue.unshift(...t.slice(0,i))}destroy(){this.timer&&clearInterval(this.timer),this.timer=null,this.flush()}getContext(){return typeof window>"u"?{sdk:{name:"@sendoracloud/sdk-web",version:Q}}:{page:{url:this.stripQueryParams?window.location.origin+window.location.pathname:window.location.href,path:window.location.pathname,title:document.title},referrer:document.referrer||void 0,locale:typeof navigator<"u"?navigator.language:void 0,sdk:{name:"@sendoracloud/sdk-web",version:Q}}}};var y=class{constructor(t,e,s){this.cache=new Map;this.refreshInterval=null;this.http=e,this.storage=s;let n=this.storage.get("flags");if(n)try{let i=JSON.parse(n);if(Array.isArray(i))for(let o of i)F(o)&&this.cache.set(o.key,o)}catch{}}setContext(t){this.userId=t.userId,this.entityId=t.entityId,this.environment=t.environment}async getFlag(t){if(typeof t!="string"||t.length===0||t.length>128)return null;let e=this.cache.get(t);if(e)return e.value;let s=await this.http.post("/feature-flags/evaluate",{key:t,userId:this.userId,entityId:this.entityId,environment:this.environment});return s.success&&s.data&&F(s.data)?(this.cache.set(t,s.data),this.persistCache(),s.data.value):null}async isEnabled(t,e=!1){let s=await this.getFlag(t);return typeof s=="boolean"?s:e}async refreshAll(){let t=await this.http.post("/feature-flags/evaluate-all",{userId:this.userId,entityId:this.entityId,environment:this.environment});if(t.success&&Array.isArray(t.data)){this.cache.clear();for(let e of t.data)F(e)&&this.cache.set(e.key,e);this.persistCache()}}startAutoRefresh(t=6e4){this.stopAutoRefresh(),this.refreshAll(),this.refreshInterval=setInterval(()=>this.refreshAll(),t)}stopAutoRefresh(){this.refreshInterval&&clearInterval(this.refreshInterval),this.refreshInterval=null}getAllFlags(){let t={};for(let[e,s]of this.cache)t[e]=s.value;return t}reset(){this.cache.clear(),this.storage.remove("flags"),this.userId=void 0,this.entityId=void 0,this.environment=void 0}destroy(){this.stopAutoRefresh()}persistCache(){this.storage.set("flags",JSON.stringify(Array.from(this.cache.values())))}};function F(r){if(r==null||typeof r!="object")return!1;let t=r;return Object.prototype.hasOwnProperty.call(t,"__proto__")||Object.prototype.hasOwnProperty.call(t,"constructor")?!1:typeof t.key=="string"&&"value"in t}var lt=32*1024,v=class{constructor(t,e,s){this.activeSurvey=null;this.http=e,this.storage=s}onDisplay(t){this.onSurveyDisplay=t}async showSurvey(t){if(typeof t!="string"||!/^[A-Za-z0-9_-]{6,64}$/.test(t))return null;let e=`survey_shown_${t}`;if(this.storage.get(e))return null;let s=await this.http.get(`/surveys/${encodeURIComponent(t)}/config`);return!s.success||!s.data||!ht(s.data)?null:(this.activeSurvey=s.data,this.storage.set(e,"1"),this.onSurveyDisplay&&this.onSurveyDisplay(s.data),s.data)}async submitResponse(t,e,s=!0){let n=0;try{n=JSON.stringify(e).length}catch{return!1}if(n>lt)return!1;let i=await this.http.post("/surveys/responses",{surveyId:t,answers:e,completed:s});return i.success&&(this.activeSurvey=null),i.success}dismiss(){this.activeSurvey=null}getActiveSurvey(){return this.activeSurvey}};function ht(r){if(r==null||typeof r!="object")return!1;let t=r;return typeof t.id!="string"||typeof t.name!="string"||!Array.isArray(t.questions)?!1:t.questions.every(ct)}function ct(r){if(r==null||typeof r!="object")return!1;let t=r;return typeof t.id=="string"&&typeof t.text=="string"}function h(r){if(!(!r||typeof r!="string"))try{let t=typeof window<"u"?window.location.href:"https://local/",e=new URL(r,t);return e.protocol==="http:"||e.protocol==="https:"?e.href:void 0}catch{return}}var pt=new Set(["b","i","u","em","strong","a","p","br","span","ul","ol","li","h1","h2","h3","h4"]),ft={a:new Set(["href","target","rel"])};function R(r){if(typeof DOMParser>"u")return"";let e=new DOMParser().parseFromString(`<div>${r}</div>`,"text/html").body.firstElementChild;return e?(V(e),e.innerHTML):""}function V(r){let t=Array.from(r.children);for(let e of t){let s=e.tagName.toLowerCase();if(!pt.has(s)){let i=document.createTextNode(e.textContent??"");e.replaceWith(i);continue}let n=ft[s]??new Set;for(let i of Array.from(e.attributes)){if(!n.has(i.name.toLowerCase())){e.removeAttribute(i.name);continue}if(i.name.toLowerCase()==="href"){let o=h(i.value);o?e.setAttribute("href",o):e.removeAttribute(i.name)}}s==="a"&&(e.setAttribute("rel","noopener noreferrer"),e.setAttribute("target","_blank")),V(e)}}var w=class{constructor(t,e,s){this.messages=[];this.pollInterval=null;this.http=e,this.storage=s}onDisplay(t){this.onMessageDisplay=t}async checkMessages(){let t=await this.http.get("/in-app-messages/active");if(!t.success||!Array.isArray(t.data))return null;this.messages=t.data.map(dt);for(let e of this.messages){let s=`iam_shown_${e.id}`;if(!this.storage.get(s))return this.storage.set(s,"1"),this.onMessageDisplay&&this.onMessageDisplay(e),e}return null}async recordAction(t,e){await this.http.post("/in-app-messages/impressions",{messageId:t,action:e})}startPolling(t=3e5){this.stopPolling(),this.checkMessages(),this.pollInterval=setInterval(()=>this.checkMessages(),t)}stopPolling(){this.pollInterval&&clearInterval(this.pollInterval),this.pollInterval=null}getMessages(){return this.messages}destroy(){this.stopPolling()}};function dt(r){return{...r,content:{...r.content,ctaUrl:h(r.content.ctaUrl),imageUrl:h(r.content.imageUrl)}}}var gt=4e3,S=class{constructor(t,e,s){this.chatbotId=null;this.messages=[];this.http=e,this.storage=s;let n=this.storage.get("chat_session_id");this.sessionId=n??g("chat_"),n||this.storage.set("chat_session_id",this.sessionId)}init(t){if(typeof t!="string"||!/^[A-Za-z0-9_-]{6,64}$/.test(t))throw new Error("[SendoraCloud] Invalid chatbotId.");this.chatbotId=t}onNewMessage(t){this.onMessage=t}async sendMessage(t,e){if(!this.chatbotId||typeof t!="string"||t.length===0||t.length>gt)return null;let s={role:"user",content:t,timestamp:new Date().toISOString()};this.messages.push(s);let n=await this.http.post("/chatbot/message",{chatbotId:this.chatbotId,sessionId:this.sessionId,message:t,userId:e});if(n.success&&n.data&&typeof n.data.reply=="string"){let i={role:"assistant",content:n.data.reply,timestamp:new Date().toISOString()};return this.messages.push(i),this.onMessage&&this.onMessage(i),i}return null}getMessages(){return[...this.messages]}newSession(){this.messages=[],this.sessionId=g("chat_"),this.storage.set("chat_session_id",this.sessionId)}getSessionId(){return this.sessionId}};var A=class{constructor(t){this.listeners=new Set;this.granted=t}isGranted(){return this.granted}grant(){this.granted||(this.granted=!0,this.emit())}revoke(){this.granted&&(this.granted=!1,this.emit())}subscribe(t){return this.listeners.add(t),()=>this.listeners.delete(t)}emit(){for(let t of this.listeners)try{t(this.granted)}catch{}}};var T="auth_access_token",b="auth_access_expires",d="auth_refresh_token",x="auth_user",mt=3e4;function P(r){if(!r||typeof r!="object")return!1;let t=r,e=t.user,s=t.tokens;return!(!e||typeof e.id!="string"||e.id.length===0||typeof e.isAnonymous!="boolean"||!s||typeof s.accessToken!="string"||s.accessToken.length===0||typeof s.refreshToken!="string"||s.refreshToken.length===0||typeof s.expiresIn!="number"||s.expiresIn<=0)}var I=class extends Error{constructor(t="An account with this email already exists"){super(t),this.name="EmailAlreadyTakenError"}},c=class extends Error{constructor(t,e){super(e),this.name="AuthError",this.code=t}},k=class{constructor(t,e,s,n,i,o){this.user=null;this.accessToken=null;this.accessExpiresAt=0;this.inflight=Promise.resolve();this.refreshInflight=null;this.http=e,this.storage=s,this.analytics=n,this.flags=i,this.chatbot=o;let u=this.storage.get(x),a=this.storage.get(T),p=this.storage.get(b);if(u&&a)try{let f=JSON.parse(u);if(typeof f.id!="string"||f.id.length===0)throw new Error("invalid cache");this.user=f,this.accessToken=a,this.accessExpiresAt=p?Number(p):0,this.analytics.identify(this.user.id)}catch{this.storage.remove(x),this.storage.remove(T),this.storage.remove(b),this.storage.remove(d)}}getCurrentUser(){return this.user}async getAccessToken(){return this.accessToken?this.accessExpiresAt&&Date.now()<this.accessExpiresAt-mt?this.accessToken:this.refreshAccessToken():null}getAccessTokenSync(){return this.accessToken}signInAnonymously(t){return this.serialize(async()=>{let e=await this.http.post("/auth-service/anonymous",{name:t?.name,metadata:t?.metadata});if(!e.success||!P(e.data))throw new c(e.error?.code??"AUTH_ERROR",e.error?.message??"Anonymous signup failed");return this.persist(e.data),e.data.user})}signUp(t,e,s){return this.serialize(async()=>{let n=this.storage.get(d);if(this.user?.isAnonymous===!0&&n){let a=await this.http.post("/auth-service/upgrade",{refreshToken:n,email:t,password:e,name:s?.name});if(a.success&&P(a.data))return this.persist(a.data),a.data.user;let p=a.error?.code??"",f=a.error?.status;throw p==="CONFLICT"||p==="EMAIL_ALREADY_TAKEN"||f===409?new I(a.error?.message):new c(p||"AUTH_ERROR",a.error?.message??"Upgrade failed")}this.user!==null&&this.user.isAnonymous===!1&&(await this.analytics.flush(),this.wipeLocalIdentity());let u=await this.http.post("/auth-service/signup",{email:t,password:e,name:s?.name,metadata:s?.metadata});if(!u.success||!P(u.data)){let a=u.error?.code??"";throw a==="CONFLICT"?new I(u.error?.message):new c(a||"AUTH_ERROR",u.error?.message??"Signup failed")}return this.persist(u.data),u.data.user})}signIn(t,e){return this.serialize(async()=>{this.user!==null&&(await this.analytics.flush(),this.wipeLocalIdentity());let n=await this.http.post("/auth-service/login",{email:t,password:e});if(!n.success||!P(n.data))throw new c(n.error?.code??"AUTH_ERROR",n.error?.message??"Login failed");return this.persist(n.data),n.data.user})}signOut(){return this.serialize(async()=>{let t=this.storage.get(d);this.wipeLocalIdentity(),t&&this.http.post("/auth-service/token/revoke",{refreshToken:t}).catch(()=>{})})}serialize(t){let e=this.inflight.then(t,t);return this.inflight=e.catch(()=>{}),e}async refreshAccessToken(){if(this.refreshInflight)return this.refreshInflight;let t=this.storage.get(d);return t?(this.refreshInflight=(async()=>{try{let e=await this.http.post("/auth-service/token/refresh",{refreshToken:t});if(!e.success||!e.data||typeof e.data!="object"||typeof e.data.accessToken!="string"||typeof e.data.refreshToken!="string"||typeof e.data.expiresIn!="number")return null;let s=e.data;return this.accessToken=s.accessToken,this.accessExpiresAt=Date.now()+s.expiresIn*1e3,this.storage.set(T,s.accessToken),this.storage.set(b,String(this.accessExpiresAt)),this.storage.set(d,s.refreshToken),s.accessToken}catch{return null}finally{this.refreshInflight=null}})(),this.refreshInflight):null}persist(t){this.user=t.user,this.accessToken=t.tokens.accessToken,this.accessExpiresAt=Date.now()+t.tokens.expiresIn*1e3,this.storage.set(x,JSON.stringify(t.user)),this.storage.set(T,t.tokens.accessToken),this.storage.set(b,String(this.accessExpiresAt)),this.storage.set(d,t.tokens.refreshToken),this.analytics.identify(t.user.id),this.flags.setContext({userId:t.user.id})}wipeLocalIdentity(){this.user=null,this.accessToken=null,this.accessExpiresAt=0,this.storage.remove(x),this.storage.remove(T),this.storage.remove(b),this.storage.remove(d),this.analytics.reset(),this.analytics.dropQueue(),this.flags.reset(),this.chatbot.newSession()}};var yt="2.2.0",U=class U{constructor(t){this.http=new _(t),this.storage=new E,this.consent=new A(t.defaultConsent===!0),this.analytics=new m(t,this.http,this.storage,this.consent),this.flags=new y(t,this.http,this.storage),this.surveys=new v(t,this.http,this.storage),this.messages=new w(t,this.http,this.storage),this.chatbot=new S(t,this.http,this.storage),this.auth=new k(t,this.http,this.storage,this.analytics,this.flags,this.chatbot),t.autoTrack!==!1&&typeof window<"u"&&this.analytics.page()}static init(t){return j(t.apiKey),new U(t)}static sanitizeHtml(t){return R(t)}static safeUrl(t){return h(t)}track(t,e){this.analytics.track(t,e)}page(t){this.analytics.page(t)}identify(t,e,s){this.analytics.identify(t,e,s),this.flags.setContext({userId:t})}reset(){this.analytics.reset(),this.flags.reset(),this.chatbot.newSession()}async flush(){await this.analytics.flush()}destroy(){this.analytics.destroy(),this.flags.destroy(),this.messages.destroy()}};U.version=yt;var M=U,vt=M;0&&(module.exports={Analytics,Auth,AuthError,Chatbot,Consent,EmailAlreadyTakenError,FeatureFlags,InAppMessaging,SendoraCloud,Surveys,ValidationError,safeUrl,sanitizeHtml});
1
+ "use strict";var F=Object.defineProperty;var ee=Object.getOwnPropertyDescriptor;var te=Object.getOwnPropertyNames;var se=Object.prototype.hasOwnProperty;var re=(r,e)=>{for(var t in e)F(r,t,{get:e[t],enumerable:!0})},ne=(r,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of te(e))!se.call(r,n)&&n!==t&&F(r,n,{get:()=>e[n],enumerable:!(s=ee(e,n))||s.enumerable});return r};var ie=r=>ne(F({},"__esModule",{value:!0}),r);var Ee={};re(Ee,{Analytics:()=>w,Auth:()=>E,AuthError:()=>u,Chatbot:()=>I,Consent:()=>k,EmailAlreadyTakenError:()=>_,FeatureFlags:()=>v,InAppMessaging:()=>S,SendoraCloud:()=>M,Surveys:()=>A,ValidationError:()=>h,default:()=>_e,safeUrl:()=>d,sanitizeHtml:()=>O});module.exports=ie(Ee);var K=/^[a-zA-Z][a-zA-Z0-9._:-]{0,127}$/;var oe=new Set(["__proto__","constructor","prototype"]),h=class extends Error{constructor(e){super(`[SendoraCloud] ${e}`),this.name="SendoraValidationError"}};function N(r){if(typeof r!="string"||!K.test(r))throw new h(`Invalid event name: must match ${K}, got "${String(r)}".`)}function $(r){if(r==null)return;if(typeof r!="object"||Array.isArray(r))throw new h("Properties must be a plain object, not an array or primitive.");let e=z(r);if(e>5)throw new h(`Properties nested too deeply (${e} levels, max 5).`);q(r);let t;try{t=JSON.stringify(r).length}catch{throw new h("Properties contain a circular reference or unserializable value.")}if(t>32768)throw new h(`Properties too large (${t} bytes, max 32768).`)}function z(r,e=0){if(e>6||r==null||typeof r!="object")return e;let t=e;for(let s of Object.keys(r)){let n=z(r[s],e+1);n>t&&(t=n)}return t}function q(r){if(!(r==null||typeof r!="object"))for(let e of Object.keys(r)){if(oe.has(e))throw new h(`Forbidden property key "${e}" (prototype pollution guard).`);q(r[e])}}var ae=/^pk_[A-Za-z0-9_-]{16,}$/;function j(r){if(!r||typeof r!="string")throw new h("apiKey is required.");if(r.startsWith("sk_")||r.startsWith("sendora_secret_"))throw new h("Secret keys must NEVER be used in browser SDK. Use a publishable key (pk_...).");if(!ae.test(r))throw new h("Invalid publishable key format. Expected pk_... (17+ chars).")}function V(r){let e=r.replace(/\/$/,"");if(!(e.startsWith("http://localhost")||e.startsWith("http://127.0.0.1"))&&!e.startsWith("https://"))throw new h("apiUrl must be https:// (only http://localhost is allowed for dev).")}var le="https://api.sendoracloud.com",ue=1e4,P=class{constructor(e){let t=(e.apiUrl??le).replace(/\/$/,"");V(t),this.apiKey=e.apiKey,this.baseUrl=t,this.debug=!!e.debug}get(e,t){return this.request("GET",e,void 0,t)}post(e,t,s){return this.request("POST",e,t,s)}delete(e,t){return this.request("DELETE",e,void 0,t)}async request(e,t,s,n){let o=`${this.baseUrl}/api/v1${t}`,i=typeof AbortController<"u"?new AbortController:null,l=i?setTimeout(()=>i.abort(),ue):null;try{let a=await fetch(o,{method:e,headers:{"Content-Type":"application/json","X-API-Key":this.apiKey,...n??{}},body:s?JSON.stringify(s):void 0,signal:i?i.signal:void 0});l&&clearTimeout(l);let c;try{c=await a.json()}catch{c={success:!1,error:{code:"PARSE_ERROR",message:`HTTP ${a.status}`}}}if(this.debug&&console.debug(`[SendoraCloud] ${e} ${t} \u2192 ${a.status}`),!a.ok)if(t.startsWith("/auth-service/")&&!this.debug)console.warn(`[SendoraCloud] ${e} ${t} \u2192 HTTP ${a.status}`);else{let B=c?.error?.code??"HTTP_ERROR",X=c?.error?.message??`HTTP ${a.status}`,H=c?.error?.details?.fieldErrors,J=H?` (${Object.entries(H).map(([G,Z])=>`${G}: ${(Z??[]).join(", ")}`).join("; ")})`:"";console.warn(`[SendoraCloud] ${e} ${t} \u2192 HTTP ${a.status} \u2014 ${B}: ${X}${J}`)}return c}catch(a){l&&clearTimeout(l);let c=a instanceof Error&&a.name==="AbortError"?"timeout":"error";return console.warn(`[SendoraCloud] ${e} ${t} network ${c}`),{success:!1,error:{code:"NETWORK_ERROR",message:c}}}}};var x="sendora_",U=class{constructor(){this.memoryStore=new Map;this.useLocalStorage=this.checkLocalStorage()}get(e){let t=x+e;if(this.useLocalStorage)try{return localStorage.getItem(t)}catch{return this.memoryStore.get(t)??null}return this.memoryStore.get(t)??null}set(e,t){let s=x+e;if(this.useLocalStorage)try{localStorage.setItem(s,t);return}catch{}this.memoryStore.set(s,t)}remove(e){let t=x+e;if(this.useLocalStorage)try{localStorage.removeItem(t)}catch{}this.memoryStore.delete(t)}clearPrefix(e){let t=x+e;if(this.useLocalStorage)try{for(let s=localStorage.length-1;s>=0;s--){let n=localStorage.key(s);n&&n.startsWith(t)&&localStorage.removeItem(n)}}catch{}for(let s of Array.from(this.memoryStore.keys()))s.startsWith(t)&&this.memoryStore.delete(s)}checkLocalStorage(){try{if(typeof localStorage>"u")return!1;let e="__sendora_test__";return localStorage.setItem(e,"1"),localStorage.removeItem(e),!0}catch{return!1}}};function y(r=""){let e=new Uint8Array(16),t=typeof globalThis<"u"&&"crypto"in globalThis?globalThis.crypto:void 0;if(!t||typeof t.getRandomValues!="function")throw new Error("[SendoraCloud] crypto.getRandomValues unavailable \u2014 refusing to mint IDs without a CSPRNG");t.getRandomValues(e);let s="";for(let n of e)s+=n.toString(16).padStart(2,"0");return r+s}var W="2.2.0",Y=500,ce=10,he=6e4,w=class{constructor(e,t,s,n){this.queue=[];this.timer=null;this.userId=null;this.identityToken=null;this.consecutiveFailures=0;this.nextFlushAfter=0;this.http=t,this.storage=s,this.consent=n,this.projectId=e.projectId,this.flushInterval=e.flushInterval??5e3,this.maxQueueSize=e.maxQueueSize??20,this.persistUserId=!!e.persistUserId,this.stripQueryParams=e.stripQueryParams!==!1;let o=this.storage.get("anonymous_id");if(o?this.anonymousId=o:(this.anonymousId=y("anon_"),this.storage.set("anonymous_id",this.anonymousId)),this.persistUserId){let i=this.storage.get("user_id");i&&(this.userId=i)}this.timer=setInterval(()=>this.flush(),this.flushInterval),typeof window<"u"&&window.addEventListener("beforeunload",()=>this.flush()),this.consent.subscribe(i=>{i&&this.flush()})}track(e,t){N(e),t&&$(t),this.queue.length>=Y&&this.queue.shift(),this.queue.push({eventType:e,properties:t??{},userId:this.userId??void 0,anonymousId:this.anonymousId,timestamp:new Date().toISOString(),identityToken:this.identityToken??void 0}),this.queue.length>=this.maxQueueSize&&this.flush()}page(e){if(typeof window>"u")return;let t=this.stripQueryParams?window.location.origin+window.location.pathname:window.location.href;this.track("page.viewed",{url:t,path:window.location.pathname,title:document.title,referrer:document.referrer||void 0,...e})}identify(e,t,s){if(typeof e!="string"||e.length===0||e.length>256)throw new Error("[SendoraCloud] userId must be a 1-256 char string.");this.userId=e,this.identityToken=s?.identityToken??null,this.persistUserId&&this.storage.set("user_id",e),this.track("user.identified",{...t})}reset(){this.userId=null,this.identityToken=null,this.storage.remove("user_id"),this.anonymousId=y("anon_"),this.storage.set("anonymous_id",this.anonymousId),this.storage.clearPrefix("iam_shown_"),this.storage.clearPrefix("survey_shown_")}dropQueue(){this.queue=[]}async flush(){if(this.queue.length===0||!this.consent.isGranted()||Date.now()<this.nextFlushAfter)return;let e=this.queue.splice(0,100),t=e.map(i=>({projectId:this.projectId,module:"custom",eventType:i.eventType,userId:i.userId,anonymousId:i.anonymousId,identityToken:i.identityToken,timestamp:i.timestamp??new Date().toISOString(),properties:i.properties??{},context:this.getContext(),consent:["analytics"]}));if((await this.http.post("/events/batch",{events:t})).success){this.consecutiveFailures=0;return}if(this.consecutiveFailures++,this.consecutiveFailures>ce)return;let n=Math.min(he,1e3*2**this.consecutiveFailures);this.nextFlushAfter=Date.now()+n;let o=Math.max(0,Y-this.queue.length);o>0&&this.queue.unshift(...e.slice(0,o))}destroy(){this.timer&&clearInterval(this.timer),this.timer=null,this.flush()}getContext(){return typeof window>"u"?{sdk:{name:"@sendoracloud/sdk-web",version:W}}:{page:{url:this.stripQueryParams?window.location.origin+window.location.pathname:window.location.href,path:window.location.pathname,title:document.title},referrer:document.referrer||void 0,locale:typeof navigator<"u"?navigator.language:void 0,sdk:{name:"@sendoracloud/sdk-web",version:W}}}};var v=class{constructor(e,t,s){this.cache=new Map;this.refreshInterval=null;this.http=t,this.storage=s;let n=this.storage.get("flags");if(n)try{let o=JSON.parse(n);if(Array.isArray(o))for(let i of o)D(i)&&this.cache.set(i.key,i)}catch{}}setContext(e){this.userId=e.userId,this.entityId=e.entityId,this.environment=e.environment}async getFlag(e){if(typeof e!="string"||e.length===0||e.length>128)return null;let t=this.cache.get(e);if(t)return t.value;let s=await this.http.post("/feature-flags/evaluate",{key:e,userId:this.userId,entityId:this.entityId,environment:this.environment});return s.success&&s.data&&D(s.data)?(this.cache.set(e,s.data),this.persistCache(),s.data.value):null}async isEnabled(e,t=!1){let s=await this.getFlag(e);return typeof s=="boolean"?s:t}async refreshAll(){let e=await this.http.post("/feature-flags/evaluate-all",{userId:this.userId,entityId:this.entityId,environment:this.environment});if(e.success&&Array.isArray(e.data)){this.cache.clear();for(let t of e.data)D(t)&&this.cache.set(t.key,t);this.persistCache()}}startAutoRefresh(e=6e4){this.stopAutoRefresh(),this.refreshAll(),this.refreshInterval=setInterval(()=>this.refreshAll(),e)}stopAutoRefresh(){this.refreshInterval&&clearInterval(this.refreshInterval),this.refreshInterval=null}getAllFlags(){let e={};for(let[t,s]of this.cache)e[t]=s.value;return e}reset(){this.cache.clear(),this.storage.remove("flags"),this.userId=void 0,this.entityId=void 0,this.environment=void 0}destroy(){this.stopAutoRefresh()}persistCache(){this.storage.set("flags",JSON.stringify(Array.from(this.cache.values())))}};function D(r){if(r==null||typeof r!="object")return!1;let e=r;return Object.prototype.hasOwnProperty.call(e,"__proto__")||Object.prototype.hasOwnProperty.call(e,"constructor")?!1:typeof e.key=="string"&&"value"in e}var de=32*1024,A=class{constructor(e,t,s){this.activeSurvey=null;this.http=t,this.storage=s}onDisplay(e){this.onSurveyDisplay=e}async showSurvey(e){if(typeof e!="string"||!/^[A-Za-z0-9_-]{6,64}$/.test(e))return null;let t=`survey_shown_${e}`;if(this.storage.get(t))return null;let s=await this.http.get(`/surveys/${encodeURIComponent(e)}/config`);return!s.success||!s.data||!pe(s.data)?null:(this.activeSurvey=s.data,this.storage.set(t,"1"),this.onSurveyDisplay&&this.onSurveyDisplay(s.data),s.data)}async submitResponse(e,t,s=!0){let n=0;try{n=JSON.stringify(t).length}catch{return!1}if(n>de)return!1;let o=await this.http.post("/surveys/responses",{surveyId:e,answers:t,completed:s});return o.success&&(this.activeSurvey=null),o.success}dismiss(){this.activeSurvey=null}getActiveSurvey(){return this.activeSurvey}};function pe(r){if(r==null||typeof r!="object")return!1;let e=r;return typeof e.id!="string"||typeof e.name!="string"||!Array.isArray(e.questions)?!1:e.questions.every(ge)}function ge(r){if(r==null||typeof r!="object")return!1;let e=r;return typeof e.id=="string"&&typeof e.text=="string"}function d(r){if(!(!r||typeof r!="string"))try{let e=typeof window<"u"?window.location.href:"https://local/",t=new URL(r,e);return t.protocol==="http:"||t.protocol==="https:"?t.href:void 0}catch{return}}var fe=new Set(["b","i","u","em","strong","a","p","br","span","ul","ol","li","h1","h2","h3","h4"]),me={a:new Set(["href","target","rel"])};function O(r){if(typeof DOMParser>"u")return"";let t=new DOMParser().parseFromString(`<div>${r}</div>`,"text/html").body.firstElementChild;return t?(Q(t),t.innerHTML):""}function Q(r){let e=Array.from(r.children);for(let t of e){let s=t.tagName.toLowerCase();if(!fe.has(s)){let o=document.createTextNode(t.textContent??"");t.replaceWith(o);continue}let n=me[s]??new Set;for(let o of Array.from(t.attributes)){if(!n.has(o.name.toLowerCase())){t.removeAttribute(o.name);continue}if(o.name.toLowerCase()==="href"){let i=d(o.value);i?t.setAttribute("href",i):t.removeAttribute(o.name)}}s==="a"&&(t.setAttribute("rel","noopener noreferrer"),t.setAttribute("target","_blank")),Q(t)}}var S=class{constructor(e,t,s){this.messages=[];this.pollInterval=null;this.http=t,this.storage=s}onDisplay(e){this.onMessageDisplay=e}async checkMessages(){let e=await this.http.get("/in-app-messages/active");if(!e.success||!Array.isArray(e.data))return null;this.messages=e.data.map(ye);for(let t of this.messages){let s=`iam_shown_${t.id}`;if(!this.storage.get(s))return this.storage.set(s,"1"),this.onMessageDisplay&&this.onMessageDisplay(t),t}return null}async recordAction(e,t){await this.http.post("/in-app-messages/impressions",{messageId:e,action:t})}startPolling(e=3e5){this.stopPolling(),this.checkMessages(),this.pollInterval=setInterval(()=>this.checkMessages(),e)}stopPolling(){this.pollInterval&&clearInterval(this.pollInterval),this.pollInterval=null}getMessages(){return this.messages}destroy(){this.stopPolling()}};function ye(r){return{...r,content:{...r.content,ctaUrl:d(r.content.ctaUrl),imageUrl:d(r.content.imageUrl)}}}var we=4e3,I=class{constructor(e,t,s){this.chatbotId=null;this.messages=[];this.http=t,this.storage=s;let n=this.storage.get("chat_session_id");this.sessionId=n??y("chat_"),n||this.storage.set("chat_session_id",this.sessionId)}init(e){if(typeof e!="string"||!/^[A-Za-z0-9_-]{6,64}$/.test(e))throw new Error("[SendoraCloud] Invalid chatbotId.");this.chatbotId=e}onNewMessage(e){this.onMessage=e}async sendMessage(e,t){if(!this.chatbotId||typeof e!="string"||e.length===0||e.length>we)return null;let s={role:"user",content:e,timestamp:new Date().toISOString()};this.messages.push(s);let n=await this.http.post("/chatbot/message",{chatbotId:this.chatbotId,sessionId:this.sessionId,message:e,userId:t});if(n.success&&n.data&&typeof n.data.reply=="string"){let o={role:"assistant",content:n.data.reply,timestamp:new Date().toISOString()};return this.messages.push(o),this.onMessage&&this.onMessage(o),o}return null}getMessages(){return[...this.messages]}newSession(){this.messages=[],this.sessionId=y("chat_"),this.storage.set("chat_session_id",this.sessionId)}getSessionId(){return this.sessionId}};var k=class{constructor(e){this.listeners=new Set;this.granted=e}isGranted(){return this.granted}grant(){this.granted||(this.granted=!0,this.emit())}revoke(){this.granted&&(this.granted=!1,this.emit())}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}emit(){for(let e of this.listeners)try{e(this.granted)}catch{}}};var b="auth_access_token",T="auth_access_expires",p="auth_refresh_token",C="auth_user",ve=3e4;function g(r){if(!r||typeof r!="object")return!1;let e=r,t=e.user,s=e.tokens;return!(!t||typeof t.id!="string"||t.id.length===0||typeof t.isAnonymous!="boolean"||!s||typeof s.accessToken!="string"||s.accessToken.length===0||typeof s.refreshToken!="string"||s.refreshToken.length===0||typeof s.expiresIn!="number"||s.expiresIn<=0)}var _=class extends Error{constructor(e="An account with this email already exists"){super(e),this.name="EmailAlreadyTakenError"}},u=class extends Error{constructor(e,t){super(t),this.name="AuthError",this.code=e}},E=class{constructor(e,t,s,n,o,i){this.user=null;this.accessToken=null;this.accessExpiresAt=0;this.inflight=Promise.resolve();this.refreshInflight=null;this.http=t,this.storage=s,this.analytics=n,this.flags=o,this.chatbot=i;let l=this.storage.get(C),a=this.storage.get(b),c=this.storage.get(T);if(l&&a)try{let m=JSON.parse(l);if(typeof m.id!="string"||m.id.length===0)throw new Error("invalid cache");this.user=m,this.accessToken=a,this.accessExpiresAt=c?Number(c):0,this.analytics.identify(this.user.id)}catch{this.storage.remove(C),this.storage.remove(b),this.storage.remove(T),this.storage.remove(p)}}getCurrentUser(){return this.user}async getAccessToken(){return this.accessToken?this.accessExpiresAt&&Date.now()<this.accessExpiresAt-ve?this.accessToken:this.refreshAccessToken():null}getAccessTokenSync(){return this.accessToken}signInAnonymously(e){return this.serialize(async()=>{let t=await this.http.post("/auth-service/anonymous",{name:e?.name,metadata:e?.metadata});if(!t.success||!g(t.data))throw new u(t.error?.code??"AUTH_ERROR",t.error?.message??"Anonymous signup failed");return this.persist(t.data),t.data.user})}signUp(e,t,s){return this.serialize(async()=>{let n=this.storage.get(p);if(this.user?.isAnonymous===!0&&n){let a=await this.http.post("/auth-service/upgrade",{refreshToken:n,email:e,password:t,name:s?.name});if(a.success&&g(a.data))return this.persist(a.data),a.data.user;let c=a.error?.code??"",m=a.error?.status;throw c==="CONFLICT"||c==="EMAIL_ALREADY_TAKEN"||m===409?new _(a.error?.message):new u(c||"AUTH_ERROR",a.error?.message??"Upgrade failed")}this.user!==null&&this.user.isAnonymous===!1&&(await this.analytics.flush(),this.wipeLocalIdentity());let l=await this.http.post("/auth-service/signup",{email:e,password:t,name:s?.name,metadata:s?.metadata});if(!l.success||!g(l.data)){let a=l.error?.code??"";throw a==="CONFLICT"?new _(l.error?.message):new u(a||"AUTH_ERROR",l.error?.message??"Signup failed")}return this.persist(l.data),l.data.user})}signIn(e,t){return this.serialize(async()=>{this.user!==null&&(await this.analytics.flush(),this.wipeLocalIdentity());let n=await this.http.post("/auth-service/login",{email:e,password:t});if(!n.success||!n.data)throw new u(n.error?.code??"AUTH_ERROR",n.error?.message??"Login failed");let o=n.data;if(o.mfaRequired===!0&&typeof o.mfaChallengeToken=="string"){let i=o.user;return{mfaRequired:!0,mfaChallengeToken:o.mfaChallengeToken,userId:i?.id??""}}if(!g(n.data))throw new u(n.error?.code??"AUTH_ERROR",n.error?.message??"Login failed");return this.persist(n.data),n.data.user})}challengeMfa(e,t){return this.serialize(async()=>{let s=await this.http.post("/auth-service/mfa/challenge",{challengeToken:e,code:t});if(!s.success||!g(s.data))throw new u(s.error?.code??"MFA_CHALLENGE_FAILED",s.error?.message??"Invalid MFA code");return this.persist(s.data),s.data.user})}async sendMagicLink(e){let t=await this.http.post("/auth-service/magic-link/request",{email:e});if(!t.success)throw new u(t.error?.code??"MAGIC_LINK_REQUEST_FAILED",t.error?.message??"Failed to send link")}async sendEmailOtp(e){let t=await this.http.post("/auth-service/email-otp/request",{email:e});if(!t.success)throw new u(t.error?.code??"EMAIL_OTP_REQUEST_FAILED",t.error?.message??"Failed to send code")}verifyEmailOtp(e,t){return this.serialize(async()=>{this.user!==null&&(await this.analytics.flush(),this.wipeLocalIdentity());let n=await this.http.post("/auth-service/email-otp/verify",{email:e,code:t});if(!n.success||!g(n.data))throw new u(n.error?.code??"EMAIL_OTP_INVALID",n.error?.message??"Wrong or expired code");return this.persist(n.data),n.data.user})}verifyMagicLink(e){return this.serialize(async()=>{this.user!==null&&(await this.analytics.flush(),this.wipeLocalIdentity());let s=await this.http.post("/auth-service/magic-link/verify",{token:e});if(!s.success||!g(s.data))throw new u(s.error?.code??"MAGIC_LINK_INVALID",s.error?.message??"Invalid or expired magic link");return this.persist(s.data),s.data.user})}async enrollMfa(){let e=await this.http.post("/auth-service/mfa/enroll/start",{},this.bearerHeaders());if(!e.success||!e.data)throw new u(e.error?.code??"MFA_ENROLL_FAILED",e.error?.message??"MFA enrollment failed");return e.data}async confirmMfa(e){let t=await this.http.post("/auth-service/mfa/enroll/confirm",{code:e},this.bearerHeaders());return!!(t.success&&t.data?.confirmed)}async disableMfa(){await this.http.post("/auth-service/mfa/disable",{},this.bearerHeaders())}async registerPasskey(e){if(typeof navigator>"u"||!navigator.credentials)throw new u("PASSKEY_UNSUPPORTED","WebAuthn not supported in this environment");let t=await this.http.post("/auth-service/passkeys/register/start",{},this.bearerHeaders());if(!t.success||!t.data)throw new u(t.error?.code??"PASSKEY_START_FAILED",t.error?.message??"Passkey start failed");let s=Ae(t.data),n;try{let l=await navigator.credentials.create({publicKey:s});if(!l)throw new Error("create returned null");n=l}catch(l){throw new u("PASSKEY_USER_CANCELLED",l instanceof Error?l.message:"User cancelled passkey registration")}let o=Ie(n),i=await this.http.post("/auth-service/passkeys/register/finish",{response:o,name:e},this.bearerHeaders());if(!i.success||!i.data)throw new u(i.error?.code??"PASSKEY_FINISH_FAILED",i.error?.message??"Passkey verification failed");return i.data}signInWithPasskey(e){return this.serialize(async()=>{if(typeof navigator>"u"||!navigator.credentials)throw new u("PASSKEY_UNSUPPORTED","WebAuthn not supported in this environment");this.user!==null&&(await this.analytics.flush(),this.wipeLocalIdentity());let s=await this.http.post("/auth-service/passkeys/authenticate/start",{userId:e??null});if(!s.success||!s.data)throw new u(s.error?.code??"PASSKEY_START_FAILED",s.error?.message??"Passkey start failed");let n=Se(s.data),o;try{let a=await navigator.credentials.get({publicKey:n});if(!a)throw new Error("get returned null");o=a}catch(a){throw new u("PASSKEY_USER_CANCELLED",a instanceof Error?a.message:"User cancelled passkey signin")}let i=be(o),l=await this.http.post("/auth-service/passkeys/authenticate/finish",{response:i,userId:e??null});if(!l.success||!g(l.data))throw new u(l.error?.code??"PASSKEY_AUTH_FAILED",l.error?.message??"Passkey signin failed");return this.persist(l.data),l.data.user})}async listPasskeys(){let e=await this.http.get("/auth-service/passkeys",this.bearerHeaders());return!e.success||!Array.isArray(e.data)?[]:e.data}async deletePasskey(e){await this.http.delete(`/auth-service/passkeys/${e}`,this.bearerHeaders())}async listMySessions(){let e=await this.http.get("/auth-service/sessions/me",this.bearerHeaders());return!e.success||!Array.isArray(e.data)?[]:e.data}async revokeSession(e){await this.http.delete(`/auth-service/sessions/me/${e}`,this.bearerHeaders())}async revokeAllSessions(){await this.http.delete("/auth-service/sessions/me",this.bearerHeaders())}async startSso(e){let t=await this.http.post("/auth-service/sso/oidc/start",{returnTo:e});if(!t.success||!t.data)throw new u(t.error?.code??"SSO_START_FAILED",t.error?.message??"SSO start failed");return t.data}async signInWithSso(e){let t=e??(typeof window<"u"?window.location.origin:"");if(!t)throw new u("SSO_NO_RETURN_TO","returnTo required outside browser context");let s=await this.startSso(t);typeof window<"u"&&(window.location.href=s.authorizationUrl)}consumeSsoTokenFromUrl(){return this.serialize(async()=>{if(typeof window>"u")return null;let e=window.location.hash.replace(/^#/,"");if(!e)return null;let t=new URLSearchParams(e),s=t.get("sendora_oidc_error");if(s)throw window.history.replaceState(null,"",window.location.pathname+window.location.search),new u("SSO_FAILED",decodeURIComponent(s));let n=t.get("sendora_oidc_token");if(!n)return null;window.history.replaceState(null,"",window.location.pathname+window.location.search),this.user!==null&&(await this.analytics.flush(),this.wipeLocalIdentity());let o=await this.http.post("/auth-service/token/refresh",{refreshToken:n});if(!o.success||!o.data)throw new u(o.error?.code??"SSO_EXCHANGE_FAILED",o.error?.message??"Could not exchange SSO token");let i=o.data;this.accessToken=i.accessToken,this.accessExpiresAt=Date.now()+i.expiresIn*1e3,this.storage.set(b,i.accessToken),this.storage.set(T,String(this.accessExpiresAt)),this.storage.set(p,i.refreshToken);let l=ke(i.accessToken),a={id:l?.sub??"",email:l?.email??null,emailVerified:l?.email_verified??!0,name:l?.name??null,isAnonymous:!1};return this.user=a,this.storage.set(C,JSON.stringify(a)),this.analytics.identify(a.id),this.flags.setContext({userId:a.id}),a})}bearerHeaders(){if(!this.accessToken)throw new u("NOT_SIGNED_IN","Not signed in");return{Authorization:`Bearer ${this.accessToken}`}}signOut(){return this.serialize(async()=>{let e=this.storage.get(p);this.wipeLocalIdentity(),e&&this.http.post("/auth-service/token/revoke",{refreshToken:e}).catch(()=>{})})}serialize(e){let t=this.inflight.then(e,e);return this.inflight=t.catch(()=>{}),t}async refreshAccessToken(){if(this.refreshInflight)return this.refreshInflight;let e=this.storage.get(p);return e?(this.refreshInflight=(async()=>{try{let t=await this.http.post("/auth-service/token/refresh",{refreshToken:e});if(!t.success||!t.data||typeof t.data!="object"||typeof t.data.accessToken!="string"||typeof t.data.refreshToken!="string"||typeof t.data.expiresIn!="number")return null;let s=t.data;return this.accessToken=s.accessToken,this.accessExpiresAt=Date.now()+s.expiresIn*1e3,this.storage.set(b,s.accessToken),this.storage.set(T,String(this.accessExpiresAt)),this.storage.set(p,s.refreshToken),s.accessToken}catch{return null}finally{this.refreshInflight=null}})(),this.refreshInflight):null}persist(e){this.user=e.user,this.accessToken=e.tokens.accessToken,this.accessExpiresAt=Date.now()+e.tokens.expiresIn*1e3,this.storage.set(C,JSON.stringify(e.user)),this.storage.set(b,e.tokens.accessToken),this.storage.set(T,String(this.accessExpiresAt)),this.storage.set(p,e.tokens.refreshToken),this.analytics.identify(e.user.id),this.flags.setContext({userId:e.user.id})}wipeLocalIdentity(){this.user=null,this.accessToken=null,this.accessExpiresAt=0,this.storage.remove(C),this.storage.remove(b),this.storage.remove(T),this.storage.remove(p),this.analytics.reset(),this.analytics.dropQueue(),this.flags.reset(),this.chatbot.newSession()}};function R(r){let e=(4-r.length%4)%4,t=(r+"===".slice(0,e)).replace(/-/g,"+").replace(/_/g,"/"),s=atob(t),n=new ArrayBuffer(s.length),o=new Uint8Array(n);for(let i=0;i<s.length;i++)o[i]=s.charCodeAt(i);return n}function f(r){let e=r instanceof Uint8Array?r:new Uint8Array(r),t="";for(let s=0;s<e.length;s++)t+=String.fromCharCode(e[s]);return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function Ae(r){return{challenge:R(r.challenge),rp:r.rp,user:{id:R(r.user.id),name:r.user.name,displayName:r.user.displayName},pubKeyCredParams:r.pubKeyCredParams,timeout:r.timeout,attestation:r.attestation,excludeCredentials:r.excludeCredentials?.map(e=>({id:R(e.id),type:"public-key",transports:e.transports})),authenticatorSelection:r.authenticatorSelection}}function Se(r){return{challenge:R(r.challenge),timeout:r.timeout,rpId:r.rpId,allowCredentials:r.allowCredentials?.map(e=>({id:R(e.id),type:"public-key",transports:e.transports})),userVerification:r.userVerification}}function Ie(r){let e=r.response;return{id:r.id,rawId:f(r.rawId),type:r.type,response:{clientDataJSON:f(e.clientDataJSON),attestationObject:f(e.attestationObject),transports:typeof e.getTransports=="function"?e.getTransports():void 0},clientExtensionResults:r.getClientExtensionResults()}}function ke(r){try{let e=r.split(".");if(e.length!==3)return null;let t=e[1].replace(/-/g,"+").replace(/_/g,"/"),s=atob(t+"===".slice(0,(4-t.length%4)%4));return JSON.parse(s)}catch{return null}}function be(r){let e=r.response;return{id:r.id,rawId:f(r.rawId),type:r.type,response:{clientDataJSON:f(e.clientDataJSON),authenticatorData:f(e.authenticatorData),signature:f(e.signature),userHandle:e.userHandle?f(e.userHandle):null},clientExtensionResults:r.getClientExtensionResults()}}var Te="2.2.0",L=class L{constructor(e){this.http=new P(e),this.storage=new U,this.consent=new k(e.defaultConsent===!0),this.analytics=new w(e,this.http,this.storage,this.consent),this.flags=new v(e,this.http,this.storage),this.surveys=new A(e,this.http,this.storage),this.messages=new S(e,this.http,this.storage),this.chatbot=new I(e,this.http,this.storage),this.auth=new E(e,this.http,this.storage,this.analytics,this.flags,this.chatbot),e.autoTrack!==!1&&typeof window<"u"&&this.analytics.page()}static init(e){return j(e.apiKey),new L(e)}static sanitizeHtml(e){return O(e)}static safeUrl(e){return d(e)}track(e,t){this.analytics.track(e,t)}page(e){this.analytics.page(e)}identify(e,t,s){this.analytics.identify(e,t,s),this.flags.setContext({userId:e})}reset(){this.analytics.reset(),this.flags.reset(),this.chatbot.newSession()}async flush(){await this.analytics.flush()}destroy(){this.analytics.destroy(),this.flags.destroy(),this.messages.destroy()}};L.version=Te;var M=L,_e=M;0&&(module.exports={Analytics,Auth,AuthError,Chatbot,Consent,EmailAlreadyTakenError,FeatureFlags,InAppMessaging,SendoraCloud,Surveys,ValidationError,safeUrl,sanitizeHtml});
package/dist/index.d.cts CHANGED
@@ -105,8 +105,9 @@ declare class HttpClient {
105
105
  private baseUrl;
106
106
  private debug;
107
107
  constructor(config: SendoraConfig);
108
- get<T>(path: string): Promise<ApiResponse<T>>;
109
- post<T>(path: string, body?: unknown): Promise<ApiResponse<T>>;
108
+ get<T>(path: string, extraHeaders?: Record<string, string>): Promise<ApiResponse<T>>;
109
+ post<T>(path: string, body?: unknown, extraHeaders?: Record<string, string>): Promise<ApiResponse<T>>;
110
+ delete<T = unknown>(path: string, extraHeaders?: Record<string, string>): Promise<ApiResponse<T>>;
110
111
  private request;
111
112
  }
112
113
 
@@ -332,7 +333,130 @@ declare class Auth {
332
333
  name?: string;
333
334
  metadata?: Record<string, unknown>;
334
335
  }): Promise<AuthUser>;
335
- signIn(email: string, password: string): Promise<AuthUser>;
336
+ /**
337
+ * Sign in with email + password. When the account has confirmed
338
+ * MFA, returns a `mfaRequired` discriminator instead of the user
339
+ * — caller must then call `challengeMfa(challengeToken, code)`
340
+ * with the TOTP / recovery code to mint a real session. Pre-MFA
341
+ * accounts get a normal `AuthUser` back.
342
+ */
343
+ signIn(email: string, password: string): Promise<AuthUser | {
344
+ mfaRequired: true;
345
+ mfaChallengeToken: string;
346
+ userId: string;
347
+ }>;
348
+ /**
349
+ * Exchange the `mfaChallengeToken` from `signIn` for a real
350
+ * session by submitting a 6-digit TOTP code or a 17-char recovery
351
+ * code from the user's authenticator app.
352
+ */
353
+ challengeMfa(mfaChallengeToken: string, code: string): Promise<AuthUser>;
354
+ /**
355
+ * Send a magic-link email to `email`. Caller's app should display
356
+ * "check your inbox" copy after success. Always resolves
357
+ * successfully regardless of whether the email matches a known
358
+ * user (the backend doesn't reveal account existence).
359
+ */
360
+ sendMagicLink(email: string): Promise<void>;
361
+ /**
362
+ * Send a 6-digit email OTP. Cross-device-friendly alternative to
363
+ * magic-link — the user types the code into the same app they
364
+ * started signing in on.
365
+ */
366
+ sendEmailOtp(email: string): Promise<void>;
367
+ /**
368
+ * Verify a typed 6-digit code + the email it was sent to. Mints
369
+ * a session on success. First-time users are auto-created with
370
+ * `emailVerified=true`.
371
+ */
372
+ verifyEmailOtp(email: string, code: string): Promise<AuthUser>;
373
+ /**
374
+ * Verify a magic-link token (extracted from the URL query string of
375
+ * the page the link points to) and mint a session. First-time
376
+ * users are auto-created with `emailVerified=true`.
377
+ */
378
+ verifyMagicLink(token: string): Promise<AuthUser>;
379
+ /**
380
+ * Begin MFA enrollment. Returns the otpauth:// URL (render as a
381
+ * QR code) + recovery codes (display + offer download — these are
382
+ * shown ONCE). Subsequent calls rotate the secret.
383
+ */
384
+ enrollMfa(): Promise<{
385
+ secret: string;
386
+ otpauthUrl: string;
387
+ recoveryCodes: string[];
388
+ }>;
389
+ /**
390
+ * Confirm MFA enrollment by submitting the first TOTP code from the
391
+ * user's authenticator app. Until this succeeds, login does NOT
392
+ * challenge MFA.
393
+ */
394
+ confirmMfa(code: string): Promise<boolean>;
395
+ /** Disable MFA for the current user (requires a fresh access token). */
396
+ disableMfa(): Promise<void>;
397
+ /**
398
+ * Register a new passkey. Triggers the platform's WebAuthn UI
399
+ * (Touch ID / Face ID / Windows Hello / hardware key). Only works
400
+ * in browser contexts where `navigator.credentials.create` exists.
401
+ */
402
+ registerPasskey(name?: string): Promise<{
403
+ id: string;
404
+ credentialId: string;
405
+ }>;
406
+ /**
407
+ * Sign in with a passkey. Browser shows the credential picker UI;
408
+ * `userId` is optional — when omitted, uses the resident-key flow
409
+ * so the user picks their account from the prompt.
410
+ */
411
+ signInWithPasskey(userId?: string | null): Promise<AuthUser>;
412
+ listPasskeys(): Promise<Array<{
413
+ id: string;
414
+ name: string | null;
415
+ transports: string[];
416
+ lastUsedAt: string | null;
417
+ createdAt: string;
418
+ }>>;
419
+ deletePasskey(passkeyId: string): Promise<void>;
420
+ listMySessions(): Promise<Array<{
421
+ id: string;
422
+ deviceInfo: string | null;
423
+ ipHash: string | null;
424
+ lastUsedAt: string | null;
425
+ createdAt: string;
426
+ }>>;
427
+ revokeSession(sessionId: string): Promise<void>;
428
+ revokeAllSessions(): Promise<void>;
429
+ /**
430
+ * Start OIDC sign-in. Returns the IdP's authorization URL — caller
431
+ * either redirects the current tab to it (`window.location = url`)
432
+ * or opens it in a popup. After the user authenticates, the IdP
433
+ * redirects through the Sendora callback back to `returnTo` with
434
+ * a `#sendora_oidc_token=...` URL fragment. Call
435
+ * `consumeSsoTokenFromUrl()` on the returnTo page to mint a session.
436
+ */
437
+ startSso(returnTo: string): Promise<{
438
+ authorizationUrl: string;
439
+ state: string;
440
+ }>;
441
+ /**
442
+ * Convenience — redirects the current tab to the IdP. Most apps
443
+ * just want this; popup flows are advanced.
444
+ */
445
+ signInWithSso(returnTo?: string): Promise<void>;
446
+ /**
447
+ * Read the OIDC fragment off `window.location.hash`, exchange it
448
+ * for a session, and clean up the URL. Returns the AuthUser on
449
+ * success, null when no fragment present, throws on
450
+ * `sendora_oidc_error=...`. Call this once on the returnTo page
451
+ * (typically from a top-level effect / page-load hook).
452
+ */
453
+ consumeSsoTokenFromUrl(): Promise<AuthUser | null>;
454
+ /**
455
+ * Build the Authorization header for end-user-authenticated routes.
456
+ * Throws if there's no current session — caller should handle as
457
+ * an unauthenticated state.
458
+ */
459
+ private bearerHeaders;
336
460
  /**
337
461
  * Sign out: wipe local identity FIRST so the user is logged out on
338
462
  * device even if the revoke network call hangs (airplane mode, 5xx,
package/dist/index.d.ts CHANGED
@@ -105,8 +105,9 @@ declare class HttpClient {
105
105
  private baseUrl;
106
106
  private debug;
107
107
  constructor(config: SendoraConfig);
108
- get<T>(path: string): Promise<ApiResponse<T>>;
109
- post<T>(path: string, body?: unknown): Promise<ApiResponse<T>>;
108
+ get<T>(path: string, extraHeaders?: Record<string, string>): Promise<ApiResponse<T>>;
109
+ post<T>(path: string, body?: unknown, extraHeaders?: Record<string, string>): Promise<ApiResponse<T>>;
110
+ delete<T = unknown>(path: string, extraHeaders?: Record<string, string>): Promise<ApiResponse<T>>;
110
111
  private request;
111
112
  }
112
113
 
@@ -332,7 +333,130 @@ declare class Auth {
332
333
  name?: string;
333
334
  metadata?: Record<string, unknown>;
334
335
  }): Promise<AuthUser>;
335
- signIn(email: string, password: string): Promise<AuthUser>;
336
+ /**
337
+ * Sign in with email + password. When the account has confirmed
338
+ * MFA, returns a `mfaRequired` discriminator instead of the user
339
+ * — caller must then call `challengeMfa(challengeToken, code)`
340
+ * with the TOTP / recovery code to mint a real session. Pre-MFA
341
+ * accounts get a normal `AuthUser` back.
342
+ */
343
+ signIn(email: string, password: string): Promise<AuthUser | {
344
+ mfaRequired: true;
345
+ mfaChallengeToken: string;
346
+ userId: string;
347
+ }>;
348
+ /**
349
+ * Exchange the `mfaChallengeToken` from `signIn` for a real
350
+ * session by submitting a 6-digit TOTP code or a 17-char recovery
351
+ * code from the user's authenticator app.
352
+ */
353
+ challengeMfa(mfaChallengeToken: string, code: string): Promise<AuthUser>;
354
+ /**
355
+ * Send a magic-link email to `email`. Caller's app should display
356
+ * "check your inbox" copy after success. Always resolves
357
+ * successfully regardless of whether the email matches a known
358
+ * user (the backend doesn't reveal account existence).
359
+ */
360
+ sendMagicLink(email: string): Promise<void>;
361
+ /**
362
+ * Send a 6-digit email OTP. Cross-device-friendly alternative to
363
+ * magic-link — the user types the code into the same app they
364
+ * started signing in on.
365
+ */
366
+ sendEmailOtp(email: string): Promise<void>;
367
+ /**
368
+ * Verify a typed 6-digit code + the email it was sent to. Mints
369
+ * a session on success. First-time users are auto-created with
370
+ * `emailVerified=true`.
371
+ */
372
+ verifyEmailOtp(email: string, code: string): Promise<AuthUser>;
373
+ /**
374
+ * Verify a magic-link token (extracted from the URL query string of
375
+ * the page the link points to) and mint a session. First-time
376
+ * users are auto-created with `emailVerified=true`.
377
+ */
378
+ verifyMagicLink(token: string): Promise<AuthUser>;
379
+ /**
380
+ * Begin MFA enrollment. Returns the otpauth:// URL (render as a
381
+ * QR code) + recovery codes (display + offer download — these are
382
+ * shown ONCE). Subsequent calls rotate the secret.
383
+ */
384
+ enrollMfa(): Promise<{
385
+ secret: string;
386
+ otpauthUrl: string;
387
+ recoveryCodes: string[];
388
+ }>;
389
+ /**
390
+ * Confirm MFA enrollment by submitting the first TOTP code from the
391
+ * user's authenticator app. Until this succeeds, login does NOT
392
+ * challenge MFA.
393
+ */
394
+ confirmMfa(code: string): Promise<boolean>;
395
+ /** Disable MFA for the current user (requires a fresh access token). */
396
+ disableMfa(): Promise<void>;
397
+ /**
398
+ * Register a new passkey. Triggers the platform's WebAuthn UI
399
+ * (Touch ID / Face ID / Windows Hello / hardware key). Only works
400
+ * in browser contexts where `navigator.credentials.create` exists.
401
+ */
402
+ registerPasskey(name?: string): Promise<{
403
+ id: string;
404
+ credentialId: string;
405
+ }>;
406
+ /**
407
+ * Sign in with a passkey. Browser shows the credential picker UI;
408
+ * `userId` is optional — when omitted, uses the resident-key flow
409
+ * so the user picks their account from the prompt.
410
+ */
411
+ signInWithPasskey(userId?: string | null): Promise<AuthUser>;
412
+ listPasskeys(): Promise<Array<{
413
+ id: string;
414
+ name: string | null;
415
+ transports: string[];
416
+ lastUsedAt: string | null;
417
+ createdAt: string;
418
+ }>>;
419
+ deletePasskey(passkeyId: string): Promise<void>;
420
+ listMySessions(): Promise<Array<{
421
+ id: string;
422
+ deviceInfo: string | null;
423
+ ipHash: string | null;
424
+ lastUsedAt: string | null;
425
+ createdAt: string;
426
+ }>>;
427
+ revokeSession(sessionId: string): Promise<void>;
428
+ revokeAllSessions(): Promise<void>;
429
+ /**
430
+ * Start OIDC sign-in. Returns the IdP's authorization URL — caller
431
+ * either redirects the current tab to it (`window.location = url`)
432
+ * or opens it in a popup. After the user authenticates, the IdP
433
+ * redirects through the Sendora callback back to `returnTo` with
434
+ * a `#sendora_oidc_token=...` URL fragment. Call
435
+ * `consumeSsoTokenFromUrl()` on the returnTo page to mint a session.
436
+ */
437
+ startSso(returnTo: string): Promise<{
438
+ authorizationUrl: string;
439
+ state: string;
440
+ }>;
441
+ /**
442
+ * Convenience — redirects the current tab to the IdP. Most apps
443
+ * just want this; popup flows are advanced.
444
+ */
445
+ signInWithSso(returnTo?: string): Promise<void>;
446
+ /**
447
+ * Read the OIDC fragment off `window.location.hash`, exchange it
448
+ * for a session, and clean up the URL. Returns the AuthUser on
449
+ * success, null when no fragment present, throws on
450
+ * `sendora_oidc_error=...`. Call this once on the returnTo page
451
+ * (typically from a top-level effect / page-load hook).
452
+ */
453
+ consumeSsoTokenFromUrl(): Promise<AuthUser | null>;
454
+ /**
455
+ * Build the Authorization header for end-user-authenticated routes.
456
+ * Throws if there's no current session — caller should handle as
457
+ * an unauthenticated state.
458
+ */
459
+ private bearerHeaders;
336
460
  /**
337
461
  * Sign out: wipe local identity FIRST so the user is logged out on
338
462
  * device even if the revoke network call hangs (airplane mode, 5xx,
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- var $=/^[a-zA-Z][a-zA-Z0-9._:-]{0,127}$/;var B=new Set(["__proto__","constructor","prototype"]),l=class extends Error{constructor(t){super(`[SendoraCloud] ${t}`),this.name="SendoraValidationError"}};function H(r){if(typeof r!="string"||!$.test(r))throw new l(`Invalid event name: must match ${$}, got "${String(r)}".`)}function D(r){if(r==null)return;if(typeof r!="object"||Array.isArray(r))throw new l("Properties must be a plain object, not an array or primitive.");let t=L(r);if(t>5)throw new l(`Properties nested too deeply (${t} levels, max 5).`);K(r);let e;try{e=JSON.stringify(r).length}catch{throw new l("Properties contain a circular reference or unserializable value.")}if(e>32768)throw new l(`Properties too large (${e} bytes, max 32768).`)}function L(r,t=0){if(t>6||r==null||typeof r!="object")return t;let e=t;for(let s of Object.keys(r)){let n=L(r[s],t+1);n>e&&(e=n)}return e}function K(r){if(!(r==null||typeof r!="object"))for(let t of Object.keys(r)){if(B.has(t))throw new l(`Forbidden property key "${t}" (prototype pollution guard).`);K(r[t])}}var J=/^pk_[A-Za-z0-9_-]{16,}$/;function N(r){if(!r||typeof r!="string")throw new l("apiKey is required.");if(r.startsWith("sk_")||r.startsWith("sendora_secret_"))throw new l("Secret keys must NEVER be used in browser SDK. Use a publishable key (pk_...).");if(!J.test(r))throw new l("Invalid publishable key format. Expected pk_... (17+ chars).")}function j(r){let t=r.replace(/\/$/,"");if(!(t.startsWith("http://localhost")||t.startsWith("http://127.0.0.1"))&&!t.startsWith("https://"))throw new l("apiUrl must be https:// (only http://localhost is allowed for dev).")}var G="https://api.sendoracloud.com",Z=1e4,_=class{constructor(t){let e=(t.apiUrl??G).replace(/\/$/,"");j(e),this.apiKey=t.apiKey,this.baseUrl=e,this.debug=!!t.debug}get(t){return this.request("GET",t)}post(t,e){return this.request("POST",t,e)}async request(t,e,s){let n=`${this.baseUrl}/api/v1${e}`,i=typeof AbortController<"u"?new AbortController:null,o=i?setTimeout(()=>i.abort(),Z):null;try{let u=await fetch(n,{method:t,headers:{"Content-Type":"application/json","X-API-Key":this.apiKey},body:s?JSON.stringify(s):void 0,signal:i?i.signal:void 0});o&&clearTimeout(o);let a;try{a=await u.json()}catch{a={success:!1,error:{code:"PARSE_ERROR",message:`HTTP ${u.status}`}}}if(this.debug&&console.debug(`[SendoraCloud] ${t} ${e} \u2192 ${u.status}`),!u.ok)if(e.startsWith("/auth-service/")&&!this.debug)console.warn(`[SendoraCloud] ${t} ${e} \u2192 HTTP ${u.status}`);else{let c=a?.error?.code??"HTTP_ERROR",V=a?.error?.message??`HTTP ${u.status}`,F=a?.error?.details?.fieldErrors,X=F?` (${Object.entries(F).map(([W,Y])=>`${W}: ${(Y??[]).join(", ")}`).join("; ")})`:"";console.warn(`[SendoraCloud] ${t} ${e} \u2192 HTTP ${u.status} \u2014 ${c}: ${V}${X}`)}return a}catch(u){o&&clearTimeout(o);let a=u instanceof Error&&u.name==="AbortError"?"timeout":"error";return console.warn(`[SendoraCloud] ${t} ${e} network ${a}`),{success:!1,error:{code:"NETWORK_ERROR",message:a}}}}};var C="sendora_",E=class{constructor(){this.memoryStore=new Map;this.useLocalStorage=this.checkLocalStorage()}get(t){let e=C+t;if(this.useLocalStorage)try{return localStorage.getItem(e)}catch{return this.memoryStore.get(e)??null}return this.memoryStore.get(e)??null}set(t,e){let s=C+t;if(this.useLocalStorage)try{localStorage.setItem(s,e);return}catch{}this.memoryStore.set(s,e)}remove(t){let e=C+t;if(this.useLocalStorage)try{localStorage.removeItem(e)}catch{}this.memoryStore.delete(e)}clearPrefix(t){let e=C+t;if(this.useLocalStorage)try{for(let s=localStorage.length-1;s>=0;s--){let n=localStorage.key(s);n&&n.startsWith(e)&&localStorage.removeItem(n)}}catch{}for(let s of Array.from(this.memoryStore.keys()))s.startsWith(e)&&this.memoryStore.delete(s)}checkLocalStorage(){try{if(typeof localStorage>"u")return!1;let t="__sendora_test__";return localStorage.setItem(t,"1"),localStorage.removeItem(t),!0}catch{return!1}}};function g(r=""){let t=new Uint8Array(16),e=typeof globalThis<"u"&&"crypto"in globalThis?globalThis.crypto:void 0;if(!e||typeof e.getRandomValues!="function")throw new Error("[SendoraCloud] crypto.getRandomValues unavailable \u2014 refusing to mint IDs without a CSPRNG");e.getRandomValues(t);let s="";for(let n of t)s+=n.toString(16).padStart(2,"0");return r+s}var z="2.2.0",Q=500,tt=10,et=6e4,m=class{constructor(t,e,s,n){this.queue=[];this.timer=null;this.userId=null;this.identityToken=null;this.consecutiveFailures=0;this.nextFlushAfter=0;this.http=e,this.storage=s,this.consent=n,this.projectId=t.projectId,this.flushInterval=t.flushInterval??5e3,this.maxQueueSize=t.maxQueueSize??20,this.persistUserId=!!t.persistUserId,this.stripQueryParams=t.stripQueryParams!==!1;let i=this.storage.get("anonymous_id");if(i?this.anonymousId=i:(this.anonymousId=g("anon_"),this.storage.set("anonymous_id",this.anonymousId)),this.persistUserId){let o=this.storage.get("user_id");o&&(this.userId=o)}this.timer=setInterval(()=>this.flush(),this.flushInterval),typeof window<"u"&&window.addEventListener("beforeunload",()=>this.flush()),this.consent.subscribe(o=>{o&&this.flush()})}track(t,e){H(t),e&&D(e),this.queue.length>=Q&&this.queue.shift(),this.queue.push({eventType:t,properties:e??{},userId:this.userId??void 0,anonymousId:this.anonymousId,timestamp:new Date().toISOString(),identityToken:this.identityToken??void 0}),this.queue.length>=this.maxQueueSize&&this.flush()}page(t){if(typeof window>"u")return;let e=this.stripQueryParams?window.location.origin+window.location.pathname:window.location.href;this.track("page.viewed",{url:e,path:window.location.pathname,title:document.title,referrer:document.referrer||void 0,...t})}identify(t,e,s){if(typeof t!="string"||t.length===0||t.length>256)throw new Error("[SendoraCloud] userId must be a 1-256 char string.");this.userId=t,this.identityToken=s?.identityToken??null,this.persistUserId&&this.storage.set("user_id",t),this.track("user.identified",{...e})}reset(){this.userId=null,this.identityToken=null,this.storage.remove("user_id"),this.anonymousId=g("anon_"),this.storage.set("anonymous_id",this.anonymousId),this.storage.clearPrefix("iam_shown_"),this.storage.clearPrefix("survey_shown_")}dropQueue(){this.queue=[]}async flush(){if(this.queue.length===0||!this.consent.isGranted()||Date.now()<this.nextFlushAfter)return;let t=this.queue.splice(0,100),e=t.map(o=>({projectId:this.projectId,module:"custom",eventType:o.eventType,userId:o.userId,anonymousId:o.anonymousId,identityToken:o.identityToken,timestamp:o.timestamp??new Date().toISOString(),properties:o.properties??{},context:this.getContext(),consent:["analytics"]}));if((await this.http.post("/events/batch",{events:e})).success){this.consecutiveFailures=0;return}if(this.consecutiveFailures++,this.consecutiveFailures>tt)return;let n=Math.min(et,1e3*2**this.consecutiveFailures);this.nextFlushAfter=Date.now()+n;let i=Math.max(0,Q-this.queue.length);i>0&&this.queue.unshift(...t.slice(0,i))}destroy(){this.timer&&clearInterval(this.timer),this.timer=null,this.flush()}getContext(){return typeof window>"u"?{sdk:{name:"@sendoracloud/sdk-web",version:z}}:{page:{url:this.stripQueryParams?window.location.origin+window.location.pathname:window.location.href,path:window.location.pathname,title:document.title},referrer:document.referrer||void 0,locale:typeof navigator<"u"?navigator.language:void 0,sdk:{name:"@sendoracloud/sdk-web",version:z}}}};var y=class{constructor(t,e,s){this.cache=new Map;this.refreshInterval=null;this.http=e,this.storage=s;let n=this.storage.get("flags");if(n)try{let i=JSON.parse(n);if(Array.isArray(i))for(let o of i)M(o)&&this.cache.set(o.key,o)}catch{}}setContext(t){this.userId=t.userId,this.entityId=t.entityId,this.environment=t.environment}async getFlag(t){if(typeof t!="string"||t.length===0||t.length>128)return null;let e=this.cache.get(t);if(e)return e.value;let s=await this.http.post("/feature-flags/evaluate",{key:t,userId:this.userId,entityId:this.entityId,environment:this.environment});return s.success&&s.data&&M(s.data)?(this.cache.set(t,s.data),this.persistCache(),s.data.value):null}async isEnabled(t,e=!1){let s=await this.getFlag(t);return typeof s=="boolean"?s:e}async refreshAll(){let t=await this.http.post("/feature-flags/evaluate-all",{userId:this.userId,entityId:this.entityId,environment:this.environment});if(t.success&&Array.isArray(t.data)){this.cache.clear();for(let e of t.data)M(e)&&this.cache.set(e.key,e);this.persistCache()}}startAutoRefresh(t=6e4){this.stopAutoRefresh(),this.refreshAll(),this.refreshInterval=setInterval(()=>this.refreshAll(),t)}stopAutoRefresh(){this.refreshInterval&&clearInterval(this.refreshInterval),this.refreshInterval=null}getAllFlags(){let t={};for(let[e,s]of this.cache)t[e]=s.value;return t}reset(){this.cache.clear(),this.storage.remove("flags"),this.userId=void 0,this.entityId=void 0,this.environment=void 0}destroy(){this.stopAutoRefresh()}persistCache(){this.storage.set("flags",JSON.stringify(Array.from(this.cache.values())))}};function M(r){if(r==null||typeof r!="object")return!1;let t=r;return Object.prototype.hasOwnProperty.call(t,"__proto__")||Object.prototype.hasOwnProperty.call(t,"constructor")?!1:typeof t.key=="string"&&"value"in t}var st=32*1024,v=class{constructor(t,e,s){this.activeSurvey=null;this.http=e,this.storage=s}onDisplay(t){this.onSurveyDisplay=t}async showSurvey(t){if(typeof t!="string"||!/^[A-Za-z0-9_-]{6,64}$/.test(t))return null;let e=`survey_shown_${t}`;if(this.storage.get(e))return null;let s=await this.http.get(`/surveys/${encodeURIComponent(t)}/config`);return!s.success||!s.data||!rt(s.data)?null:(this.activeSurvey=s.data,this.storage.set(e,"1"),this.onSurveyDisplay&&this.onSurveyDisplay(s.data),s.data)}async submitResponse(t,e,s=!0){let n=0;try{n=JSON.stringify(e).length}catch{return!1}if(n>st)return!1;let i=await this.http.post("/surveys/responses",{surveyId:t,answers:e,completed:s});return i.success&&(this.activeSurvey=null),i.success}dismiss(){this.activeSurvey=null}getActiveSurvey(){return this.activeSurvey}};function rt(r){if(r==null||typeof r!="object")return!1;let t=r;return typeof t.id!="string"||typeof t.name!="string"||!Array.isArray(t.questions)?!1:t.questions.every(nt)}function nt(r){if(r==null||typeof r!="object")return!1;let t=r;return typeof t.id=="string"&&typeof t.text=="string"}function p(r){if(!(!r||typeof r!="string"))try{let t=typeof window<"u"?window.location.href:"https://local/",e=new URL(r,t);return e.protocol==="http:"||e.protocol==="https:"?e.href:void 0}catch{return}}var it=new Set(["b","i","u","em","strong","a","p","br","span","ul","ol","li","h1","h2","h3","h4"]),ot={a:new Set(["href","target","rel"])};function U(r){if(typeof DOMParser>"u")return"";let e=new DOMParser().parseFromString(`<div>${r}</div>`,"text/html").body.firstElementChild;return e?(q(e),e.innerHTML):""}function q(r){let t=Array.from(r.children);for(let e of t){let s=e.tagName.toLowerCase();if(!it.has(s)){let i=document.createTextNode(e.textContent??"");e.replaceWith(i);continue}let n=ot[s]??new Set;for(let i of Array.from(e.attributes)){if(!n.has(i.name.toLowerCase())){e.removeAttribute(i.name);continue}if(i.name.toLowerCase()==="href"){let o=p(i.value);o?e.setAttribute("href",o):e.removeAttribute(i.name)}}s==="a"&&(e.setAttribute("rel","noopener noreferrer"),e.setAttribute("target","_blank")),q(e)}}var w=class{constructor(t,e,s){this.messages=[];this.pollInterval=null;this.http=e,this.storage=s}onDisplay(t){this.onMessageDisplay=t}async checkMessages(){let t=await this.http.get("/in-app-messages/active");if(!t.success||!Array.isArray(t.data))return null;this.messages=t.data.map(at);for(let e of this.messages){let s=`iam_shown_${e.id}`;if(!this.storage.get(s))return this.storage.set(s,"1"),this.onMessageDisplay&&this.onMessageDisplay(e),e}return null}async recordAction(t,e){await this.http.post("/in-app-messages/impressions",{messageId:t,action:e})}startPolling(t=3e5){this.stopPolling(),this.checkMessages(),this.pollInterval=setInterval(()=>this.checkMessages(),t)}stopPolling(){this.pollInterval&&clearInterval(this.pollInterval),this.pollInterval=null}getMessages(){return this.messages}destroy(){this.stopPolling()}};function at(r){return{...r,content:{...r.content,ctaUrl:p(r.content.ctaUrl),imageUrl:p(r.content.imageUrl)}}}var ut=4e3,S=class{constructor(t,e,s){this.chatbotId=null;this.messages=[];this.http=e,this.storage=s;let n=this.storage.get("chat_session_id");this.sessionId=n??g("chat_"),n||this.storage.set("chat_session_id",this.sessionId)}init(t){if(typeof t!="string"||!/^[A-Za-z0-9_-]{6,64}$/.test(t))throw new Error("[SendoraCloud] Invalid chatbotId.");this.chatbotId=t}onNewMessage(t){this.onMessage=t}async sendMessage(t,e){if(!this.chatbotId||typeof t!="string"||t.length===0||t.length>ut)return null;let s={role:"user",content:t,timestamp:new Date().toISOString()};this.messages.push(s);let n=await this.http.post("/chatbot/message",{chatbotId:this.chatbotId,sessionId:this.sessionId,message:t,userId:e});if(n.success&&n.data&&typeof n.data.reply=="string"){let i={role:"assistant",content:n.data.reply,timestamp:new Date().toISOString()};return this.messages.push(i),this.onMessage&&this.onMessage(i),i}return null}getMessages(){return[...this.messages]}newSession(){this.messages=[],this.sessionId=g("chat_"),this.storage.set("chat_session_id",this.sessionId)}getSessionId(){return this.sessionId}};var A=class{constructor(t){this.listeners=new Set;this.granted=t}isGranted(){return this.granted}grant(){this.granted||(this.granted=!0,this.emit())}revoke(){this.granted&&(this.granted=!1,this.emit())}subscribe(t){return this.listeners.add(t),()=>this.listeners.delete(t)}emit(){for(let t of this.listeners)try{t(this.granted)}catch{}}};var I="auth_access_token",k="auth_access_expires",f="auth_refresh_token",R="auth_user",lt=3e4;function x(r){if(!r||typeof r!="object")return!1;let t=r,e=t.user,s=t.tokens;return!(!e||typeof e.id!="string"||e.id.length===0||typeof e.isAnonymous!="boolean"||!s||typeof s.accessToken!="string"||s.accessToken.length===0||typeof s.refreshToken!="string"||s.refreshToken.length===0||typeof s.expiresIn!="number"||s.expiresIn<=0)}var T=class extends Error{constructor(t="An account with this email already exists"){super(t),this.name="EmailAlreadyTakenError"}},d=class extends Error{constructor(t,e){super(e),this.name="AuthError",this.code=t}},b=class{constructor(t,e,s,n,i,o){this.user=null;this.accessToken=null;this.accessExpiresAt=0;this.inflight=Promise.resolve();this.refreshInflight=null;this.http=e,this.storage=s,this.analytics=n,this.flags=i,this.chatbot=o;let u=this.storage.get(R),a=this.storage.get(I),h=this.storage.get(k);if(u&&a)try{let c=JSON.parse(u);if(typeof c.id!="string"||c.id.length===0)throw new Error("invalid cache");this.user=c,this.accessToken=a,this.accessExpiresAt=h?Number(h):0,this.analytics.identify(this.user.id)}catch{this.storage.remove(R),this.storage.remove(I),this.storage.remove(k),this.storage.remove(f)}}getCurrentUser(){return this.user}async getAccessToken(){return this.accessToken?this.accessExpiresAt&&Date.now()<this.accessExpiresAt-lt?this.accessToken:this.refreshAccessToken():null}getAccessTokenSync(){return this.accessToken}signInAnonymously(t){return this.serialize(async()=>{let e=await this.http.post("/auth-service/anonymous",{name:t?.name,metadata:t?.metadata});if(!e.success||!x(e.data))throw new d(e.error?.code??"AUTH_ERROR",e.error?.message??"Anonymous signup failed");return this.persist(e.data),e.data.user})}signUp(t,e,s){return this.serialize(async()=>{let n=this.storage.get(f);if(this.user?.isAnonymous===!0&&n){let a=await this.http.post("/auth-service/upgrade",{refreshToken:n,email:t,password:e,name:s?.name});if(a.success&&x(a.data))return this.persist(a.data),a.data.user;let h=a.error?.code??"",c=a.error?.status;throw h==="CONFLICT"||h==="EMAIL_ALREADY_TAKEN"||c===409?new T(a.error?.message):new d(h||"AUTH_ERROR",a.error?.message??"Upgrade failed")}this.user!==null&&this.user.isAnonymous===!1&&(await this.analytics.flush(),this.wipeLocalIdentity());let u=await this.http.post("/auth-service/signup",{email:t,password:e,name:s?.name,metadata:s?.metadata});if(!u.success||!x(u.data)){let a=u.error?.code??"";throw a==="CONFLICT"?new T(u.error?.message):new d(a||"AUTH_ERROR",u.error?.message??"Signup failed")}return this.persist(u.data),u.data.user})}signIn(t,e){return this.serialize(async()=>{this.user!==null&&(await this.analytics.flush(),this.wipeLocalIdentity());let n=await this.http.post("/auth-service/login",{email:t,password:e});if(!n.success||!x(n.data))throw new d(n.error?.code??"AUTH_ERROR",n.error?.message??"Login failed");return this.persist(n.data),n.data.user})}signOut(){return this.serialize(async()=>{let t=this.storage.get(f);this.wipeLocalIdentity(),t&&this.http.post("/auth-service/token/revoke",{refreshToken:t}).catch(()=>{})})}serialize(t){let e=this.inflight.then(t,t);return this.inflight=e.catch(()=>{}),e}async refreshAccessToken(){if(this.refreshInflight)return this.refreshInflight;let t=this.storage.get(f);return t?(this.refreshInflight=(async()=>{try{let e=await this.http.post("/auth-service/token/refresh",{refreshToken:t});if(!e.success||!e.data||typeof e.data!="object"||typeof e.data.accessToken!="string"||typeof e.data.refreshToken!="string"||typeof e.data.expiresIn!="number")return null;let s=e.data;return this.accessToken=s.accessToken,this.accessExpiresAt=Date.now()+s.expiresIn*1e3,this.storage.set(I,s.accessToken),this.storage.set(k,String(this.accessExpiresAt)),this.storage.set(f,s.refreshToken),s.accessToken}catch{return null}finally{this.refreshInflight=null}})(),this.refreshInflight):null}persist(t){this.user=t.user,this.accessToken=t.tokens.accessToken,this.accessExpiresAt=Date.now()+t.tokens.expiresIn*1e3,this.storage.set(R,JSON.stringify(t.user)),this.storage.set(I,t.tokens.accessToken),this.storage.set(k,String(this.accessExpiresAt)),this.storage.set(f,t.tokens.refreshToken),this.analytics.identify(t.user.id),this.flags.setContext({userId:t.user.id})}wipeLocalIdentity(){this.user=null,this.accessToken=null,this.accessExpiresAt=0,this.storage.remove(R),this.storage.remove(I),this.storage.remove(k),this.storage.remove(f),this.analytics.reset(),this.analytics.dropQueue(),this.flags.reset(),this.chatbot.newSession()}};var ht="2.2.0",P=class P{constructor(t){this.http=new _(t),this.storage=new E,this.consent=new A(t.defaultConsent===!0),this.analytics=new m(t,this.http,this.storage,this.consent),this.flags=new y(t,this.http,this.storage),this.surveys=new v(t,this.http,this.storage),this.messages=new w(t,this.http,this.storage),this.chatbot=new S(t,this.http,this.storage),this.auth=new b(t,this.http,this.storage,this.analytics,this.flags,this.chatbot),t.autoTrack!==!1&&typeof window<"u"&&this.analytics.page()}static init(t){return N(t.apiKey),new P(t)}static sanitizeHtml(t){return U(t)}static safeUrl(t){return p(t)}track(t,e){this.analytics.track(t,e)}page(t){this.analytics.page(t)}identify(t,e,s){this.analytics.identify(t,e,s),this.flags.setContext({userId:t})}reset(){this.analytics.reset(),this.flags.reset(),this.chatbot.newSession()}async flush(){await this.analytics.flush()}destroy(){this.analytics.destroy(),this.flags.destroy(),this.messages.destroy()}};P.version=ht;var O=P,Lt=O;export{m as Analytics,b as Auth,d as AuthError,S as Chatbot,A as Consent,T as EmailAlreadyTakenError,y as FeatureFlags,w as InAppMessaging,O as SendoraCloud,v as Surveys,l as ValidationError,Lt as default,p as safeUrl,U as sanitizeHtml};
1
+ var H=/^[a-zA-Z][a-zA-Z0-9._:-]{0,127}$/;var Z=new Set(["__proto__","constructor","prototype"]),h=class extends Error{constructor(e){super(`[SendoraCloud] ${e}`),this.name="SendoraValidationError"}};function K(r){if(typeof r!="string"||!H.test(r))throw new h(`Invalid event name: must match ${H}, got "${String(r)}".`)}function N(r){if(r==null)return;if(typeof r!="object"||Array.isArray(r))throw new h("Properties must be a plain object, not an array or primitive.");let e=$(r);if(e>5)throw new h(`Properties nested too deeply (${e} levels, max 5).`);z(r);let t;try{t=JSON.stringify(r).length}catch{throw new h("Properties contain a circular reference or unserializable value.")}if(t>32768)throw new h(`Properties too large (${t} bytes, max 32768).`)}function $(r,e=0){if(e>6||r==null||typeof r!="object")return e;let t=e;for(let s of Object.keys(r)){let n=$(r[s],e+1);n>t&&(t=n)}return t}function z(r){if(!(r==null||typeof r!="object"))for(let e of Object.keys(r)){if(Z.has(e))throw new h(`Forbidden property key "${e}" (prototype pollution guard).`);z(r[e])}}var ee=/^pk_[A-Za-z0-9_-]{16,}$/;function q(r){if(!r||typeof r!="string")throw new h("apiKey is required.");if(r.startsWith("sk_")||r.startsWith("sendora_secret_"))throw new h("Secret keys must NEVER be used in browser SDK. Use a publishable key (pk_...).");if(!ee.test(r))throw new h("Invalid publishable key format. Expected pk_... (17+ chars).")}function j(r){let e=r.replace(/\/$/,"");if(!(e.startsWith("http://localhost")||e.startsWith("http://127.0.0.1"))&&!e.startsWith("https://"))throw new h("apiUrl must be https:// (only http://localhost is allowed for dev).")}var te="https://api.sendoracloud.com",se=1e4,P=class{constructor(e){let t=(e.apiUrl??te).replace(/\/$/,"");j(t),this.apiKey=e.apiKey,this.baseUrl=t,this.debug=!!e.debug}get(e,t){return this.request("GET",e,void 0,t)}post(e,t,s){return this.request("POST",e,t,s)}delete(e,t){return this.request("DELETE",e,void 0,t)}async request(e,t,s,n){let o=`${this.baseUrl}/api/v1${t}`,i=typeof AbortController<"u"?new AbortController:null,l=i?setTimeout(()=>i.abort(),se):null;try{let a=await fetch(o,{method:e,headers:{"Content-Type":"application/json","X-API-Key":this.apiKey,...n??{}},body:s?JSON.stringify(s):void 0,signal:i?i.signal:void 0});l&&clearTimeout(l);let c;try{c=await a.json()}catch{c={success:!1,error:{code:"PARSE_ERROR",message:`HTTP ${a.status}`}}}if(this.debug&&console.debug(`[SendoraCloud] ${e} ${t} \u2192 ${a.status}`),!a.ok)if(t.startsWith("/auth-service/")&&!this.debug)console.warn(`[SendoraCloud] ${e} ${t} \u2192 HTTP ${a.status}`);else{let Q=c?.error?.code??"HTTP_ERROR",B=c?.error?.message??`HTTP ${a.status}`,D=c?.error?.details?.fieldErrors,X=D?` (${Object.entries(D).map(([J,G])=>`${J}: ${(G??[]).join(", ")}`).join("; ")})`:"";console.warn(`[SendoraCloud] ${e} ${t} \u2192 HTTP ${a.status} \u2014 ${Q}: ${B}${X}`)}return c}catch(a){l&&clearTimeout(l);let c=a instanceof Error&&a.name==="AbortError"?"timeout":"error";return console.warn(`[SendoraCloud] ${e} ${t} network ${c}`),{success:!1,error:{code:"NETWORK_ERROR",message:c}}}}};var x="sendora_",U=class{constructor(){this.memoryStore=new Map;this.useLocalStorage=this.checkLocalStorage()}get(e){let t=x+e;if(this.useLocalStorage)try{return localStorage.getItem(t)}catch{return this.memoryStore.get(t)??null}return this.memoryStore.get(t)??null}set(e,t){let s=x+e;if(this.useLocalStorage)try{localStorage.setItem(s,t);return}catch{}this.memoryStore.set(s,t)}remove(e){let t=x+e;if(this.useLocalStorage)try{localStorage.removeItem(t)}catch{}this.memoryStore.delete(t)}clearPrefix(e){let t=x+e;if(this.useLocalStorage)try{for(let s=localStorage.length-1;s>=0;s--){let n=localStorage.key(s);n&&n.startsWith(t)&&localStorage.removeItem(n)}}catch{}for(let s of Array.from(this.memoryStore.keys()))s.startsWith(t)&&this.memoryStore.delete(s)}checkLocalStorage(){try{if(typeof localStorage>"u")return!1;let e="__sendora_test__";return localStorage.setItem(e,"1"),localStorage.removeItem(e),!0}catch{return!1}}};function y(r=""){let e=new Uint8Array(16),t=typeof globalThis<"u"&&"crypto"in globalThis?globalThis.crypto:void 0;if(!t||typeof t.getRandomValues!="function")throw new Error("[SendoraCloud] crypto.getRandomValues unavailable \u2014 refusing to mint IDs without a CSPRNG");t.getRandomValues(e);let s="";for(let n of e)s+=n.toString(16).padStart(2,"0");return r+s}var V="2.2.0",W=500,re=10,ne=6e4,A=class{constructor(e,t,s,n){this.queue=[];this.timer=null;this.userId=null;this.identityToken=null;this.consecutiveFailures=0;this.nextFlushAfter=0;this.http=t,this.storage=s,this.consent=n,this.projectId=e.projectId,this.flushInterval=e.flushInterval??5e3,this.maxQueueSize=e.maxQueueSize??20,this.persistUserId=!!e.persistUserId,this.stripQueryParams=e.stripQueryParams!==!1;let o=this.storage.get("anonymous_id");if(o?this.anonymousId=o:(this.anonymousId=y("anon_"),this.storage.set("anonymous_id",this.anonymousId)),this.persistUserId){let i=this.storage.get("user_id");i&&(this.userId=i)}this.timer=setInterval(()=>this.flush(),this.flushInterval),typeof window<"u"&&window.addEventListener("beforeunload",()=>this.flush()),this.consent.subscribe(i=>{i&&this.flush()})}track(e,t){K(e),t&&N(t),this.queue.length>=W&&this.queue.shift(),this.queue.push({eventType:e,properties:t??{},userId:this.userId??void 0,anonymousId:this.anonymousId,timestamp:new Date().toISOString(),identityToken:this.identityToken??void 0}),this.queue.length>=this.maxQueueSize&&this.flush()}page(e){if(typeof window>"u")return;let t=this.stripQueryParams?window.location.origin+window.location.pathname:window.location.href;this.track("page.viewed",{url:t,path:window.location.pathname,title:document.title,referrer:document.referrer||void 0,...e})}identify(e,t,s){if(typeof e!="string"||e.length===0||e.length>256)throw new Error("[SendoraCloud] userId must be a 1-256 char string.");this.userId=e,this.identityToken=s?.identityToken??null,this.persistUserId&&this.storage.set("user_id",e),this.track("user.identified",{...t})}reset(){this.userId=null,this.identityToken=null,this.storage.remove("user_id"),this.anonymousId=y("anon_"),this.storage.set("anonymous_id",this.anonymousId),this.storage.clearPrefix("iam_shown_"),this.storage.clearPrefix("survey_shown_")}dropQueue(){this.queue=[]}async flush(){if(this.queue.length===0||!this.consent.isGranted()||Date.now()<this.nextFlushAfter)return;let e=this.queue.splice(0,100),t=e.map(i=>({projectId:this.projectId,module:"custom",eventType:i.eventType,userId:i.userId,anonymousId:i.anonymousId,identityToken:i.identityToken,timestamp:i.timestamp??new Date().toISOString(),properties:i.properties??{},context:this.getContext(),consent:["analytics"]}));if((await this.http.post("/events/batch",{events:t})).success){this.consecutiveFailures=0;return}if(this.consecutiveFailures++,this.consecutiveFailures>re)return;let n=Math.min(ne,1e3*2**this.consecutiveFailures);this.nextFlushAfter=Date.now()+n;let o=Math.max(0,W-this.queue.length);o>0&&this.queue.unshift(...e.slice(0,o))}destroy(){this.timer&&clearInterval(this.timer),this.timer=null,this.flush()}getContext(){return typeof window>"u"?{sdk:{name:"@sendoracloud/sdk-web",version:V}}:{page:{url:this.stripQueryParams?window.location.origin+window.location.pathname:window.location.href,path:window.location.pathname,title:document.title},referrer:document.referrer||void 0,locale:typeof navigator<"u"?navigator.language:void 0,sdk:{name:"@sendoracloud/sdk-web",version:V}}}};var S=class{constructor(e,t,s){this.cache=new Map;this.refreshInterval=null;this.http=t,this.storage=s;let n=this.storage.get("flags");if(n)try{let o=JSON.parse(n);if(Array.isArray(o))for(let i of o)M(i)&&this.cache.set(i.key,i)}catch{}}setContext(e){this.userId=e.userId,this.entityId=e.entityId,this.environment=e.environment}async getFlag(e){if(typeof e!="string"||e.length===0||e.length>128)return null;let t=this.cache.get(e);if(t)return t.value;let s=await this.http.post("/feature-flags/evaluate",{key:e,userId:this.userId,entityId:this.entityId,environment:this.environment});return s.success&&s.data&&M(s.data)?(this.cache.set(e,s.data),this.persistCache(),s.data.value):null}async isEnabled(e,t=!1){let s=await this.getFlag(e);return typeof s=="boolean"?s:t}async refreshAll(){let e=await this.http.post("/feature-flags/evaluate-all",{userId:this.userId,entityId:this.entityId,environment:this.environment});if(e.success&&Array.isArray(e.data)){this.cache.clear();for(let t of e.data)M(t)&&this.cache.set(t.key,t);this.persistCache()}}startAutoRefresh(e=6e4){this.stopAutoRefresh(),this.refreshAll(),this.refreshInterval=setInterval(()=>this.refreshAll(),e)}stopAutoRefresh(){this.refreshInterval&&clearInterval(this.refreshInterval),this.refreshInterval=null}getAllFlags(){let e={};for(let[t,s]of this.cache)e[t]=s.value;return e}reset(){this.cache.clear(),this.storage.remove("flags"),this.userId=void 0,this.entityId=void 0,this.environment=void 0}destroy(){this.stopAutoRefresh()}persistCache(){this.storage.set("flags",JSON.stringify(Array.from(this.cache.values())))}};function M(r){if(r==null||typeof r!="object")return!1;let e=r;return Object.prototype.hasOwnProperty.call(e,"__proto__")||Object.prototype.hasOwnProperty.call(e,"constructor")?!1:typeof e.key=="string"&&"value"in e}var ie=32*1024,I=class{constructor(e,t,s){this.activeSurvey=null;this.http=t,this.storage=s}onDisplay(e){this.onSurveyDisplay=e}async showSurvey(e){if(typeof e!="string"||!/^[A-Za-z0-9_-]{6,64}$/.test(e))return null;let t=`survey_shown_${e}`;if(this.storage.get(t))return null;let s=await this.http.get(`/surveys/${encodeURIComponent(e)}/config`);return!s.success||!s.data||!oe(s.data)?null:(this.activeSurvey=s.data,this.storage.set(t,"1"),this.onSurveyDisplay&&this.onSurveyDisplay(s.data),s.data)}async submitResponse(e,t,s=!0){let n=0;try{n=JSON.stringify(t).length}catch{return!1}if(n>ie)return!1;let o=await this.http.post("/surveys/responses",{surveyId:e,answers:t,completed:s});return o.success&&(this.activeSurvey=null),o.success}dismiss(){this.activeSurvey=null}getActiveSurvey(){return this.activeSurvey}};function oe(r){if(r==null||typeof r!="object")return!1;let e=r;return typeof e.id!="string"||typeof e.name!="string"||!Array.isArray(e.questions)?!1:e.questions.every(ae)}function ae(r){if(r==null||typeof r!="object")return!1;let e=r;return typeof e.id=="string"&&typeof e.text=="string"}function f(r){if(!(!r||typeof r!="string"))try{let e=typeof window<"u"?window.location.href:"https://local/",t=new URL(r,e);return t.protocol==="http:"||t.protocol==="https:"?t.href:void 0}catch{return}}var le=new Set(["b","i","u","em","strong","a","p","br","span","ul","ol","li","h1","h2","h3","h4"]),ue={a:new Set(["href","target","rel"])};function L(r){if(typeof DOMParser>"u")return"";let t=new DOMParser().parseFromString(`<div>${r}</div>`,"text/html").body.firstElementChild;return t?(Y(t),t.innerHTML):""}function Y(r){let e=Array.from(r.children);for(let t of e){let s=t.tagName.toLowerCase();if(!le.has(s)){let o=document.createTextNode(t.textContent??"");t.replaceWith(o);continue}let n=ue[s]??new Set;for(let o of Array.from(t.attributes)){if(!n.has(o.name.toLowerCase())){t.removeAttribute(o.name);continue}if(o.name.toLowerCase()==="href"){let i=f(o.value);i?t.setAttribute("href",i):t.removeAttribute(o.name)}}s==="a"&&(t.setAttribute("rel","noopener noreferrer"),t.setAttribute("target","_blank")),Y(t)}}var k=class{constructor(e,t,s){this.messages=[];this.pollInterval=null;this.http=t,this.storage=s}onDisplay(e){this.onMessageDisplay=e}async checkMessages(){let e=await this.http.get("/in-app-messages/active");if(!e.success||!Array.isArray(e.data))return null;this.messages=e.data.map(ce);for(let t of this.messages){let s=`iam_shown_${t.id}`;if(!this.storage.get(s))return this.storage.set(s,"1"),this.onMessageDisplay&&this.onMessageDisplay(t),t}return null}async recordAction(e,t){await this.http.post("/in-app-messages/impressions",{messageId:e,action:t})}startPolling(e=3e5){this.stopPolling(),this.checkMessages(),this.pollInterval=setInterval(()=>this.checkMessages(),e)}stopPolling(){this.pollInterval&&clearInterval(this.pollInterval),this.pollInterval=null}getMessages(){return this.messages}destroy(){this.stopPolling()}};function ce(r){return{...r,content:{...r.content,ctaUrl:f(r.content.ctaUrl),imageUrl:f(r.content.imageUrl)}}}var he=4e3,b=class{constructor(e,t,s){this.chatbotId=null;this.messages=[];this.http=t,this.storage=s;let n=this.storage.get("chat_session_id");this.sessionId=n??y("chat_"),n||this.storage.set("chat_session_id",this.sessionId)}init(e){if(typeof e!="string"||!/^[A-Za-z0-9_-]{6,64}$/.test(e))throw new Error("[SendoraCloud] Invalid chatbotId.");this.chatbotId=e}onNewMessage(e){this.onMessage=e}async sendMessage(e,t){if(!this.chatbotId||typeof e!="string"||e.length===0||e.length>he)return null;let s={role:"user",content:e,timestamp:new Date().toISOString()};this.messages.push(s);let n=await this.http.post("/chatbot/message",{chatbotId:this.chatbotId,sessionId:this.sessionId,message:e,userId:t});if(n.success&&n.data&&typeof n.data.reply=="string"){let o={role:"assistant",content:n.data.reply,timestamp:new Date().toISOString()};return this.messages.push(o),this.onMessage&&this.onMessage(o),o}return null}getMessages(){return[...this.messages]}newSession(){this.messages=[],this.sessionId=y("chat_"),this.storage.set("chat_session_id",this.sessionId)}getSessionId(){return this.sessionId}};var T=class{constructor(e){this.listeners=new Set;this.granted=e}isGranted(){return this.granted}grant(){this.granted||(this.granted=!0,this.emit())}revoke(){this.granted&&(this.granted=!1,this.emit())}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}emit(){for(let e of this.listeners)try{e(this.granted)}catch{}}};var w="auth_access_token",v="auth_access_expires",d="auth_refresh_token",_="auth_user",de=3e4;function p(r){if(!r||typeof r!="object")return!1;let e=r,t=e.user,s=e.tokens;return!(!t||typeof t.id!="string"||t.id.length===0||typeof t.isAnonymous!="boolean"||!s||typeof s.accessToken!="string"||s.accessToken.length===0||typeof s.refreshToken!="string"||s.refreshToken.length===0||typeof s.expiresIn!="number"||s.expiresIn<=0)}var C=class extends Error{constructor(e="An account with this email already exists"){super(e),this.name="EmailAlreadyTakenError"}},u=class extends Error{constructor(e,t){super(t),this.name="AuthError",this.code=e}},R=class{constructor(e,t,s,n,o,i){this.user=null;this.accessToken=null;this.accessExpiresAt=0;this.inflight=Promise.resolve();this.refreshInflight=null;this.http=t,this.storage=s,this.analytics=n,this.flags=o,this.chatbot=i;let l=this.storage.get(_),a=this.storage.get(w),c=this.storage.get(v);if(l&&a)try{let m=JSON.parse(l);if(typeof m.id!="string"||m.id.length===0)throw new Error("invalid cache");this.user=m,this.accessToken=a,this.accessExpiresAt=c?Number(c):0,this.analytics.identify(this.user.id)}catch{this.storage.remove(_),this.storage.remove(w),this.storage.remove(v),this.storage.remove(d)}}getCurrentUser(){return this.user}async getAccessToken(){return this.accessToken?this.accessExpiresAt&&Date.now()<this.accessExpiresAt-de?this.accessToken:this.refreshAccessToken():null}getAccessTokenSync(){return this.accessToken}signInAnonymously(e){return this.serialize(async()=>{let t=await this.http.post("/auth-service/anonymous",{name:e?.name,metadata:e?.metadata});if(!t.success||!p(t.data))throw new u(t.error?.code??"AUTH_ERROR",t.error?.message??"Anonymous signup failed");return this.persist(t.data),t.data.user})}signUp(e,t,s){return this.serialize(async()=>{let n=this.storage.get(d);if(this.user?.isAnonymous===!0&&n){let a=await this.http.post("/auth-service/upgrade",{refreshToken:n,email:e,password:t,name:s?.name});if(a.success&&p(a.data))return this.persist(a.data),a.data.user;let c=a.error?.code??"",m=a.error?.status;throw c==="CONFLICT"||c==="EMAIL_ALREADY_TAKEN"||m===409?new C(a.error?.message):new u(c||"AUTH_ERROR",a.error?.message??"Upgrade failed")}this.user!==null&&this.user.isAnonymous===!1&&(await this.analytics.flush(),this.wipeLocalIdentity());let l=await this.http.post("/auth-service/signup",{email:e,password:t,name:s?.name,metadata:s?.metadata});if(!l.success||!p(l.data)){let a=l.error?.code??"";throw a==="CONFLICT"?new C(l.error?.message):new u(a||"AUTH_ERROR",l.error?.message??"Signup failed")}return this.persist(l.data),l.data.user})}signIn(e,t){return this.serialize(async()=>{this.user!==null&&(await this.analytics.flush(),this.wipeLocalIdentity());let n=await this.http.post("/auth-service/login",{email:e,password:t});if(!n.success||!n.data)throw new u(n.error?.code??"AUTH_ERROR",n.error?.message??"Login failed");let o=n.data;if(o.mfaRequired===!0&&typeof o.mfaChallengeToken=="string"){let i=o.user;return{mfaRequired:!0,mfaChallengeToken:o.mfaChallengeToken,userId:i?.id??""}}if(!p(n.data))throw new u(n.error?.code??"AUTH_ERROR",n.error?.message??"Login failed");return this.persist(n.data),n.data.user})}challengeMfa(e,t){return this.serialize(async()=>{let s=await this.http.post("/auth-service/mfa/challenge",{challengeToken:e,code:t});if(!s.success||!p(s.data))throw new u(s.error?.code??"MFA_CHALLENGE_FAILED",s.error?.message??"Invalid MFA code");return this.persist(s.data),s.data.user})}async sendMagicLink(e){let t=await this.http.post("/auth-service/magic-link/request",{email:e});if(!t.success)throw new u(t.error?.code??"MAGIC_LINK_REQUEST_FAILED",t.error?.message??"Failed to send link")}async sendEmailOtp(e){let t=await this.http.post("/auth-service/email-otp/request",{email:e});if(!t.success)throw new u(t.error?.code??"EMAIL_OTP_REQUEST_FAILED",t.error?.message??"Failed to send code")}verifyEmailOtp(e,t){return this.serialize(async()=>{this.user!==null&&(await this.analytics.flush(),this.wipeLocalIdentity());let n=await this.http.post("/auth-service/email-otp/verify",{email:e,code:t});if(!n.success||!p(n.data))throw new u(n.error?.code??"EMAIL_OTP_INVALID",n.error?.message??"Wrong or expired code");return this.persist(n.data),n.data.user})}verifyMagicLink(e){return this.serialize(async()=>{this.user!==null&&(await this.analytics.flush(),this.wipeLocalIdentity());let s=await this.http.post("/auth-service/magic-link/verify",{token:e});if(!s.success||!p(s.data))throw new u(s.error?.code??"MAGIC_LINK_INVALID",s.error?.message??"Invalid or expired magic link");return this.persist(s.data),s.data.user})}async enrollMfa(){let e=await this.http.post("/auth-service/mfa/enroll/start",{},this.bearerHeaders());if(!e.success||!e.data)throw new u(e.error?.code??"MFA_ENROLL_FAILED",e.error?.message??"MFA enrollment failed");return e.data}async confirmMfa(e){let t=await this.http.post("/auth-service/mfa/enroll/confirm",{code:e},this.bearerHeaders());return!!(t.success&&t.data?.confirmed)}async disableMfa(){await this.http.post("/auth-service/mfa/disable",{},this.bearerHeaders())}async registerPasskey(e){if(typeof navigator>"u"||!navigator.credentials)throw new u("PASSKEY_UNSUPPORTED","WebAuthn not supported in this environment");let t=await this.http.post("/auth-service/passkeys/register/start",{},this.bearerHeaders());if(!t.success||!t.data)throw new u(t.error?.code??"PASSKEY_START_FAILED",t.error?.message??"Passkey start failed");let s=pe(t.data),n;try{let l=await navigator.credentials.create({publicKey:s});if(!l)throw new Error("create returned null");n=l}catch(l){throw new u("PASSKEY_USER_CANCELLED",l instanceof Error?l.message:"User cancelled passkey registration")}let o=fe(n),i=await this.http.post("/auth-service/passkeys/register/finish",{response:o,name:e},this.bearerHeaders());if(!i.success||!i.data)throw new u(i.error?.code??"PASSKEY_FINISH_FAILED",i.error?.message??"Passkey verification failed");return i.data}signInWithPasskey(e){return this.serialize(async()=>{if(typeof navigator>"u"||!navigator.credentials)throw new u("PASSKEY_UNSUPPORTED","WebAuthn not supported in this environment");this.user!==null&&(await this.analytics.flush(),this.wipeLocalIdentity());let s=await this.http.post("/auth-service/passkeys/authenticate/start",{userId:e??null});if(!s.success||!s.data)throw new u(s.error?.code??"PASSKEY_START_FAILED",s.error?.message??"Passkey start failed");let n=ge(s.data),o;try{let a=await navigator.credentials.get({publicKey:n});if(!a)throw new Error("get returned null");o=a}catch(a){throw new u("PASSKEY_USER_CANCELLED",a instanceof Error?a.message:"User cancelled passkey signin")}let i=ye(o),l=await this.http.post("/auth-service/passkeys/authenticate/finish",{response:i,userId:e??null});if(!l.success||!p(l.data))throw new u(l.error?.code??"PASSKEY_AUTH_FAILED",l.error?.message??"Passkey signin failed");return this.persist(l.data),l.data.user})}async listPasskeys(){let e=await this.http.get("/auth-service/passkeys",this.bearerHeaders());return!e.success||!Array.isArray(e.data)?[]:e.data}async deletePasskey(e){await this.http.delete(`/auth-service/passkeys/${e}`,this.bearerHeaders())}async listMySessions(){let e=await this.http.get("/auth-service/sessions/me",this.bearerHeaders());return!e.success||!Array.isArray(e.data)?[]:e.data}async revokeSession(e){await this.http.delete(`/auth-service/sessions/me/${e}`,this.bearerHeaders())}async revokeAllSessions(){await this.http.delete("/auth-service/sessions/me",this.bearerHeaders())}async startSso(e){let t=await this.http.post("/auth-service/sso/oidc/start",{returnTo:e});if(!t.success||!t.data)throw new u(t.error?.code??"SSO_START_FAILED",t.error?.message??"SSO start failed");return t.data}async signInWithSso(e){let t=e??(typeof window<"u"?window.location.origin:"");if(!t)throw new u("SSO_NO_RETURN_TO","returnTo required outside browser context");let s=await this.startSso(t);typeof window<"u"&&(window.location.href=s.authorizationUrl)}consumeSsoTokenFromUrl(){return this.serialize(async()=>{if(typeof window>"u")return null;let e=window.location.hash.replace(/^#/,"");if(!e)return null;let t=new URLSearchParams(e),s=t.get("sendora_oidc_error");if(s)throw window.history.replaceState(null,"",window.location.pathname+window.location.search),new u("SSO_FAILED",decodeURIComponent(s));let n=t.get("sendora_oidc_token");if(!n)return null;window.history.replaceState(null,"",window.location.pathname+window.location.search),this.user!==null&&(await this.analytics.flush(),this.wipeLocalIdentity());let o=await this.http.post("/auth-service/token/refresh",{refreshToken:n});if(!o.success||!o.data)throw new u(o.error?.code??"SSO_EXCHANGE_FAILED",o.error?.message??"Could not exchange SSO token");let i=o.data;this.accessToken=i.accessToken,this.accessExpiresAt=Date.now()+i.expiresIn*1e3,this.storage.set(w,i.accessToken),this.storage.set(v,String(this.accessExpiresAt)),this.storage.set(d,i.refreshToken);let l=me(i.accessToken),a={id:l?.sub??"",email:l?.email??null,emailVerified:l?.email_verified??!0,name:l?.name??null,isAnonymous:!1};return this.user=a,this.storage.set(_,JSON.stringify(a)),this.analytics.identify(a.id),this.flags.setContext({userId:a.id}),a})}bearerHeaders(){if(!this.accessToken)throw new u("NOT_SIGNED_IN","Not signed in");return{Authorization:`Bearer ${this.accessToken}`}}signOut(){return this.serialize(async()=>{let e=this.storage.get(d);this.wipeLocalIdentity(),e&&this.http.post("/auth-service/token/revoke",{refreshToken:e}).catch(()=>{})})}serialize(e){let t=this.inflight.then(e,e);return this.inflight=t.catch(()=>{}),t}async refreshAccessToken(){if(this.refreshInflight)return this.refreshInflight;let e=this.storage.get(d);return e?(this.refreshInflight=(async()=>{try{let t=await this.http.post("/auth-service/token/refresh",{refreshToken:e});if(!t.success||!t.data||typeof t.data!="object"||typeof t.data.accessToken!="string"||typeof t.data.refreshToken!="string"||typeof t.data.expiresIn!="number")return null;let s=t.data;return this.accessToken=s.accessToken,this.accessExpiresAt=Date.now()+s.expiresIn*1e3,this.storage.set(w,s.accessToken),this.storage.set(v,String(this.accessExpiresAt)),this.storage.set(d,s.refreshToken),s.accessToken}catch{return null}finally{this.refreshInflight=null}})(),this.refreshInflight):null}persist(e){this.user=e.user,this.accessToken=e.tokens.accessToken,this.accessExpiresAt=Date.now()+e.tokens.expiresIn*1e3,this.storage.set(_,JSON.stringify(e.user)),this.storage.set(w,e.tokens.accessToken),this.storage.set(v,String(this.accessExpiresAt)),this.storage.set(d,e.tokens.refreshToken),this.analytics.identify(e.user.id),this.flags.setContext({userId:e.user.id})}wipeLocalIdentity(){this.user=null,this.accessToken=null,this.accessExpiresAt=0,this.storage.remove(_),this.storage.remove(w),this.storage.remove(v),this.storage.remove(d),this.analytics.reset(),this.analytics.dropQueue(),this.flags.reset(),this.chatbot.newSession()}};function E(r){let e=(4-r.length%4)%4,t=(r+"===".slice(0,e)).replace(/-/g,"+").replace(/_/g,"/"),s=atob(t),n=new ArrayBuffer(s.length),o=new Uint8Array(n);for(let i=0;i<s.length;i++)o[i]=s.charCodeAt(i);return n}function g(r){let e=r instanceof Uint8Array?r:new Uint8Array(r),t="";for(let s=0;s<e.length;s++)t+=String.fromCharCode(e[s]);return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function pe(r){return{challenge:E(r.challenge),rp:r.rp,user:{id:E(r.user.id),name:r.user.name,displayName:r.user.displayName},pubKeyCredParams:r.pubKeyCredParams,timeout:r.timeout,attestation:r.attestation,excludeCredentials:r.excludeCredentials?.map(e=>({id:E(e.id),type:"public-key",transports:e.transports})),authenticatorSelection:r.authenticatorSelection}}function ge(r){return{challenge:E(r.challenge),timeout:r.timeout,rpId:r.rpId,allowCredentials:r.allowCredentials?.map(e=>({id:E(e.id),type:"public-key",transports:e.transports})),userVerification:r.userVerification}}function fe(r){let e=r.response;return{id:r.id,rawId:g(r.rawId),type:r.type,response:{clientDataJSON:g(e.clientDataJSON),attestationObject:g(e.attestationObject),transports:typeof e.getTransports=="function"?e.getTransports():void 0},clientExtensionResults:r.getClientExtensionResults()}}function me(r){try{let e=r.split(".");if(e.length!==3)return null;let t=e[1].replace(/-/g,"+").replace(/_/g,"/"),s=atob(t+"===".slice(0,(4-t.length%4)%4));return JSON.parse(s)}catch{return null}}function ye(r){let e=r.response;return{id:r.id,rawId:g(r.rawId),type:r.type,response:{clientDataJSON:g(e.clientDataJSON),authenticatorData:g(e.authenticatorData),signature:g(e.signature),userHandle:e.userHandle?g(e.userHandle):null},clientExtensionResults:r.getClientExtensionResults()}}var we="2.2.0",O=class O{constructor(e){this.http=new P(e),this.storage=new U,this.consent=new T(e.defaultConsent===!0),this.analytics=new A(e,this.http,this.storage,this.consent),this.flags=new S(e,this.http,this.storage),this.surveys=new I(e,this.http,this.storage),this.messages=new k(e,this.http,this.storage),this.chatbot=new b(e,this.http,this.storage),this.auth=new R(e,this.http,this.storage,this.analytics,this.flags,this.chatbot),e.autoTrack!==!1&&typeof window<"u"&&this.analytics.page()}static init(e){return q(e.apiKey),new O(e)}static sanitizeHtml(e){return L(e)}static safeUrl(e){return f(e)}track(e,t){this.analytics.track(e,t)}page(e){this.analytics.page(e)}identify(e,t,s){this.analytics.identify(e,t,s),this.flags.setContext({userId:e})}reset(){this.analytics.reset(),this.flags.reset(),this.chatbot.newSession()}async flush(){await this.analytics.flush()}destroy(){this.analytics.destroy(),this.flags.destroy(),this.messages.destroy()}};O.version=we;var F=O,Ye=F;export{A as Analytics,R as Auth,u as AuthError,b as Chatbot,T as Consent,C as EmailAlreadyTakenError,S as FeatureFlags,k as InAppMessaging,F as SendoraCloud,I as Surveys,h as ValidationError,Ye as default,f as safeUrl,L as sanitizeHtml};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sendoracloud/sdk-web",
3
- "version": "2.2.0",
3
+ "version": "2.5.0",
4
4
  "description": "Sendora Cloud Web SDK — analytics, feature flags, surveys, in-app messaging, chatbot, auth. Zero dependencies, TypeScript, SSR-safe.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",