@sendoracloud/sdk-web 3.16.0 → 3.17.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 ce=Object.defineProperty;var Ne=Object.getOwnPropertyDescriptor;var xe=Object.getOwnPropertyNames;var Oe=Object.prototype.hasOwnProperty;var Me=(s,e)=>{for(var t in e)ce(s,t,{get:e[t],enumerable:!0})},He=(s,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of xe(e))!Oe.call(s,n)&&n!==t&&ce(s,n,{get:()=>e[n],enumerable:!(r=Ne(e,n))||r.enumerable});return s};var Fe=s=>He(ce({},"__esModule",{value:!0}),s);var kt={};Me(kt,{AlreadyIdentifiedError:()=>K,Analytics:()=>N,Audiences:()=>$,Auth:()=>V,AuthError:()=>u,Chatbot:()=>H,Consent:()=>F,CredentialInUseError:()=>P,EmailAlreadyTakenError:()=>C,FeatureFlags:()=>x,InAppMessaging:()=>M,LinkError:()=>h,Links:()=>B,SendoraCloud:()=>re,Surveys:()=>O,ValidationError:()=>f,WebPush:()=>W,default:()=>wt,extractShortcodeFromUrl:()=>Le,safeUrl:()=>S,sanitizeHtml:()=>J});module.exports=Fe(kt);var fe=/^[a-zA-Z][a-zA-Z0-9._:-]{0,127}$/;var Ke=new Set(["__proto__","constructor","prototype"]),f=class extends Error{constructor(e){super(`[SendoraCloud] ${e}`),this.name="SendoraValidationError"}};function me(s){if(typeof s!="string"||!fe.test(s))throw new f(`Invalid event name: must match ${fe}, got "${String(s)}".`)}function ye(s){if(s==null)return;if(typeof s!="object"||Array.isArray(s))throw new f("Properties must be a plain object, not an array or primitive.");let e=ve(s);if(e>5)throw new f(`Properties nested too deeply (${e} levels, max 5).`);we(s);let t;try{t=JSON.stringify(s).length}catch{throw new f("Properties contain a circular reference or unserializable value.")}if(t>32768)throw new f(`Properties too large (${t} bytes, max 32768).`)}function ve(s,e=0){if(e>6||s==null||typeof s!="object")return e;let t=e;for(let r of Object.keys(s)){let n=ve(s[r],e+1);n>t&&(t=n)}return t}function we(s){if(!(s==null||typeof s!="object"))for(let e of Object.keys(s)){if(Ke.has(e))throw new f(`Forbidden property key "${e}" (prototype pollution guard).`);we(s[e])}}var Ve=/^pk_[A-Za-z0-9_-]{16,}$/;function ke(s){if(!s||typeof s!="string")throw new f("apiKey is required.");if(s.startsWith("sk_")||s.startsWith("sendora_secret_"))throw new f("Secret keys must NEVER be used in browser SDK. Use a publishable key (pk_...).");if(!Ve.test(s))throw new f("Invalid publishable key format. Expected pk_... (17+ chars).")}function Ae(s){let e=s.replace(/\/$/,"");if(!(e.startsWith("http://localhost")||e.startsWith("http://127.0.0.1"))&&!e.startsWith("https://"))throw new f("apiUrl must be https:// (only http://localhost is allowed for dev).")}var Ie={name:"@sendoracloud/sdk-web",version:"3.16.0",description:"Sendora Cloud Web SDK \u2014 analytics, feature flags, surveys, in-app messaging, chatbot, auth, deep links (Branch / Firebase parity). Zero dependencies, TypeScript, SSR-safe.",type:"module",main:"./dist/index.cjs",module:"./dist/index.js",types:"./dist/index.d.ts",exports:{".":{types:"./dist/index.d.ts",import:"./dist/index.js",require:"./dist/index.cjs"}},files:["dist","README.md","LICENSE"],keywords:["sendora","analytics","feature-flags","surveys","in-app-messaging","chatbot","tracking","customer-engagement"],homepage:"https://sendoracloud.com/sdks",repository:{type:"git",url:"git+https://github.com/sendoracloud/sdk-web.git"},bugs:{url:"https://github.com/sendoracloud/sdk-web/issues"},author:"Sendora Cloud <hello@sendoracloud.com>",license:"Apache-2.0",publishConfig:{access:"public"},sideEffects:!1,scripts:{build:"tsup src/index.ts --format cjs,esm --dts --clean --minify",typecheck:"tsc --noEmit",prepublishOnly:"npm run build"},devDependencies:{typescript:"^5.4.0",tsup:"^8.0.0"}};var E=Ie.version;var $e="https://api.sendoracloud.com",Be=1e4,qe="@sendoracloud/sdk-web",Y=class{constructor(e){let t=(e.apiUrl??$e).replace(/\/$/,"");Ae(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,r){return this.request("POST",e,t,r)}patch(e,t,r){return this.request("PATCH",e,t,r)}delete(e,t){return this.request("DELETE",e,void 0,t)}async request(e,t,r,n){let i=`${this.baseUrl}/api/v1${t}`,o=typeof AbortController<"u"?new AbortController:null,a=o?setTimeout(()=>o.abort(),Be):null;try{let c=await fetch(i,{method:e,headers:{"Content-Type":"application/json","X-API-Key":this.apiKey,"X-Sendora-SDK-Name":qe,"X-Sendora-SDK-Version":E,...n??{}},body:r?JSON.stringify(r):void 0,signal:o?o.signal:void 0});a&&clearTimeout(a);let l;try{l=await c.json()}catch{l={success:!1,error:{code:"PARSE_ERROR",message:`HTTP ${c.status}`}}}if(this.debug&&console.debug(`[SendoraCloud] ${e} ${t} \u2192 ${c.status}`),!c.ok){if(t.startsWith("/auth-service/")&&!this.debug)console.warn(`[SendoraCloud] ${e} ${t} \u2192 HTTP ${c.status}`);else{let v=l?.error?.code??"HTTP_ERROR",w=l?.error?.message??`HTTP ${c.status}`,k=l?.error?.details?.fieldErrors,L=k?` (${Object.entries(k).map(([A,ne])=>`${A}: ${(ne??[]).join(", ")}`).join("; ")})`:"";console.warn(`[SendoraCloud] ${e} ${t} \u2192 HTTP ${c.status} \u2014 ${v}: ${w}${L}`)}l&&l.error&&(l.error={...l.error,status:c.status})}return l}catch(c){a&&clearTimeout(a);let l=c instanceof Error&&c.name==="AbortError"?"timeout":"error";return console.warn(`[SendoraCloud] ${e} ${t} network ${l}`),{success:!1,error:{code:"NETWORK_ERROR",message:l}}}}};var X="sendora_",Q=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 r=X+e;if(this.useLocalStorage)try{localStorage.setItem(r,t);return}catch{}this.memoryStore.set(r,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 r=localStorage.length-1;r>=0;r--){let n=localStorage.key(r);n&&n.startsWith(t)&&localStorage.removeItem(n)}}catch{}for(let r of Array.from(this.memoryStore.keys()))r.startsWith(t)&&this.memoryStore.delete(r)}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 U(s=""){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 r="";for(let n of e)r+=n.toString(16).padStart(2,"0");return s+r}var Se=500,ue=10,le=6e4,N=class{constructor(e,t,r,n){this.queue=[];this.timer=null;this.userId=null;this.identityToken=null;this.consecutiveFailures=0;this.nextFlushAfter=0;this.breakerOpenAt=0;this.breakerWarned=!1;this.http=t,this.storage=r,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,this.appVersion=e.appVersion,this.storage.get("schema_version")===null&&this.storage.set("schema_version","1");let i=this.storage.get("anonymous_id");if(i?this.anonymousId=i:(this.anonymousId=U("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(e,t){me(e),t&&ye(t),this.queue.length>=Se&&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,r){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=r?.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=U("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;this.consecutiveFailures>ue&&Date.now()-this.breakerOpenAt>=le&&(this.consecutiveFailures=0,this.breakerOpenAt=0,this.breakerWarned=!1);let e=this.queue.splice(0,100),t=this.getSessionId(),r=e.map(a=>({projectId:this.projectId,module:"custom",eventType:a.eventType,userId:a.userId,anonymousId:a.anonymousId,identityToken:a.identityToken,timestamp:a.timestamp??new Date().toISOString(),properties:a.properties??{},context:this.getContext(),...t?{sessionId:t}:{},consent:["analytics"]}));if((await this.http.post("/events/batch",{events:r})).success){this.consecutiveFailures=0,this.breakerOpenAt=0,this.breakerWarned=!1;return}if(this.consecutiveFailures++,this.consecutiveFailures>ue){this.breakerWarned||(this.breakerOpenAt=Date.now(),this.breakerWarned=!0,console.warn(`[SendoraCloud] Analytics circuit breaker open after ${ue} consecutive failed flushes \u2014 events will be dropped until a flush succeeds or ${le}ms elapse.`));return}let i=Math.min(le,1e3*2**this.consecutiveFailures);this.nextFlushAfter=Date.now()+i;let o=Math.max(0,Se-this.queue.length);o>0&&this.queue.unshift(...e.slice(0,o))}destroy(){this.timer&&clearInterval(this.timer),this.timer=null,this.flush()}getSessionId(){try{return typeof sessionStorage>"u"?void 0:sessionStorage.getItem("sendora_session_id")||void 0}catch{return}}getContext(){return typeof window>"u"?{sdk:{name:"@sendoracloud/sdk-web",version:E}}:{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,device:this.buildDevice(),sdk:{name:"@sendoracloud/sdk-web",version:E}}}buildDevice(){if(typeof navigator>"u")return;let e=navigator.userAgent||"",r=navigator.userAgentData?.mobile??/Mobi|Android|iPhone|iPod/i.test(e);return{type:/iPad|Tablet|(?:Android(?!.*Mobile))/i.test(e)?"tablet":r?"mobile":"desktop",os:"Web",osVersion:"",model:"",appVersion:this.resolveAppVersion(),browser:je(e)}}resolveAppVersion(){if(this.appVersion)return this.appVersion;if(typeof document<"u"){let r=document.querySelector('meta[name="sendora:app-version"]')?.getAttribute("content");if(r)return r}let e=globalThis.__SENDORA_APP_VERSION__;return typeof e=="string"?e:""}};function je(s){let e=[["Edge",/Edg\/(\d+)/],["Opera",/OPR\/(\d+)/],["Firefox",/Firefox\/(\d+)/],["Samsung Internet",/SamsungBrowser\/(\d+)/],["Chrome",/Chrome\/(\d+)/],["Safari",/Version\/(\d+)[^ ]* Safari/]];for(let[t,r]of e){let n=r.exec(s);if(n)return`${t} ${n[1]}`}return"Unknown"}var x=class{constructor(e,t,r){this.cache=new Map;this.refreshInterval=null;this.http=t,this.storage=r;let n=this.storage.get("flags");if(n)try{let i=JSON.parse(n);if(Array.isArray(i))for(let o of i)de(o)&&this.cache.set(o.key,o)}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 r=await this.http.post("/feature-flags/evaluate",{key:e,userId:this.userId,entityId:this.entityId,environment:this.environment});return r.success&&r.data&&de(r.data)?(this.cache.set(e,r.data),this.persistCache(),r.data.value):null}async isEnabled(e,t=!1){let r=await this.getFlag(e);return typeof r=="boolean"?r: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)de(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,r]of this.cache)e[t]=r.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 de(s){if(s==null||typeof s!="object")return!1;let e=s;return Object.prototype.hasOwnProperty.call(e,"__proto__")||Object.prototype.hasOwnProperty.call(e,"constructor")?!1:typeof e.key=="string"&&"value"in e}var ze=32*1024,O=class{constructor(e,t,r){this.activeSurvey=null;this.http=t,this.storage=r}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 r=await this.http.get(`/surveys/${encodeURIComponent(e)}/config`);return!r.success||!r.data||!Ge(r.data)?null:(this.activeSurvey=r.data,this.storage.set(t,"1"),this.onSurveyDisplay&&this.onSurveyDisplay(r.data),r.data)}async submitResponse(e,t,r=!0){let n=0;try{n=JSON.stringify(t).length}catch{return!1}if(n>ze)return!1;let i=await this.http.post("/surveys/responses",{surveyId:e,answers:t,completed:r});return i.success&&(this.activeSurvey=null),i.success}dismiss(){this.activeSurvey=null}getActiveSurvey(){return this.activeSurvey}};function Ge(s){if(s==null||typeof s!="object")return!1;let e=s;return typeof e.id!="string"||typeof e.name!="string"||!Array.isArray(e.questions)?!1:e.questions.every(Ye)}function Ye(s){if(s==null||typeof s!="object")return!1;let e=s;return typeof e.id=="string"&&typeof e.text=="string"}function S(s){if(!(!s||typeof s!="string"))try{let e=typeof window<"u"?window.location.href:"https://local/",t=new URL(s,e);return t.protocol==="http:"||t.protocol==="https:"?t.href:void 0}catch{return}}var Xe=new Set(["b","i","u","em","strong","a","p","br","span","ul","ol","li","h1","h2","h3","h4"]),Qe={a:new Set(["href","target","rel"])};function J(s){if(typeof DOMParser>"u")return"";let t=new DOMParser().parseFromString(`<div>${s}</div>`,"text/html").body.firstElementChild;return t?(be(t),t.innerHTML):""}function be(s){let e=Array.from(s.children);for(let t of e){let r=t.tagName.toLowerCase();if(!Xe.has(r)){let i=document.createTextNode(t.textContent??"");t.replaceWith(i);continue}let n=Qe[r]??new Set;for(let i of Array.from(t.attributes)){if(!n.has(i.name.toLowerCase())){t.removeAttribute(i.name);continue}if(i.name.toLowerCase()==="href"){let o=S(i.value);o?t.setAttribute("href",o):t.removeAttribute(i.name)}}r==="a"&&(t.setAttribute("rel","noopener noreferrer"),t.setAttribute("target","_blank")),be(t)}}var M=class{constructor(e,t,r){this.messages=[];this.pollInterval=null;this.http=t,this.storage=r}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(Je);for(let t of this.messages){let r=`iam_shown_${t.id}`;if(!this.storage.get(r))return this.storage.set(r,"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 Je(s){return{...s,content:{...s.content,ctaUrl:S(s.content.ctaUrl),imageUrl:S(s.content.imageUrl)}}}var Ze=4e3,H=class{constructor(e,t,r){this.chatbotId=null;this.messages=[];this.http=t,this.storage=r;let n=this.storage.get("chat_session_id");this.sessionId=n??U("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>Ze)return null;let r={role:"user",content:e,timestamp:new Date().toISOString()};this.messages.push(r);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 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=U("chat_"),this.storage.set("chat_session_id",this.sessionId)}getSessionId(){return this.sessionId}};var F=class{constructor(e,t){this.listeners=new Set;this.granted=e,this.http=t}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)}async record(e){if(!this.http)throw new Error("consent.record requires SDK init (HttpClient unavailable)");let t=await this.http.post("/consent/record",e);if(!t.success||!t.data)throw new Error(t.error?.message??"consent.record failed");return t.data}async requestDeletion(e){if(!this.http)throw new Error("consent.requestDeletion requires SDK init (HttpClient unavailable)");let t=await this.http.post("/consent/deletion-request",e);if(!t.success||!t.data)throw new Error(t.error?.message??"consent.requestDeletion failed");return t.data}emit(){for(let e of this.listeners)try{e(this.granted)}catch{}}};var T="auth_access_token",R="auth_access_expires",m="auth_refresh_token",_="auth_user",et=3e4,tt=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function y(s){if(!s||typeof s!="object")return!1;let e=s,t=e.user,r=e.tokens;return!(!t||typeof t.id!="string"||t.id.length===0||!r||typeof r.accessToken!="string"||r.accessToken.length===0||typeof r.refreshToken!="string"||r.refreshToken.length===0||typeof r.expiresIn!="number"||r.expiresIn<=0||t.isAnonymous!==void 0&&typeof t.isAnonymous!="boolean"||t.email!==void 0&&t.email!==null&&typeof t.email!="string"||t.emailVerified!==void 0&&typeof t.emailVerified!="boolean"||t.name!==void 0&&t.name!==null&&typeof t.name!="string"||t.signupMethod!==void 0&&t.signupMethod!==null&&typeof t.signupMethod!="string"||t.lastLoginMethod!==void 0&&t.lastLoginMethod!==null&&typeof t.lastLoginMethod!="string"||e.retiredAnonUserId!==void 0&&typeof e.retiredAnonUserId!="string")}var C=class extends Error{constructor(e="An account with this email already exists"){super(e),this.name="EmailAlreadyTakenError"}},K=class extends Error{constructor(e="Already signed in with an identity; use link*() to add a credential, or sign out first"){super(e),this.name="AlreadyIdentifiedError"}},P=class extends Error{constructor(t="This credential is already linked to a different account",r){super(t);this.code="CREDENTIAL_IN_USE";this.kind="credential_in_use";this.retryable=!1;this.name="CredentialInUseError",this.provider=r?.provider,this.collision=r?.collision,this.status=r?.status}};function Ee(s){let e=s?.error?.details;if(!e||typeof e!="object")return{};let t=e;return{provider:typeof t.provider=="string"?t.provider:void 0,collision:t.collision==="identity"||t.collision==="email"?t.collision:void 0}}var rt=new Set(["network","server","rate_limited","account_locked"]);function st(s,e){switch(s){case"NETWORK_ERROR":case"NETWORK_TIMEOUT":return"network";case"PARSE_ERROR":return"server";case"RATE_LIMITED":case"RATE_LIMIT":case"RATE_LIMIT_EXCEEDED":case"QUOTA_EXCEEDED":return"rate_limited";case"ACCOUNT_LOCKED":return"account_locked";case"CREDENTIAL_IN_USE":return"credential_in_use";case"NOT_ANONYMOUS":case"FORBIDDEN_NON_ANONYMOUS":return"already_identified";case"PASSKEY_USER_CANCELLED":case"GAME_CENTER_CANCELLED":case"PLAY_GAMES_CANCELLED":case"SSO_CANCELLED":return"cancelled";case"GAME_CENTER_UNAVAILABLE":case"PLAY_GAMES_UNAVAILABLE":case"ENTITLEMENT_ERROR":case"tier_required":return"config";case"UNAUTHORIZED":case"INVALID_CREDENTIALS":case"INVALID_REFRESH_TOKEN":case"EMAIL_OTP_INVALID":case"MAGIC_LINK_INVALID":case"PASSKEY_RP_MISMATCH":case"NOT_SIGNED_IN":return"invalid_credential";default:break}if(typeof e=="number"){if(e===429)return"rate_limited";if(e>=500)return"server";if(e>=400)return"invalid_credential"}return"unknown"}var u=class extends Error{constructor(e,t,r){super(t),this.name="AuthError",this.code=e,this.kind=r?.kind??st(e,r?.status),this.retryable=rt.has(this.kind),this.retryAfterSeconds=r?.retryAfterSeconds,this.status=r?.status}};function nt(s){if(s instanceof u||s instanceof C||s instanceof K||s instanceof P)return s;let e=s?.name;return e==="AbortError"||e==="TimeoutError"||e==="NetworkTimeoutError"?new u("NETWORK_TIMEOUT",s instanceof Error?s.message:"timeout",{kind:"network"}):new u("UNKNOWN_ERROR",s instanceof Error?s.message:String(s),{kind:"unknown"})}function p(s){let e=s.error?.status,t=s.error?.details?.retryAfterSeconds;return{status:typeof e=="number"?e:void 0,retryAfterSeconds:typeof t=="number"&&Number.isFinite(t)&&t>0?t:void 0}}var V=class{constructor(e,t,r,n,i,o){this.user=null;this.accessToken=null;this.accessExpiresAt=0;this.inflight=Promise.resolve();this.refreshInflight=null;this.refreshFailureCount=0;this.refreshCooldownUntil=0;this.visibilityResetUnsub=null;this.proactiveRefreshTimer=null;this.proactiveRefreshCleanup=null;this.takeoverListeners=new Set;this.lastTakeover=null;this.deletionCancelledListeners=new Set;this.lastDeletionCancelled=null;this.authStateListeners=new Set;this.pendingAnonTakeover=null;this.hydrated=!1;this.http=t,this.storage=r,this.analytics=n,this.flags=i,this.chatbot=o;let a=this.storage.get(_),c=this.storage.get(T),l=this.storage.get(m),g=this.storage.get(R);if(a&&(c||l))try{let v=JSON.parse(a);if(typeof v.id!="string"||v.id.length===0)throw new Error("invalid cache");this.user=v,c?(this.accessToken=c,this.accessExpiresAt=g?Number(g):0):(this.accessToken=null,this.accessExpiresAt=0),this.analytics.identify(this.user.id)}catch{this.storage.remove(_),this.storage.remove(T),this.storage.remove(R)}this.hydrated=!0}getCurrentUser(){return this.user}onDeviceTakeover(e){return this.takeoverListeners.add(e),()=>{this.takeoverListeners.delete(e)}}getLastDeviceTakeover(){return this.lastTakeover}onAuthStateChanged(e){if(this.authStateListeners.add(e),this.hydrated&&this.user){let t=this.user;try{e({event:"signed_in",user:t})}catch{}}return()=>{this.authStateListeners.delete(e)}}emitAuthState(e){for(let t of this.authStateListeners)try{t(e)}catch{}}fireDeviceTakeover(e,t){if(!e||!tt.test(e))return;let r={retiredAnonUserId:e,identifiedUserId:t,at:new Date};this.lastTakeover=r;for(let n of this.takeoverListeners)try{n(r)}catch{}this.emitAuthState({event:"device_takeover",user:this.user,retiredAnonUserId:e})}onDeletionCancelled(e){return this.deletionCancelledListeners.add(e),()=>{this.deletionCancelledListeners.delete(e)}}getLastDeletionCancelled(){return this.lastDeletionCancelled}fireDeletionCancelled(e){let t={userId:e,at:new Date};this.lastDeletionCancelled=t;for(let r of this.deletionCancelledListeners)try{r(t)}catch{}this.emitAuthState({event:"deletion_cancelled",user:this.user})}takeoverHint(){if(this.user?.isAnonymous)return this.storage.get(m)??void 0}async getAccessToken(){return this.accessToken&&this.accessExpiresAt&&Date.now()<this.accessExpiresAt-et?this.accessToken:this.storage.get(m)?this.refreshInflight?this.refreshInflight:Date.now()<this.refreshCooldownUntil?null:this.refreshAccessToken():null}getAccessTokenSync(){return this.accessToken}signInAnonymously(e){return this.serialize(async()=>{let t=this.user;if(!e?.forceNew&&t?.isAnonymous&&this.storage.get(m))return t;let r=await this.http.post("/auth-service/anonymous",{name:e?.name,metadata:e?.metadata});if(!r.success||!y(r.data))throw new u(r.error?.code??"AUTH_ERROR",r.error?.message??"Anonymous signup failed",p(r));return this.persist(r.data),r.data.user})}signUp(e,t,r){return this.serialize(async()=>{let n=this.storage.get(m);if(n){let o=await this.http.post("/auth-service/upgrade",{refreshToken:n,email:e,password:t,name:r?.name});if(o.success&&y(o.data))return this.persist(o.data),o.data.user;let a=o.error?.code??"",c=p(o),l=c.status;if(a==="NOT_ANONYMOUS"||a==="FORBIDDEN_NON_ANONYMOUS")throw new K(o.error?.message);if(a==="CONFLICT"||a==="EMAIL_ALREADY_TAKEN"||l===409)throw new C(o.error?.message);if(a==="INVALID_REFRESH"||a==="UNAUTHORIZED"||a==="REFRESH_EXPIRED")await this.analytics.flush(),this.wipeLocalIdentity("session_expired");else throw new u(a||"AUTH_ERROR",o.error?.message??"Upgrade failed",c)}let i=await this.http.post("/auth-service/signup",{email:e,password:t,name:r?.name,metadata:r?.metadata});if(!i.success||!y(i.data)){let o=i.error?.code??"";throw o==="CONFLICT"?new C(i.error?.message):new u(o||"AUTH_ERROR",i.error?.message??"Signup failed",p(i))}return this.persist(i.data),i.data.user})}signIn(e,t){return this.serialize(async()=>{let r=this.takeoverHint(),n=await this.http.post("/auth-service/login",{email:e,password:t,...r?{prevAnonRefreshToken:r}:{}});if(!n.success||!n.data)throw new u(n.error?.code??"AUTH_ERROR",n.error?.message??"Login failed",p(n));let i=n.data;if(i.mfaRequired===!0&&typeof i.mfaChallengeToken=="string"){r&&(this.pendingAnonTakeover={challengeToken:i.mfaChallengeToken,prevAnonRefreshToken:r});let o=i.user;return{mfaRequired:!0,mfaChallengeToken:i.mfaChallengeToken,userId:o?.id??""}}if(!y(n.data))throw new u(n.error?.code??"AUTH_ERROR",n.error?.message??"Login failed",p(n));return await this.replaceLocalIdentity(),this.persist(n.data),n.data.user})}loginSocial(e){return this.serialize(async()=>{let t=this.takeoverHint(),{link:r,onCredentialInUse:n,...i}=e,o=await this.http.post("/auth-service/login/social",{...i,...t?{prevAnonRefreshToken:t}:{},...r?{linkAnonymous:!0}:{},...n?{onCredentialInUse:n}:{}});if(!o.success||!y(o.data))throw o.error?.code==="CREDENTIAL_IN_USE"?new P(o.error.message,{...Ee(o),status:p(o).status}):new u(o.error?.code??"SOCIAL_LOGIN_FAILED",o.error?.message??"Social login failed",p(o));return await this.replaceLocalIdentity(),this.persist(o.data),o.data.user})}signInWithGoogle(e,t,r){return this.loginSocial({provider:"google",code:e,redirectUri:t,link:r})}signInWithGitHub(e,t){return this.loginSocial({provider:"github",code:e,redirectUri:t})}signInWithApple(e){return this.loginSocial({provider:"apple",...e})}signInWithMicrosoft(e,t){return this.loginSocial({provider:"microsoft",code:e,redirectUri:t})}signInWithLinkedIn(e,t){return this.loginSocial({provider:"linkedin",code:e,redirectUri:t})}signInWithFacebook(e,t){return this.loginSocial({provider:"facebook",code:e,redirectUri:t})}linkEmailPassword(e,t){return this.linkCredential("/auth-service/me/link/email",{email:e,password:t})}linkSocial(e){return this.linkCredential("/auth-service/me/link/social",e)}linkGoogle(e){return this.linkSocial({provider:"google",...e})}linkApple(e){return this.linkSocial({provider:"apple",...e})}async linkCredential(e,t){let r=await this.getAccessToken();if(!r)throw new u("NOT_SIGNED_IN","Sign in before linking a credential");let n=await this.http.post(e,t,{Authorization:`Bearer ${r}`});if(!n.success||!n.data?.user){let i=n.error?.code??"LINK_FAILED";throw i==="CREDENTIAL_IN_USE"?new P(n.error?.message,{...Ee(n),status:p(n).status}):new u(i,n.error?.message??"Failed to link credential",p(n))}return n.data.upgraded&&y(n.data)?(this.persist(n.data),n.data.user):(this.updateLocalUser(n.data.user),n.data.user)}updateLocalUser(e){this.user=e,this.storage.set(_,JSON.stringify(e))}async listLinkedIdentities(){let e=await this.getAccessToken();if(!e)throw new u("NOT_SIGNED_IN","Sign in before listing linked identities");let t=await this.http.get("/auth-service/me/identities",{Authorization:`Bearer ${e}`});if(!t.success||!t.data)throw new u(t.error?.code??"IDENTITIES_FAILED",t.error?.message??"Failed to list linked identities",p(t));return{identities:t.data.identities??[],hasPassword:!!t.data.hasPassword}}signInWithDiscord(e,t){return this.loginSocial({provider:"discord",code:e,redirectUri:t})}challengeMfa(e,t){return this.serialize(async()=>{let n=(this.pendingAnonTakeover?.challengeToken===e?this.pendingAnonTakeover.prevAnonRefreshToken:void 0)??this.takeoverHint(),i=await this.http.post("/auth-service/mfa/challenge",{challengeToken:e,code:t,...n?{prevAnonRefreshToken:n}:{}});if(!i.success||!y(i.data))throw new u(i.error?.code??"MFA_CHALLENGE_FAILED",i.error?.message??"Invalid MFA code",p(i));return await this.replaceLocalIdentity(),this.persist(i.data),i.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()=>{let r=this.takeoverHint(),n=await this.http.post("/auth-service/email-otp/verify",{email:e,code:t,...r?{prevAnonRefreshToken:r}:{}});if(!n.success||!y(n.data))throw new u(n.error?.code??"EMAIL_OTP_INVALID",n.error?.message??"Wrong or expired code",p(n));return await this.replaceLocalIdentity(),this.persist(n.data),n.data.user})}async requestPasswordReset(e){let t=await this.http.post("/auth-service/password/forgot",{email:e});if(!t.success)throw new u(t.error?.code??"PASSWORD_RESET_REQUEST_FAILED",t.error?.message??"Failed to send reset email")}async resetPassword(e,t){let r=await this.http.post("/auth-service/password/reset",{token:e,newPassword:t});if(!r.success)throw new u(r.error?.code??"PASSWORD_RESET_FAILED",r.error?.message??"Reset failed")}async verifyEmail(e){let t=await this.http.post("/auth-service/email/verify",{token:e});if(!t.success)throw new u(t.error?.code??"EMAIL_VERIFICATION_FAILED",t.error?.message??"Verification failed")}async sendVerificationEmail(){let e=await this.getAccessToken();if(!e)throw new u("UNAUTHORIZED","Sign in before requesting a verification email");let t=await this.http.post("/auth-service/email/verify/resend",{},{Authorization:`Bearer ${e}`});if(!t.success)throw new u(t.error?.code??"VERIFICATION_RESEND_FAILED",t.error?.message??"Failed to send verification email")}verifyMagicLink(e){return this.serialize(async()=>{let t=this.takeoverHint(),r=await this.http.post("/auth-service/magic-link/verify",{token:e,...t?{prevAnonRefreshToken:t}:{}});if(!r.success||!y(r.data))throw new u(r.error?.code??"MAGIC_LINK_INVALID",r.error?.message??"Invalid or expired magic link",p(r));return await this.replaceLocalIdentity(),this.persist(r.data),r.data.user})}async enrollMfa(){let e=await this.http.post("/auth-service/mfa/enroll/start",{},await 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},await this.bearerHeaders());return!!(t.success&&t.data?.confirmed)}async disableMfa(){await this.http.post("/auth-service/mfa/disable",{},await 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",{},await this.bearerHeaders());if(!t.success||!t.data)throw new u(t.error?.code??"PASSKEY_START_FAILED",t.error?.message??"Passkey start failed");let r=ot(t.data),n;try{let a=await navigator.credentials.create({publicKey:r});if(!a)throw new Error("create returned null");n=a}catch(a){throw new u("PASSKEY_USER_CANCELLED",a instanceof Error?a.message:"User cancelled passkey registration")}let i=ct(n),o=await this.http.post("/auth-service/passkeys/register/finish",{response:i,name:e},await this.bearerHeaders());if(!o.success||!o.data)throw new u(o.error?.code??"PASSKEY_FINISH_FAILED",o.error?.message??"Passkey verification failed");return o.data}signInWithPasskey(e){return this.serialize(async()=>{if(typeof navigator>"u"||!navigator.credentials)throw new u("PASSKEY_UNSUPPORTED","WebAuthn not supported in this environment");let t=this.takeoverHint(),r=await this.http.post("/auth-service/passkeys/authenticate/start",{userId:e??null});if(!r.success||!r.data)throw new u(r.error?.code??"PASSKEY_START_FAILED",r.error?.message??"Passkey start failed",p(r));let n=at(r.data),i;try{let c=await navigator.credentials.get({publicKey:n});if(!c)throw new Error("get returned null");i=c}catch(c){throw new u("PASSKEY_USER_CANCELLED",c instanceof Error?c.message:"User cancelled passkey signin")}let o=dt(i),a=await this.http.post("/auth-service/passkeys/authenticate/finish",{response:o,userId:e??null,...t?{prevAnonRefreshToken:t}:{}});if(!a.success||!y(a.data))throw new u(a.error?.code??"PASSKEY_AUTH_FAILED",a.error?.message??"Passkey signin failed",p(a));return await this.replaceLocalIdentity(),this.persist(a.data),a.data.user})}async listPasskeys(){let e=await this.http.get("/auth-service/passkeys",await this.bearerHeaders());return!e.success||!Array.isArray(e.data)?[]:e.data}async deletePasskey(e){await this.http.delete(`/auth-service/passkeys/${e}`,await this.bearerHeaders())}async listMySessions(){let e=await this.http.get("/auth-service/sessions/me",await this.bearerHeaders());return!e.success||!Array.isArray(e.data)?[]:e.data}async revokeSession(e){await this.http.delete(`/auth-service/sessions/me/${e}`,await this.bearerHeaders())}async revokeAllSessions(){await this.http.delete("/auth-service/sessions/me",await this.bearerHeaders())}async startSso(e){let t=this.takeoverHint(),r=await this.http.post("/auth-service/sso/oidc/start",{returnTo:e,...t?{prevAnonRefreshToken:t}:{}});if(!r.success||!r.data)throw new u(r.error?.code??"SSO_START_FAILED",r.error?.message??"SSO start failed");return r.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 r=await this.startSso(t);typeof window<"u"&&(window.location.href=r.authorizationUrl)}async startSaml(e){let t=this.takeoverHint(),r=await this.http.post("/auth-service/sso/saml/start",{returnTo:e,...t?{prevAnonRefreshToken:t}:{}});if(!r.success||!r.data)throw new u(r.error?.code??"SAML_START_FAILED",r.error?.message??"SAML start failed");return r.data}async signInWithSaml(e){let t=e??(typeof window<"u"?window.location.origin:"");if(!t)throw new u("SAML_NO_RETURN_TO","returnTo required outside browser context");let r=await this.startSaml(t);typeof window<"u"&&(window.location.href=r.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),r=t.get("sendora_oidc_error")??t.get("sendora_saml_error");if(r)throw window.history.replaceState(null,"",window.location.pathname+window.location.search),new u("SSO_FAILED",decodeURIComponent(r));let n=t.get("sendora_oidc_token")??t.get("sendora_saml_token");if(!n)return null;let i=t.get("sendora_retired_anon");window.history.replaceState(null,"",window.location.pathname+window.location.search);let o=await this.http.post("/auth-service/token/refresh",{refreshToken:n}),a=o.success?Te(o.data):null;if(!a)throw new u(o.error?.code??"SSO_EXCHANGE_FAILED",o.error?.message??"Could not exchange SSO token",p(o));let c={...a,tokenType:"Bearer"};await this.replaceLocalIdentity(),this.accessToken=c.accessToken,this.accessExpiresAt=Date.now()+c.expiresIn*1e3,this.storage.set(T,c.accessToken),this.storage.set(R,String(this.accessExpiresAt)),this.storage.set(m,c.refreshToken);let l=lt(c.accessToken),g={id:l?.sub??"",email:l?.email??null,emailVerified:l?.email_verified??!0,name:l?.name??null,isAnonymous:!1};return this.user=g,this.storage.set(_,JSON.stringify(g)),this.analytics.identify(g.id),this.flags.setContext({userId:g.id}),this.emitAuthState({event:"signed_in",user:g}),i&&this.fireDeviceTakeover(i,g.id),g})}async bearerHeaders(){let e=await this.getAccessToken();if(!e)throw new u("NOT_SIGNED_IN","Not signed in");return{Authorization:`Bearer ${e}`}}signOut(){return this.serialize(async()=>{let e=this.storage.get(m);this.wipeLocalIdentity("user"),e&&this.http.post("/auth-service/token/revoke",{refreshToken:e}).catch(()=>{})})}serialize(e){let t=async()=>{try{return await e()}catch(n){throw nt(n)}},r=this.inflight.then(t,t);return this.inflight=r.catch(()=>{}),r}async refreshAccessToken(){if(this.refreshInflight)return this.refreshInflight;let e=this.storage.get(m);if(!e)return null;let t=typeof navigator<"u"&&"locks"in navigator,r=async()=>{let n=this.storage.get(m);if(n&&n!==e){let i=this.storage.get(T),o=this.storage.get(R);if(i&&o){let a=Number(o);if(Number.isFinite(a)&&a>Date.now())return this.accessToken=i,this.accessExpiresAt=a,i}}try{let i=n??e,o=await this.http.post("/auth-service/token/refresh",{refreshToken:i});if(!o.success)return it(o.error?.code)&&this.tokenStillCurrent(i)?(this.wipeLocalIdentity("session_expired"),this.refreshFailureCount=0,this.refreshCooldownUntil=0,null):(this.armBackoff(),null);let a=Te(o.data);if(!a)return this.armBackoff(),null;if(!this.tokenStillCurrent(i))return null;if(this.accessToken=a.accessToken,this.accessExpiresAt=Date.now()+a.expiresIn*1e3,this.storage.set(T,a.accessToken),this.storage.set(R,String(this.accessExpiresAt)),this.storage.set(m,a.refreshToken),this.user===null){let c=ut(o.data);c&&(this.user=c,this.storage.set(_,JSON.stringify(c)),this.analytics.identify(c.id),this.emitAuthState({event:"signed_in",user:c}))}return this.refreshFailureCount=0,this.refreshCooldownUntil=0,a.accessToken}catch{return this.armBackoff(),null}};return this.refreshInflight=(async()=>{try{return t?await navigator.locks.request("sendora-authservice-refresh",{mode:"exclusive"},r):await r()}finally{this.refreshInflight=null}})(),this.refreshInflight}armBackoff(){this.refreshFailureCount+=1;let e=Math.min(3e4,1e3*Math.pow(2,this.refreshFailureCount-1));this.refreshCooldownUntil=Date.now()+e}resetRefreshState(){this.refreshFailureCount=0,this.refreshCooldownUntil=0}startVisibilityRefreshReset(){if(this.visibilityResetUnsub||typeof document>"u")return;let e=()=>{document.visibilityState==="visible"&&this.resetRefreshState()};document.addEventListener("visibilitychange",e),this.visibilityResetUnsub=()=>document.removeEventListener("visibilitychange",e)}stopVisibilityRefreshReset(){this.visibilityResetUnsub&&(this.visibilityResetUnsub(),this.visibilityResetUnsub=null)}startProactiveRefreshCron(){if(typeof window>"u"||this.proactiveRefreshTimer)return;let e=.8,t=6e4,r=3e4,n=async()=>{if(!this.accessToken||!this.accessExpiresAt)return;let o=Date.now(),a=this.accessExpiresAt-o;if(a<=0)return;let l=Math.max(a,5*6e4)*(1-e)+(Math.random()*r*2-r);a<=l&&await this.refreshAccessToken()};this.proactiveRefreshTimer=window.setInterval(()=>{n()},t);let i=()=>{document.visibilityState==="visible"&&n()};document.addEventListener("visibilitychange",i),this.proactiveRefreshCleanup=()=>{this.proactiveRefreshTimer&&(window.clearInterval(this.proactiveRefreshTimer),this.proactiveRefreshTimer=null),document.removeEventListener("visibilitychange",i)}}stopProactiveRefreshCron(){this.proactiveRefreshCleanup&&(this.proactiveRefreshCleanup(),this.proactiveRefreshCleanup=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(T,e.tokens.accessToken),this.storage.set(R,String(this.accessExpiresAt)),this.storage.set(m,e.tokens.refreshToken),this.analytics.identify(e.user.id),this.flags.setContext({userId:e.user.id}),this.startProactiveRefreshCron(),this.startVisibilityRefreshReset(),this.emitAuthState({event:"signed_in",user:e.user}),e.retiredAnonUserId&&this.fireDeviceTakeover(e.retiredAnonUserId,e.user.id),e.reactivatedFromDeletion&&this.fireDeletionCancelled(e.user.id)}async replaceLocalIdentity(){this.user!==null&&(await this.analytics.flush(),this.wipeLocalIdentity("replaced"))}tokenStillCurrent(e){return this.storage.get(m)===e}wipeLocalIdentity(e="replaced"){this.user=null,this.accessToken=null,this.accessExpiresAt=0,this.lastTakeover=null,this.lastDeletionCancelled=null,this.pendingAnonTakeover=null,this.storage.remove(_),this.storage.remove(T),this.storage.remove(R),this.storage.remove(m),this.analytics.reset(),this.analytics.dropQueue(),this.flags.reset(),this.chatbot.newSession(),this.stopProactiveRefreshCron(),this.stopVisibilityRefreshReset(),this.refreshFailureCount=0,this.refreshCooldownUntil=0,e!=="replaced"&&this.emitAuthState({event:"signed_out",user:null,reason:e})}};function it(s){return s?s==="INVALID_REFRESH_TOKEN":!1}function j(s){let e=(4-s.length%4)%4,t=(s+"===".slice(0,e)).replace(/-/g,"+").replace(/_/g,"/"),r=atob(t),n=new ArrayBuffer(r.length),i=new Uint8Array(n);for(let o=0;o<r.length;o++)i[o]=r.charCodeAt(o);return n}function b(s){let e=s instanceof Uint8Array?s:new Uint8Array(s),t="";for(let r=0;r<e.length;r++)t+=String.fromCharCode(e[r]);return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function ot(s){return{challenge:j(s.challenge),rp:s.rp,user:{id:j(s.user.id),name:s.user.name,displayName:s.user.displayName},pubKeyCredParams:s.pubKeyCredParams,timeout:s.timeout,attestation:s.attestation,excludeCredentials:s.excludeCredentials?.map(e=>({id:j(e.id),type:"public-key",transports:e.transports})),authenticatorSelection:s.authenticatorSelection}}function at(s){return{challenge:j(s.challenge),timeout:s.timeout,rpId:s.rpId,allowCredentials:s.allowCredentials?.map(e=>({id:j(e.id),type:"public-key",transports:e.transports})),userVerification:s.userVerification}}function ct(s){let e=s.response;return{id:s.id,rawId:b(s.rawId),type:s.type,response:{clientDataJSON:b(e.clientDataJSON),attestationObject:b(e.attestationObject),transports:typeof e.getTransports=="function"?e.getTransports():void 0},clientExtensionResults:s.getClientExtensionResults()}}function Te(s){if(!s||typeof s!="object")return null;let e=s.tokens,t=e&&typeof e=="object"?e:s;return typeof t.accessToken!="string"||!t.accessToken||typeof t.refreshToken!="string"||!t.refreshToken||typeof t.expiresIn!="number"||t.expiresIn<=0?null:{accessToken:t.accessToken,refreshToken:t.refreshToken,expiresIn:t.expiresIn}}function ut(s){if(!s||typeof s!="object")return null;let e=s.user;if(!e||typeof e!="object")return null;let t=e.id;return typeof t!="string"||t.length===0?null:e}function lt(s){try{let e=s.split(".");if(e.length!==3)return null;let t=e[1].replace(/-/g,"+").replace(/_/g,"/"),r=atob(t+"===".slice(0,(4-t.length%4)%4));return JSON.parse(r)}catch{return null}}function dt(s){let e=s.response;return{id:s.id,rawId:b(s.rawId),type:s.type,response:{clientDataJSON:b(e.clientDataJSON),authenticatorData:b(e.authenticatorData),signature:b(e.signature),userHandle:e.userHandle?b(e.userHandle):null},clientExtensionResults:s.getClientExtensionResults()}}function ht(s){return s===!1?{pageViews:!1,sessionStart:!1,appOpen:!1,appBackground:!1,engagement:!1}:s===!0||s===void 0?{pageViews:!0,sessionStart:!0,appOpen:!0,appBackground:!0,engagement:!0}:{pageViews:s.pageViews??!0,sessionStart:s.sessionStart??!0,appOpen:s.appOpen??!0,appBackground:s.appBackground??!0,engagement:s.engagement??!0}}var pt=250,Re=360*60*1e3;function Pe(s,e){if(typeof window>"u")return()=>{};let t=ht(e.autoTrack),r=e.sessionIdleMs??18e5,n=[],i="sendora_session_id",o="sendora_session_last_activity";function a(){return _e(i)??""}function c(d){Ce(i,d)}function l(){let d=_e(o);return d&&Number(d)||0}function g(d){Ce(o,String(d))}function v(){return`sess_${Date.now().toString(36)}_${Math.random().toString(36).slice(2,10)}`}function w(d){let I=Date.now(),q=l();if(!a()||q&&I-q>r){let G=v();c(G),d&&t.sessionStart&&s.track("session.start",{sessionId:G})}g(I)}w(t.sessionStart),t.appOpen&&s.track("app.opened",{sessionId:a()||void 0,url:window.location.origin+window.location.pathname,referrer:document.referrer||void 0});let k=window.location.pathname,L=0,A=document.visibilityState==="visible"?Date.now():0;function ne(){A>0&&(L+=Date.now()-A,A=0)}function z(){if(!t.engagement)return;ne();let d=L;if(L=0,!(d<pt)){d>Re&&(d=Re);try{s.track("app.engagement",{durationMs:Math.round(d),screen:k,sessionId:a()||void 0})}catch{}}}function De(d){k=d,L=0,A=document.visibilityState==="visible"?Date.now():0}if(t.pageViews||t.engagement){t.pageViews&&s.page();let d=()=>{z(),w(!0),t.pageViews&&s.page(),De(window.location.pathname)},I=()=>d();window.addEventListener("popstate",I),n.push(()=>window.removeEventListener("popstate",I));let q=window;if(!q.__sendoraHistoryPatched){q.__sendoraHistoryPatched=!0;let G=history.pushState,Ue=history.replaceState;history.pushState=function(...oe){let ae=G.apply(this,oe);return window.dispatchEvent(new Event("sendora:locationchange")),ae},history.replaceState=function(...oe){let ae=Ue.apply(this,oe);return window.dispatchEvent(new Event("sendora:locationchange")),ae}}let ie=()=>d();window.addEventListener("sendora:locationchange",ie),n.push(()=>window.removeEventListener("sendora:locationchange",ie))}if(t.appBackground||t.engagement){let d=()=>{if(document.visibilityState==="hidden"){z(),t.appBackground&&(w(!0),s.track("app.backgrounded",{sessionId:a()||void 0}));try{s.flush()}catch{}}else document.visibilityState==="visible"&&(t.appBackground&&(w(!0),s.track("app.foregrounded",{sessionId:a()||void 0})),t.engagement&&A===0&&(A=Date.now()))};if(document.addEventListener("visibilitychange",d),n.push(()=>document.removeEventListener("visibilitychange",d)),t.engagement){let I=()=>{z();try{s.flush()}catch{}};window.addEventListener("pagehide",I),n.push(()=>window.removeEventListener("pagehide",I))}}let ge=0,D=()=>{let d=Date.now();d-ge<5e3||(ge=d,w(!0))};return window.addEventListener("click",D,{passive:!0}),window.addEventListener("keydown",D,{passive:!0}),window.addEventListener("scroll",D,{passive:!0}),n.push(()=>{window.removeEventListener("click",D),window.removeEventListener("keydown",D),window.removeEventListener("scroll",D)}),()=>{z();for(let d of n)try{d()}catch{}}}function _e(s){try{return window.sessionStorage?.getItem(s)??null}catch{return null}}function Ce(s,e){try{window.sessionStorage?.setItem(s,e)}catch{}}var W=class s{constructor(e){this.http=e}static isSupported(){return typeof window>"u"?!1:"serviceWorker"in navigator&&"PushManager"in window&&"Notification"in window}static permission(){return typeof Notification>"u"?"denied":Notification.permission}async subscribe(e){if(!s.isSupported())throw new Error("[sendoracloud] Web Push not supported in this browser.");let t=Notification.permission;if(t==="default"&&(t=await Notification.requestPermission()),t!=="granted")throw new Error(`[sendoracloud] Notification permission ${t}.`);let r=await navigator.serviceWorker.getRegistration(e.scope??"/");r||(r=await navigator.serviceWorker.register(e.serviceWorkerPath,{scope:e.scope??"/"})),await navigator.serviceWorker.ready;let i=(await this.http.get("/push/vapid"))?.data?.publicKey;if(!i)throw new Error("[sendoracloud] backend returned no VAPID public key.");let a=await r.pushManager.getSubscription()??await r.pushManager.subscribe({userVisibleOnly:!0,applicationServerKey:gt(i)}),c=a.toJSON(),l=c.endpoint,g=c.keys?.p256dh??"",v=c.keys?.auth??"",w=await ft(l),k=await this.http.post("/push/tokens",{platform:"web",token:w,webEndpoint:l,webP256dh:g,webAuth:v,userAgent:typeof navigator<"u"?navigator.userAgent:void 0,locale:typeof navigator<"u"?navigator.language:void 0,timezone:Intl?.DateTimeFormat?.().resolvedOptions?.().timeZone,...e.userId?{userId:e.userId}:{}});if(!k?.data?.tokenId)throw new Error("[sendoracloud] backend returned no tokenId on subscribe.");return{tokenId:k.data.tokenId,subscription:a}}async unsubscribe(e="/"){if(!s.isSupported())return!1;let t=await navigator.serviceWorker.getRegistration(e);if(!t)return!1;let r=await t.pushManager.getSubscription();return r?await r.unsubscribe():!1}async trackOpen(e,t){let r={sendId:e};t!==void 0&&(r.clickAction=t);let n=await this.http.post("/push/track-open",r);if(!n.success)throw new Error(n.error?.message??"push.trackOpen failed")}};function gt(s){let e="=".repeat((4-s.length%4)%4),t=(s+e).replace(/-/g,"+").replace(/_/g,"/"),r=atob(t),n=new Uint8Array(r.length);for(let i=0;i<r.length;i++)n[i]=r.charCodeAt(i);return n}async function ft(s){let e=new TextEncoder().encode(s),t=await crypto.subtle.digest("SHA-256",e);return Array.from(new Uint8Array(t)).map(n=>n.toString(16).padStart(2,"0")).join("")}var $=class{constructor(e){}unsupported(){throw new Error("[SendoraCloud] sendora.audiences is not available in the browser SDK. Audiences require a secret (sk_*) key with the audiences:read/write scopes and must be managed from a trusted server, not the browser.")}list(){return this.unsupported()}get(e){return this.unsupported()}create(e){return this.unsupported()}update(e,t){return this.unsupported()}delete(e){return this.unsupported()}};var h=class extends Error{constructor(e,t,r){super(t),this.name="LinkError",this.code=e,this.statusCode=r}};function he(s,e){let t=(s??"").toUpperCase();return t==="NOT_FOUND"?new h("NOT_FOUND",e):t==="FORBIDDEN"||t==="UNAUTHORIZED"?new h("UNAUTHORIZED",e):t==="RATE_LIMITED"?new h("RATE_LIMITED",e):t==="ENTITLEMENT_ERROR"||/plan limit/i.test(e)?new h("PLAN_LIMIT",e):t==="VALIDATION_ERROR"?/iOS bundle|Android package/i.test(e)?new h("BUNDLE_MISMATCH",e):/2KB|10KB|linkData/i.test(e)?new h("DATA_TOO_LARGE",e):/fallbackUrl/i.test(e)&&/apps/i.test(e)?new h("FALLBACK_REQUIRED",e):new h("INVALID_INPUT",e):t==="PARSE_ERROR"||t==="HTTP_ERROR"||/HTTP \d{3}/.test(e)?new h("NETWORK",e):new h("UNKNOWN",e||`Unknown LinkError (code=${s??"none"})`)}var pe=/^[a-z0-9-]{3,20}$/;function Le(s){try{let t=new URL(s).pathname.split("/").filter(Boolean);if(t.length===0)return null;let r=t[t.length-1];return pe.test(r)?r:null}catch{return null}}var mt=5*6e4,yt=50,vt=5,B=class{constructor(e){this.http=e;this.prewarmCache=new Map;this.prewarmInflight=0}cacheKey(e,t){if(t)return`k:${t}`;let r={},n=e;for(let i of Object.keys(n).sort())r[i]=n[i];return`s:${JSON.stringify(r)}`}evictExpired(){let e=Date.now();for(let[t,r]of this.prewarmCache)e-r.ts>mt&&this.prewarmCache.delete(t);for(;this.prewarmCache.size>yt;){let t=this.prewarmCache.keys().next().value;if(t===void 0)break;this.prewarmCache.delete(t)}}prewarm(e,t){this.evictExpired();let r=this.cacheKey(e,t?.key);if(this.prewarmCache.has(r)||this.prewarmInflight>=vt)return;this.prewarmInflight++;let n=this.doCreate(e).catch(i=>{throw this.prewarmCache.delete(r),i}).finally(()=>{this.prewarmInflight--});this.prewarmCache.set(r,{promise:n,ts:Date.now()})}async create(e,t){if(!e.title)throw new h("INVALID_INPUT","links.create: title is required");this.evictExpired();let r=this.cacheKey(e,t?.key),n=this.prewarmCache.get(r);return n?(this.prewarmCache.delete(r),n.promise):this.doCreate(e)}async doCreate(e){let t={title:e.title};e.fallbackUrl&&(t.fallbackUrl=e.fallbackUrl),e.iosDeepLinkPath&&(t.iosDeepLinkPath=e.iosDeepLinkPath),e.androidDeepLinkPath&&(t.androidDeepLinkPath=e.androidDeepLinkPath),e.linkData&&(t.linkData=e.linkData),e.ogTitle&&(t.ogTitle=e.ogTitle),e.ogDescription&&(t.ogDescription=e.ogDescription),e.ogImageUrl&&(t.ogImageUrl=e.ogImageUrl),e.campaign&&(t.campaign=e.campaign),e.source&&(t.source=e.source),e.medium&&(t.medium=e.medium),e.channel&&(t.channel=e.channel),e.tags&&(t.tags=e.tags),e.expiresAt&&(t.expiresAt=e.expiresAt),e.iosBundleId&&(t.iosBundleId=e.iosBundleId),e.androidPackageName&&(t.androidPackageName=e.androidPackageName);let r=await this.http.post("/sdk/links",t);if(!r.success||!r.data)throw he(r.error?.code,r.error?.message??"links.create failed");return r.data}async revoke(e){if(!pe.test(e))throw new h("INVALID_INPUT",`links.revoke: '${e}' is not a valid shortcode`);let t=await this.http.post(`/sdk/links/${encodeURIComponent(e)}/revoke`,{});if(!t.success||!t.data)throw he(t.error?.code,t.error?.message??"links.revoke failed");return{shortcode:t.data.shortcode,revoked:!0}}async getStats(e){if(!pe.test(e))throw new h("INVALID_INPUT",`links.getStats: '${e}' is not a valid shortcode`);let t=await this.http.get(`/sdk/links/${encodeURIComponent(e)}/stats`);if(!t.success||!t.data)throw he(t.error?.code,t.error?.message??"links.getStats failed");return t.data}};var Z=class{constructor(e){this.http=e}async reportInstall(e={}){let t=await this.http.post("/attribution/install",e);if(!t.success||!t.data)throw new Error(t.error?.message??"attribution.reportInstall failed");return t.data}async checkDeferred(e={}){let t=await this.http.post("/attribution/deferred",e);if(!t.success)throw new Error(t.error?.message??"attribution.checkDeferred failed");return t.data??null}};var ee=class{constructor(e){this.http=e}async createTicket(e){let t=await this.http.post("/support/tickets",e);if(!t.success||!t.data)throw new Error(t.error?.message??"support.createTicket failed");return t.data}async submitCsat(e){let t=await this.http.post("/support/csat",e);if(!t.success||!t.data)throw new Error(t.error?.message??"support.submitCsat failed");return t.data}};var te=class{constructor(e){this.http=e}async search(e){if(!e.query||e.query.trim().length===0)return[];let t=new URLSearchParams({query:e.query});e.limit&&t.set("limit",String(e.limit));let r=await this.http.get(`/kb/search?${t.toString()}`);if(!r.success||!Array.isArray(r.data))throw new Error(r.error?.message??"kb.search failed");return r.data}};var se=class se{constructor(e){this.autoTrackCleanup=null;this.http=new Y(e),this.storage=new Q,this.consent=new F(e.defaultConsent===!0,this.http),this.analytics=new N(e,this.http,this.storage,this.consent),this.flags=new x(e,this.http,this.storage),this.surveys=new O(e,this.http,this.storage),this.messages=new M(e,this.http,this.storage),this.chatbot=new H(e,this.http,this.storage),this.auth=new V(e,this.http,this.storage,this.analytics,this.flags,this.chatbot),this.webPush=new W(this.http),this.audiences=new $(this.http),this.links=new B(this.http),this.attribution=new Z(this.http),this.support=new ee(this.http),this.kb=new te(this.http),e.autoTrack!==!1&&(this.autoTrackCleanup=Pe(this.analytics,e))}static init(e){return ke(e.apiKey),new se(e)}static sanitizeHtml(e){return J(e)}static safeUrl(e){return S(e)}track(e,t){this.analytics.track(e,t)}page(e){this.analytics.page(e)}identify(e,t,r){this.analytics.identify(e,t,r),this.flags.setContext({userId:e})}reset(){this.analytics.reset(),this.flags.reset(),this.chatbot.newSession()}async flush(){await this.analytics.flush()}destroy(){this.autoTrackCleanup&&(this.autoTrackCleanup(),this.autoTrackCleanup=null),this.analytics.destroy(),this.flags.destroy(),this.messages.destroy()}};se.version=E;var re=se,wt=re;0&&(module.exports={AlreadyIdentifiedError,Analytics,Audiences,Auth,AuthError,Chatbot,Consent,CredentialInUseError,EmailAlreadyTakenError,FeatureFlags,InAppMessaging,LinkError,Links,SendoraCloud,Surveys,ValidationError,WebPush,extractShortcodeFromUrl,safeUrl,sanitizeHtml});
1
+ "use strict";var ce=Object.defineProperty;var Ne=Object.getOwnPropertyDescriptor;var xe=Object.getOwnPropertyNames;var Oe=Object.prototype.hasOwnProperty;var Me=(s,e)=>{for(var t in e)ce(s,t,{get:e[t],enumerable:!0})},He=(s,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of xe(e))!Oe.call(s,n)&&n!==t&&ce(s,n,{get:()=>e[n],enumerable:!(r=Ne(e,n))||r.enumerable});return s};var Fe=s=>He(ce({},"__esModule",{value:!0}),s);var kt={};Me(kt,{AlreadyIdentifiedError:()=>K,Analytics:()=>N,Audiences:()=>$,Auth:()=>V,AuthError:()=>u,Chatbot:()=>H,Consent:()=>F,CredentialInUseError:()=>P,EmailAlreadyTakenError:()=>C,FeatureFlags:()=>x,InAppMessaging:()=>M,LinkError:()=>h,Links:()=>B,SendoraCloud:()=>re,Surveys:()=>O,ValidationError:()=>f,WebPush:()=>W,default:()=>wt,extractShortcodeFromUrl:()=>Le,safeUrl:()=>S,sanitizeHtml:()=>J});module.exports=Fe(kt);var fe=/^[a-zA-Z][a-zA-Z0-9._:-]{0,127}$/;var Ke=new Set(["__proto__","constructor","prototype"]),f=class extends Error{constructor(e){super(`[SendoraCloud] ${e}`),this.name="SendoraValidationError"}};function me(s){if(typeof s!="string"||!fe.test(s))throw new f(`Invalid event name: must match ${fe}, got "${String(s)}".`)}function ye(s){if(s==null)return;if(typeof s!="object"||Array.isArray(s))throw new f("Properties must be a plain object, not an array or primitive.");let e=ve(s);if(e>5)throw new f(`Properties nested too deeply (${e} levels, max 5).`);we(s);let t;try{t=JSON.stringify(s).length}catch{throw new f("Properties contain a circular reference or unserializable value.")}if(t>32768)throw new f(`Properties too large (${t} bytes, max 32768).`)}function ve(s,e=0){if(e>6||s==null||typeof s!="object")return e;let t=e;for(let r of Object.keys(s)){let n=ve(s[r],e+1);n>t&&(t=n)}return t}function we(s){if(!(s==null||typeof s!="object"))for(let e of Object.keys(s)){if(Ke.has(e))throw new f(`Forbidden property key "${e}" (prototype pollution guard).`);we(s[e])}}var Ve=/^pk_[A-Za-z0-9_-]{16,}$/;function ke(s){if(!s||typeof s!="string")throw new f("apiKey is required.");if(s.startsWith("sk_")||s.startsWith("sendora_secret_"))throw new f("Secret keys must NEVER be used in browser SDK. Use a publishable key (pk_...).");if(!Ve.test(s))throw new f("Invalid publishable key format. Expected pk_... (17+ chars).")}function Ae(s){let e=s.replace(/\/$/,"");if(!(e.startsWith("http://localhost")||e.startsWith("http://127.0.0.1"))&&!e.startsWith("https://"))throw new f("apiUrl must be https:// (only http://localhost is allowed for dev).")}var Ie={name:"@sendoracloud/sdk-web",version:"3.17.0",description:"Sendora Cloud Web SDK \u2014 analytics, feature flags, surveys, in-app messaging, chatbot, auth, deep links (Branch / Firebase parity). Zero dependencies, TypeScript, SSR-safe.",type:"module",main:"./dist/index.cjs",module:"./dist/index.js",types:"./dist/index.d.ts",exports:{".":{types:"./dist/index.d.ts",import:"./dist/index.js",require:"./dist/index.cjs"}},files:["dist","README.md","LICENSE"],keywords:["sendora","analytics","feature-flags","surveys","in-app-messaging","chatbot","tracking","customer-engagement"],homepage:"https://sendoracloud.com/sdks",repository:{type:"git",url:"git+https://github.com/sendoracloud/sdk-web.git"},bugs:{url:"https://github.com/sendoracloud/sdk-web/issues"},author:"Sendora Cloud <hello@sendoracloud.com>",license:"Apache-2.0",publishConfig:{access:"public"},sideEffects:!1,scripts:{build:"tsup src/index.ts --format cjs,esm --dts --clean --minify",typecheck:"tsc --noEmit",prepublishOnly:"npm run build"},devDependencies:{typescript:"^5.4.0",tsup:"^8.0.0"}};var E=Ie.version;var $e="https://api.sendoracloud.com",Be=1e4,qe="@sendoracloud/sdk-web",Y=class{constructor(e){let t=(e.apiUrl??$e).replace(/\/$/,"");Ae(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,r){return this.request("POST",e,t,r)}patch(e,t,r){return this.request("PATCH",e,t,r)}delete(e,t){return this.request("DELETE",e,void 0,t)}async request(e,t,r,n){let i=`${this.baseUrl}/api/v1${t}`,o=typeof AbortController<"u"?new AbortController:null,a=o?setTimeout(()=>o.abort(),Be):null;try{let c=await fetch(i,{method:e,headers:{"Content-Type":"application/json","X-API-Key":this.apiKey,"X-Sendora-SDK-Name":qe,"X-Sendora-SDK-Version":E,...n??{}},body:r?JSON.stringify(r):void 0,signal:o?o.signal:void 0});a&&clearTimeout(a);let l;try{l=await c.json()}catch{l={success:!1,error:{code:"PARSE_ERROR",message:`HTTP ${c.status}`}}}if(this.debug&&console.debug(`[SendoraCloud] ${e} ${t} \u2192 ${c.status}`),!c.ok){if(t.startsWith("/auth-service/")&&!this.debug)console.warn(`[SendoraCloud] ${e} ${t} \u2192 HTTP ${c.status}`);else{let v=l?.error?.code??"HTTP_ERROR",w=l?.error?.message??`HTTP ${c.status}`,k=l?.error?.details?.fieldErrors,L=k?` (${Object.entries(k).map(([A,ne])=>`${A}: ${(ne??[]).join(", ")}`).join("; ")})`:"";console.warn(`[SendoraCloud] ${e} ${t} \u2192 HTTP ${c.status} \u2014 ${v}: ${w}${L}`)}l&&l.error&&(l.error={...l.error,status:c.status})}return l}catch(c){a&&clearTimeout(a);let l=c instanceof Error&&c.name==="AbortError"?"timeout":"error";return console.warn(`[SendoraCloud] ${e} ${t} network ${l}`),{success:!1,error:{code:"NETWORK_ERROR",message:l}}}}};var X="sendora_",Q=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 r=X+e;if(this.useLocalStorage)try{localStorage.setItem(r,t);return}catch{}this.memoryStore.set(r,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 r=localStorage.length-1;r>=0;r--){let n=localStorage.key(r);n&&n.startsWith(t)&&localStorage.removeItem(n)}}catch{}for(let r of Array.from(this.memoryStore.keys()))r.startsWith(t)&&this.memoryStore.delete(r)}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 U(s=""){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 r="";for(let n of e)r+=n.toString(16).padStart(2,"0");return s+r}var Se=500,ue=10,le=6e4,N=class{constructor(e,t,r,n){this.queue=[];this.timer=null;this.userId=null;this.identityToken=null;this.consecutiveFailures=0;this.nextFlushAfter=0;this.breakerOpenAt=0;this.breakerWarned=!1;this.http=t,this.storage=r,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,this.appVersion=e.appVersion,this.storage.get("schema_version")===null&&this.storage.set("schema_version","1");let i=this.storage.get("anonymous_id");if(i?this.anonymousId=i:(this.anonymousId=U("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(e,t){me(e),t&&ye(t),this.queue.length>=Se&&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,r){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=r?.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=U("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;this.consecutiveFailures>ue&&Date.now()-this.breakerOpenAt>=le&&(this.consecutiveFailures=0,this.breakerOpenAt=0,this.breakerWarned=!1);let e=this.queue.splice(0,100),t=this.getSessionId(),r=e.map(a=>({projectId:this.projectId,module:"custom",eventType:a.eventType,userId:a.userId,anonymousId:a.anonymousId,identityToken:a.identityToken,timestamp:a.timestamp??new Date().toISOString(),properties:a.properties??{},context:this.getContext(),...t?{sessionId:t}:{},consent:["analytics"]}));if((await this.http.post("/events/batch",{events:r})).success){this.consecutiveFailures=0,this.breakerOpenAt=0,this.breakerWarned=!1;return}if(this.consecutiveFailures++,this.consecutiveFailures>ue){this.breakerWarned||(this.breakerOpenAt=Date.now(),this.breakerWarned=!0,console.warn(`[SendoraCloud] Analytics circuit breaker open after ${ue} consecutive failed flushes \u2014 events will be dropped until a flush succeeds or ${le}ms elapse.`));return}let i=Math.min(le,1e3*2**this.consecutiveFailures);this.nextFlushAfter=Date.now()+i;let o=Math.max(0,Se-this.queue.length);o>0&&this.queue.unshift(...e.slice(0,o))}destroy(){this.timer&&clearInterval(this.timer),this.timer=null,this.flush()}getSessionId(){try{return typeof sessionStorage>"u"?void 0:sessionStorage.getItem("sendora_session_id")||void 0}catch{return}}getContext(){return typeof window>"u"?{sdk:{name:"@sendoracloud/sdk-web",version:E}}:{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,device:this.buildDevice(),sdk:{name:"@sendoracloud/sdk-web",version:E}}}buildDevice(){if(typeof navigator>"u")return;let e=navigator.userAgent||"",r=navigator.userAgentData?.mobile??/Mobi|Android|iPhone|iPod/i.test(e);return{type:/iPad|Tablet|(?:Android(?!.*Mobile))/i.test(e)?"tablet":r?"mobile":"desktop",os:"Web",osVersion:"",model:"",appVersion:this.resolveAppVersion(),browser:je(e)}}resolveAppVersion(){if(this.appVersion)return this.appVersion;if(typeof document<"u"){let r=document.querySelector('meta[name="sendora:app-version"]')?.getAttribute("content");if(r)return r}let e=globalThis.__SENDORA_APP_VERSION__;return typeof e=="string"?e:""}};function je(s){let e=[["Edge",/Edg\/(\d+)/],["Opera",/OPR\/(\d+)/],["Firefox",/Firefox\/(\d+)/],["Samsung Internet",/SamsungBrowser\/(\d+)/],["Chrome",/Chrome\/(\d+)/],["Safari",/Version\/(\d+)[^ ]* Safari/]];for(let[t,r]of e){let n=r.exec(s);if(n)return`${t} ${n[1]}`}return"Unknown"}var x=class{constructor(e,t,r){this.cache=new Map;this.refreshInterval=null;this.http=t,this.storage=r;let n=this.storage.get("flags");if(n)try{let i=JSON.parse(n);if(Array.isArray(i))for(let o of i)de(o)&&this.cache.set(o.key,o)}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 r=await this.http.post("/feature-flags/evaluate",{key:e,userId:this.userId,entityId:this.entityId,environment:this.environment});return r.success&&r.data&&de(r.data)?(this.cache.set(e,r.data),this.persistCache(),r.data.value):null}async isEnabled(e,t=!1){let r=await this.getFlag(e);return typeof r=="boolean"?r: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)de(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,r]of this.cache)e[t]=r.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 de(s){if(s==null||typeof s!="object")return!1;let e=s;return Object.prototype.hasOwnProperty.call(e,"__proto__")||Object.prototype.hasOwnProperty.call(e,"constructor")?!1:typeof e.key=="string"&&"value"in e}var ze=32*1024,O=class{constructor(e,t,r){this.activeSurvey=null;this.http=t,this.storage=r}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 r=await this.http.get(`/surveys/${encodeURIComponent(e)}/config`);return!r.success||!r.data||!Ge(r.data)?null:(this.activeSurvey=r.data,this.storage.set(t,"1"),this.onSurveyDisplay&&this.onSurveyDisplay(r.data),r.data)}async submitResponse(e,t,r=!0){let n=0;try{n=JSON.stringify(t).length}catch{return!1}if(n>ze)return!1;let i=await this.http.post("/surveys/responses",{surveyId:e,answers:t,completed:r});return i.success&&(this.activeSurvey=null),i.success}dismiss(){this.activeSurvey=null}getActiveSurvey(){return this.activeSurvey}};function Ge(s){if(s==null||typeof s!="object")return!1;let e=s;return typeof e.id!="string"||typeof e.name!="string"||!Array.isArray(e.questions)?!1:e.questions.every(Ye)}function Ye(s){if(s==null||typeof s!="object")return!1;let e=s;return typeof e.id=="string"&&typeof e.text=="string"}function S(s){if(!(!s||typeof s!="string"))try{let e=typeof window<"u"?window.location.href:"https://local/",t=new URL(s,e);return t.protocol==="http:"||t.protocol==="https:"?t.href:void 0}catch{return}}var Xe=new Set(["b","i","u","em","strong","a","p","br","span","ul","ol","li","h1","h2","h3","h4"]),Qe={a:new Set(["href","target","rel"])};function J(s){if(typeof DOMParser>"u")return"";let t=new DOMParser().parseFromString(`<div>${s}</div>`,"text/html").body.firstElementChild;return t?(be(t),t.innerHTML):""}function be(s){let e=Array.from(s.children);for(let t of e){let r=t.tagName.toLowerCase();if(!Xe.has(r)){let i=document.createTextNode(t.textContent??"");t.replaceWith(i);continue}let n=Qe[r]??new Set;for(let i of Array.from(t.attributes)){if(!n.has(i.name.toLowerCase())){t.removeAttribute(i.name);continue}if(i.name.toLowerCase()==="href"){let o=S(i.value);o?t.setAttribute("href",o):t.removeAttribute(i.name)}}r==="a"&&(t.setAttribute("rel","noopener noreferrer"),t.setAttribute("target","_blank")),be(t)}}var M=class{constructor(e,t,r){this.messages=[];this.pollInterval=null;this.http=t,this.storage=r}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(Je);for(let t of this.messages){let r=`iam_shown_${t.id}`;if(!this.storage.get(r))return this.storage.set(r,"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 Je(s){return{...s,content:{...s.content,ctaUrl:S(s.content.ctaUrl),imageUrl:S(s.content.imageUrl)}}}var Ze=4e3,H=class{constructor(e,t,r){this.chatbotId=null;this.messages=[];this.http=t,this.storage=r;let n=this.storage.get("chat_session_id");this.sessionId=n??U("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>Ze)return null;let r={role:"user",content:e,timestamp:new Date().toISOString()};this.messages.push(r);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 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=U("chat_"),this.storage.set("chat_session_id",this.sessionId)}getSessionId(){return this.sessionId}};var F=class{constructor(e,t){this.listeners=new Set;this.granted=e,this.http=t}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)}async record(e){if(!this.http)throw new Error("consent.record requires SDK init (HttpClient unavailable)");let t=await this.http.post("/consent/record",e);if(!t.success||!t.data)throw new Error(t.error?.message??"consent.record failed");return t.data}async requestDeletion(e){if(!this.http)throw new Error("consent.requestDeletion requires SDK init (HttpClient unavailable)");let t=await this.http.post("/consent/deletion-request",e);if(!t.success||!t.data)throw new Error(t.error?.message??"consent.requestDeletion failed");return t.data}emit(){for(let e of this.listeners)try{e(this.granted)}catch{}}};var T="auth_access_token",R="auth_access_expires",m="auth_refresh_token",_="auth_user",et=3e4,tt=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function y(s){if(!s||typeof s!="object")return!1;let e=s,t=e.user,r=e.tokens;return!(!t||typeof t.id!="string"||t.id.length===0||!r||typeof r.accessToken!="string"||r.accessToken.length===0||typeof r.refreshToken!="string"||r.refreshToken.length===0||typeof r.expiresIn!="number"||r.expiresIn<=0||t.isAnonymous!==void 0&&typeof t.isAnonymous!="boolean"||t.email!==void 0&&t.email!==null&&typeof t.email!="string"||t.emailVerified!==void 0&&typeof t.emailVerified!="boolean"||t.name!==void 0&&t.name!==null&&typeof t.name!="string"||t.signupMethod!==void 0&&t.signupMethod!==null&&typeof t.signupMethod!="string"||t.lastLoginMethod!==void 0&&t.lastLoginMethod!==null&&typeof t.lastLoginMethod!="string"||e.retiredAnonUserId!==void 0&&typeof e.retiredAnonUserId!="string")}var C=class extends Error{constructor(e="An account with this email already exists"){super(e),this.name="EmailAlreadyTakenError"}},K=class extends Error{constructor(e="Already signed in with an identity; use link*() to add a credential, or sign out first"){super(e),this.name="AlreadyIdentifiedError"}},P=class extends Error{constructor(t="This credential is already linked to a different account",r){super(t);this.code="CREDENTIAL_IN_USE";this.kind="credential_in_use";this.retryable=!1;this.name="CredentialInUseError",this.provider=r?.provider,this.collision=r?.collision,this.status=r?.status}};function Ee(s){let e=s?.error?.details;if(!e||typeof e!="object")return{};let t=e;return{provider:typeof t.provider=="string"?t.provider:void 0,collision:t.collision==="identity"||t.collision==="email"?t.collision:void 0}}var rt=new Set(["network","server","rate_limited","account_locked"]);function st(s,e){switch(s){case"NETWORK_ERROR":case"NETWORK_TIMEOUT":return"network";case"PARSE_ERROR":return"server";case"RATE_LIMITED":case"RATE_LIMIT":case"RATE_LIMIT_EXCEEDED":case"QUOTA_EXCEEDED":return"rate_limited";case"ACCOUNT_LOCKED":return"account_locked";case"CREDENTIAL_IN_USE":return"credential_in_use";case"NOT_ANONYMOUS":case"FORBIDDEN_NON_ANONYMOUS":return"already_identified";case"PASSKEY_USER_CANCELLED":case"GAME_CENTER_CANCELLED":case"PLAY_GAMES_CANCELLED":case"SSO_CANCELLED":return"cancelled";case"GAME_CENTER_UNAVAILABLE":case"PLAY_GAMES_UNAVAILABLE":case"ENTITLEMENT_ERROR":case"tier_required":return"config";case"UNAUTHORIZED":case"INVALID_CREDENTIALS":case"INVALID_REFRESH_TOKEN":case"EMAIL_OTP_INVALID":case"MAGIC_LINK_INVALID":case"PASSKEY_RP_MISMATCH":case"NOT_SIGNED_IN":return"invalid_credential";default:break}if(typeof e=="number"){if(e===429)return"rate_limited";if(e>=500)return"server";if(e>=400)return"invalid_credential"}return"unknown"}var u=class extends Error{constructor(e,t,r){super(t),this.name="AuthError",this.code=e,this.kind=r?.kind??st(e,r?.status),this.retryable=rt.has(this.kind),this.retryAfterSeconds=r?.retryAfterSeconds,this.status=r?.status}};function nt(s){if(s instanceof u||s instanceof C||s instanceof K||s instanceof P)return s;let e=s?.name;return e==="AbortError"||e==="TimeoutError"||e==="NetworkTimeoutError"?new u("NETWORK_TIMEOUT",s instanceof Error?s.message:"timeout",{kind:"network"}):new u("UNKNOWN_ERROR",s instanceof Error?s.message:String(s),{kind:"unknown"})}function p(s){let e=s.error?.status,t=s.error?.details?.retryAfterSeconds;return{status:typeof e=="number"?e:void 0,retryAfterSeconds:typeof t=="number"&&Number.isFinite(t)&&t>0?t:void 0}}var V=class{constructor(e,t,r,n,i,o){this.user=null;this.accessToken=null;this.accessExpiresAt=0;this.inflight=Promise.resolve();this.refreshInflight=null;this.refreshFailureCount=0;this.refreshCooldownUntil=0;this.visibilityResetUnsub=null;this.proactiveRefreshTimer=null;this.proactiveRefreshCleanup=null;this.takeoverListeners=new Set;this.lastTakeover=null;this.deletionCancelledListeners=new Set;this.lastDeletionCancelled=null;this.lastAnonRetirement=null;this.authStateListeners=new Set;this.pendingAnonTakeover=null;this.hydrated=!1;this.http=t,this.storage=r,this.analytics=n,this.flags=i,this.chatbot=o;let a=this.storage.get(_),c=this.storage.get(T),l=this.storage.get(m),g=this.storage.get(R);if(a&&(c||l))try{let v=JSON.parse(a);if(typeof v.id!="string"||v.id.length===0)throw new Error("invalid cache");this.user=v,c?(this.accessToken=c,this.accessExpiresAt=g?Number(g):0):(this.accessToken=null,this.accessExpiresAt=0),this.analytics.identify(this.user.id)}catch{this.storage.remove(_),this.storage.remove(T),this.storage.remove(R)}this.hydrated=!0}getCurrentUser(){return this.user}onDeviceTakeover(e){return this.takeoverListeners.add(e),()=>{this.takeoverListeners.delete(e)}}getLastDeviceTakeover(){return this.lastTakeover}onAuthStateChanged(e){if(this.authStateListeners.add(e),this.hydrated&&this.user){let t=this.user;try{e({event:"signed_in",user:t})}catch{}}return()=>{this.authStateListeners.delete(e)}}emitAuthState(e){for(let t of this.authStateListeners)try{t(e)}catch{}}fireDeviceTakeover(e,t){if(!e||!tt.test(e))return;let r={retiredAnonUserId:e,identifiedUserId:t,at:new Date};this.lastTakeover=r;for(let n of this.takeoverListeners)try{n(r)}catch{}this.emitAuthState({event:"device_takeover",user:this.user,retiredAnonUserId:e})}onDeletionCancelled(e){return this.deletionCancelledListeners.add(e),()=>{this.deletionCancelledListeners.delete(e)}}getLastDeletionCancelled(){return this.lastDeletionCancelled}getLastAnonRetirement(){return this.lastAnonRetirement}fireDeletionCancelled(e){let t={userId:e,at:new Date};this.lastDeletionCancelled=t;for(let r of this.deletionCancelledListeners)try{r(t)}catch{}this.emitAuthState({event:"deletion_cancelled",user:this.user})}takeoverHint(){if(this.user?.isAnonymous)return this.storage.get(m)??void 0}async getAccessToken(){return this.accessToken&&this.accessExpiresAt&&Date.now()<this.accessExpiresAt-et?this.accessToken:this.storage.get(m)?this.refreshInflight?this.refreshInflight:Date.now()<this.refreshCooldownUntil?null:this.refreshAccessToken():null}getAccessTokenSync(){return this.accessToken}signInAnonymously(e){return this.serialize(async()=>{let t=this.user;if(!e?.forceNew&&t?.isAnonymous&&this.storage.get(m))return t;let r=await this.http.post("/auth-service/anonymous",{name:e?.name,metadata:e?.metadata});if(!r.success||!y(r.data))throw new u(r.error?.code??"AUTH_ERROR",r.error?.message??"Anonymous signup failed",p(r));return this.persist(r.data),r.data.user})}signUp(e,t,r){return this.serialize(async()=>{let n=this.storage.get(m);if(n){let o=await this.http.post("/auth-service/upgrade",{refreshToken:n,email:e,password:t,name:r?.name});if(o.success&&y(o.data))return this.persist(o.data),o.data.user;let a=o.error?.code??"",c=p(o),l=c.status;if(a==="NOT_ANONYMOUS"||a==="FORBIDDEN_NON_ANONYMOUS")throw new K(o.error?.message);if(a==="CONFLICT"||a==="EMAIL_ALREADY_TAKEN"||l===409)throw new C(o.error?.message);if(a==="INVALID_REFRESH"||a==="UNAUTHORIZED"||a==="REFRESH_EXPIRED")await this.analytics.flush(),this.wipeLocalIdentity("session_expired");else throw new u(a||"AUTH_ERROR",o.error?.message??"Upgrade failed",c)}let i=await this.http.post("/auth-service/signup",{email:e,password:t,name:r?.name,metadata:r?.metadata});if(!i.success||!y(i.data)){let o=i.error?.code??"";throw o==="CONFLICT"?new C(i.error?.message):new u(o||"AUTH_ERROR",i.error?.message??"Signup failed",p(i))}return this.persist(i.data),i.data.user})}signIn(e,t){return this.serialize(async()=>{let r=this.takeoverHint(),n=await this.http.post("/auth-service/login",{email:e,password:t,...r?{prevAnonRefreshToken:r}:{}});if(!n.success||!n.data)throw new u(n.error?.code??"AUTH_ERROR",n.error?.message??"Login failed",p(n));let i=n.data;if(i.mfaRequired===!0&&typeof i.mfaChallengeToken=="string"){r&&(this.pendingAnonTakeover={challengeToken:i.mfaChallengeToken,prevAnonRefreshToken:r});let o=i.user;return{mfaRequired:!0,mfaChallengeToken:i.mfaChallengeToken,userId:o?.id??""}}if(!y(n.data))throw new u(n.error?.code??"AUTH_ERROR",n.error?.message??"Login failed",p(n));return await this.replaceLocalIdentity(),this.persist(n.data),n.data.user})}loginSocial(e){return this.serialize(async()=>{let t=this.takeoverHint(),{link:r,onCredentialInUse:n,...i}=e,o=await this.http.post("/auth-service/login/social",{...i,...t?{prevAnonRefreshToken:t}:{},...r?{linkAnonymous:!0}:{},...n?{onCredentialInUse:n}:{}});if(!o.success||!y(o.data))throw o.error?.code==="CREDENTIAL_IN_USE"?new P(o.error.message,{...Ee(o),status:p(o).status}):new u(o.error?.code??"SOCIAL_LOGIN_FAILED",o.error?.message??"Social login failed",p(o));return await this.replaceLocalIdentity(),this.persist(o.data),o.data.user})}signInWithGoogle(e,t,r){return this.loginSocial({provider:"google",code:e,redirectUri:t,link:r})}signInWithGitHub(e,t){return this.loginSocial({provider:"github",code:e,redirectUri:t})}signInWithApple(e){return this.loginSocial({provider:"apple",...e})}signInWithMicrosoft(e,t){return this.loginSocial({provider:"microsoft",code:e,redirectUri:t})}signInWithLinkedIn(e,t){return this.loginSocial({provider:"linkedin",code:e,redirectUri:t})}signInWithFacebook(e,t){return this.loginSocial({provider:"facebook",code:e,redirectUri:t})}linkEmailPassword(e,t){return this.linkCredential("/auth-service/me/link/email",{email:e,password:t})}linkSocial(e){return this.linkCredential("/auth-service/me/link/social",e)}linkGoogle(e){return this.linkSocial({provider:"google",...e})}linkApple(e){return this.linkSocial({provider:"apple",...e})}async linkCredential(e,t){let r=await this.getAccessToken();if(!r)throw new u("NOT_SIGNED_IN","Sign in before linking a credential");let n=await this.http.post(e,t,{Authorization:`Bearer ${r}`});if(!n.success||!n.data?.user){let i=n.error?.code??"LINK_FAILED";throw i==="CREDENTIAL_IN_USE"?new P(n.error?.message,{...Ee(n),status:p(n).status}):new u(i,n.error?.message??"Failed to link credential",p(n))}return n.data.upgraded&&y(n.data)?(this.persist(n.data),n.data.user):(this.updateLocalUser(n.data.user),n.data.user)}updateLocalUser(e){this.user=e,this.storage.set(_,JSON.stringify(e))}async listLinkedIdentities(){let e=await this.getAccessToken();if(!e)throw new u("NOT_SIGNED_IN","Sign in before listing linked identities");let t=await this.http.get("/auth-service/me/identities",{Authorization:`Bearer ${e}`});if(!t.success||!t.data)throw new u(t.error?.code??"IDENTITIES_FAILED",t.error?.message??"Failed to list linked identities",p(t));return{identities:t.data.identities??[],hasPassword:!!t.data.hasPassword}}signInWithDiscord(e,t){return this.loginSocial({provider:"discord",code:e,redirectUri:t})}challengeMfa(e,t){return this.serialize(async()=>{let n=(this.pendingAnonTakeover?.challengeToken===e?this.pendingAnonTakeover.prevAnonRefreshToken:void 0)??this.takeoverHint(),i=await this.http.post("/auth-service/mfa/challenge",{challengeToken:e,code:t,...n?{prevAnonRefreshToken:n}:{}});if(!i.success||!y(i.data))throw new u(i.error?.code??"MFA_CHALLENGE_FAILED",i.error?.message??"Invalid MFA code",p(i));return await this.replaceLocalIdentity(),this.persist(i.data),i.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()=>{let r=this.takeoverHint(),n=await this.http.post("/auth-service/email-otp/verify",{email:e,code:t,...r?{prevAnonRefreshToken:r}:{}});if(!n.success||!y(n.data))throw new u(n.error?.code??"EMAIL_OTP_INVALID",n.error?.message??"Wrong or expired code",p(n));return await this.replaceLocalIdentity(),this.persist(n.data),n.data.user})}async requestPasswordReset(e){let t=await this.http.post("/auth-service/password/forgot",{email:e});if(!t.success)throw new u(t.error?.code??"PASSWORD_RESET_REQUEST_FAILED",t.error?.message??"Failed to send reset email")}async resetPassword(e,t){let r=await this.http.post("/auth-service/password/reset",{token:e,newPassword:t});if(!r.success)throw new u(r.error?.code??"PASSWORD_RESET_FAILED",r.error?.message??"Reset failed")}async verifyEmail(e){let t=await this.http.post("/auth-service/email/verify",{token:e});if(!t.success)throw new u(t.error?.code??"EMAIL_VERIFICATION_FAILED",t.error?.message??"Verification failed")}async sendVerificationEmail(){let e=await this.getAccessToken();if(!e)throw new u("UNAUTHORIZED","Sign in before requesting a verification email");let t=await this.http.post("/auth-service/email/verify/resend",{},{Authorization:`Bearer ${e}`});if(!t.success)throw new u(t.error?.code??"VERIFICATION_RESEND_FAILED",t.error?.message??"Failed to send verification email")}verifyMagicLink(e){return this.serialize(async()=>{let t=this.takeoverHint(),r=await this.http.post("/auth-service/magic-link/verify",{token:e,...t?{prevAnonRefreshToken:t}:{}});if(!r.success||!y(r.data))throw new u(r.error?.code??"MAGIC_LINK_INVALID",r.error?.message??"Invalid or expired magic link",p(r));return await this.replaceLocalIdentity(),this.persist(r.data),r.data.user})}async enrollMfa(){let e=await this.http.post("/auth-service/mfa/enroll/start",{},await 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},await this.bearerHeaders());return!!(t.success&&t.data?.confirmed)}async disableMfa(){await this.http.post("/auth-service/mfa/disable",{},await 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",{},await this.bearerHeaders());if(!t.success||!t.data)throw new u(t.error?.code??"PASSKEY_START_FAILED",t.error?.message??"Passkey start failed");let r=ot(t.data),n;try{let a=await navigator.credentials.create({publicKey:r});if(!a)throw new Error("create returned null");n=a}catch(a){throw new u("PASSKEY_USER_CANCELLED",a instanceof Error?a.message:"User cancelled passkey registration")}let i=ct(n),o=await this.http.post("/auth-service/passkeys/register/finish",{response:i,name:e},await this.bearerHeaders());if(!o.success||!o.data)throw new u(o.error?.code??"PASSKEY_FINISH_FAILED",o.error?.message??"Passkey verification failed");return o.data}signInWithPasskey(e){return this.serialize(async()=>{if(typeof navigator>"u"||!navigator.credentials)throw new u("PASSKEY_UNSUPPORTED","WebAuthn not supported in this environment");let t=this.takeoverHint(),r=await this.http.post("/auth-service/passkeys/authenticate/start",{userId:e??null});if(!r.success||!r.data)throw new u(r.error?.code??"PASSKEY_START_FAILED",r.error?.message??"Passkey start failed",p(r));let n=at(r.data),i;try{let c=await navigator.credentials.get({publicKey:n});if(!c)throw new Error("get returned null");i=c}catch(c){throw new u("PASSKEY_USER_CANCELLED",c instanceof Error?c.message:"User cancelled passkey signin")}let o=dt(i),a=await this.http.post("/auth-service/passkeys/authenticate/finish",{response:o,userId:e??null,...t?{prevAnonRefreshToken:t}:{}});if(!a.success||!y(a.data))throw new u(a.error?.code??"PASSKEY_AUTH_FAILED",a.error?.message??"Passkey signin failed",p(a));return await this.replaceLocalIdentity(),this.persist(a.data),a.data.user})}async listPasskeys(){let e=await this.http.get("/auth-service/passkeys",await this.bearerHeaders());return!e.success||!Array.isArray(e.data)?[]:e.data}async deletePasskey(e){await this.http.delete(`/auth-service/passkeys/${e}`,await this.bearerHeaders())}async listMySessions(){let e=await this.http.get("/auth-service/sessions/me",await this.bearerHeaders());return!e.success||!Array.isArray(e.data)?[]:e.data}async revokeSession(e){await this.http.delete(`/auth-service/sessions/me/${e}`,await this.bearerHeaders())}async revokeAllSessions(){await this.http.delete("/auth-service/sessions/me",await this.bearerHeaders())}async startSso(e){let t=this.takeoverHint(),r=await this.http.post("/auth-service/sso/oidc/start",{returnTo:e,...t?{prevAnonRefreshToken:t}:{}});if(!r.success||!r.data)throw new u(r.error?.code??"SSO_START_FAILED",r.error?.message??"SSO start failed");return r.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 r=await this.startSso(t);typeof window<"u"&&(window.location.href=r.authorizationUrl)}async startSaml(e){let t=this.takeoverHint(),r=await this.http.post("/auth-service/sso/saml/start",{returnTo:e,...t?{prevAnonRefreshToken:t}:{}});if(!r.success||!r.data)throw new u(r.error?.code??"SAML_START_FAILED",r.error?.message??"SAML start failed");return r.data}async signInWithSaml(e){let t=e??(typeof window<"u"?window.location.origin:"");if(!t)throw new u("SAML_NO_RETURN_TO","returnTo required outside browser context");let r=await this.startSaml(t);typeof window<"u"&&(window.location.href=r.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),r=t.get("sendora_oidc_error")??t.get("sendora_saml_error");if(r)throw window.history.replaceState(null,"",window.location.pathname+window.location.search),new u("SSO_FAILED",decodeURIComponent(r));let n=t.get("sendora_oidc_token")??t.get("sendora_saml_token");if(!n)return null;let i=t.get("sendora_retired_anon");window.history.replaceState(null,"",window.location.pathname+window.location.search);let o=await this.http.post("/auth-service/token/refresh",{refreshToken:n}),a=o.success?Te(o.data):null;if(!a)throw new u(o.error?.code??"SSO_EXCHANGE_FAILED",o.error?.message??"Could not exchange SSO token",p(o));let c={...a,tokenType:"Bearer"};await this.replaceLocalIdentity(),this.accessToken=c.accessToken,this.accessExpiresAt=Date.now()+c.expiresIn*1e3,this.storage.set(T,c.accessToken),this.storage.set(R,String(this.accessExpiresAt)),this.storage.set(m,c.refreshToken);let l=lt(c.accessToken),g={id:l?.sub??"",email:l?.email??null,emailVerified:l?.email_verified??!0,name:l?.name??null,isAnonymous:!1};return this.user=g,this.storage.set(_,JSON.stringify(g)),this.analytics.identify(g.id),this.flags.setContext({userId:g.id}),this.emitAuthState({event:"signed_in",user:g}),i&&this.fireDeviceTakeover(i,g.id),g})}async bearerHeaders(){let e=await this.getAccessToken();if(!e)throw new u("NOT_SIGNED_IN","Not signed in");return{Authorization:`Bearer ${e}`}}signOut(){return this.serialize(async()=>{let e=this.storage.get(m);this.wipeLocalIdentity("user"),e&&this.http.post("/auth-service/token/revoke",{refreshToken:e}).catch(()=>{})})}serialize(e){let t=async()=>{try{return await e()}catch(n){throw nt(n)}},r=this.inflight.then(t,t);return this.inflight=r.catch(()=>{}),r}async refreshAccessToken(){if(this.refreshInflight)return this.refreshInflight;let e=this.storage.get(m);if(!e)return null;let t=typeof navigator<"u"&&"locks"in navigator,r=async()=>{let n=this.storage.get(m);if(n&&n!==e){let i=this.storage.get(T),o=this.storage.get(R);if(i&&o){let a=Number(o);if(Number.isFinite(a)&&a>Date.now())return this.accessToken=i,this.accessExpiresAt=a,i}}try{let i=n??e,o=await this.http.post("/auth-service/token/refresh",{refreshToken:i});if(!o.success)return it(o.error?.code)&&this.tokenStillCurrent(i)?(this.wipeLocalIdentity("session_expired"),this.refreshFailureCount=0,this.refreshCooldownUntil=0,null):(this.armBackoff(),null);let a=Te(o.data);if(!a)return this.armBackoff(),null;if(!this.tokenStillCurrent(i))return null;if(this.accessToken=a.accessToken,this.accessExpiresAt=Date.now()+a.expiresIn*1e3,this.storage.set(T,a.accessToken),this.storage.set(R,String(this.accessExpiresAt)),this.storage.set(m,a.refreshToken),this.user===null){let c=ut(o.data);c&&(this.user=c,this.storage.set(_,JSON.stringify(c)),this.analytics.identify(c.id),this.emitAuthState({event:"signed_in",user:c}))}return this.refreshFailureCount=0,this.refreshCooldownUntil=0,a.accessToken}catch{return this.armBackoff(),null}};return this.refreshInflight=(async()=>{try{return t?await navigator.locks.request("sendora-authservice-refresh",{mode:"exclusive"},r):await r()}finally{this.refreshInflight=null}})(),this.refreshInflight}armBackoff(){this.refreshFailureCount+=1;let e=Math.min(3e4,1e3*Math.pow(2,this.refreshFailureCount-1));this.refreshCooldownUntil=Date.now()+e}resetRefreshState(){this.refreshFailureCount=0,this.refreshCooldownUntil=0}startVisibilityRefreshReset(){if(this.visibilityResetUnsub||typeof document>"u")return;let e=()=>{document.visibilityState==="visible"&&this.resetRefreshState()};document.addEventListener("visibilitychange",e),this.visibilityResetUnsub=()=>document.removeEventListener("visibilitychange",e)}stopVisibilityRefreshReset(){this.visibilityResetUnsub&&(this.visibilityResetUnsub(),this.visibilityResetUnsub=null)}startProactiveRefreshCron(){if(typeof window>"u"||this.proactiveRefreshTimer)return;let e=.8,t=6e4,r=3e4,n=async()=>{if(!this.accessToken||!this.accessExpiresAt)return;let o=Date.now(),a=this.accessExpiresAt-o;if(a<=0)return;let l=Math.max(a,5*6e4)*(1-e)+(Math.random()*r*2-r);a<=l&&await this.refreshAccessToken()};this.proactiveRefreshTimer=window.setInterval(()=>{n()},t);let i=()=>{document.visibilityState==="visible"&&n()};document.addEventListener("visibilitychange",i),this.proactiveRefreshCleanup=()=>{this.proactiveRefreshTimer&&(window.clearInterval(this.proactiveRefreshTimer),this.proactiveRefreshTimer=null),document.removeEventListener("visibilitychange",i)}}stopProactiveRefreshCron(){this.proactiveRefreshCleanup&&(this.proactiveRefreshCleanup(),this.proactiveRefreshCleanup=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(T,e.tokens.accessToken),this.storage.set(R,String(this.accessExpiresAt)),this.storage.set(m,e.tokens.refreshToken),this.analytics.identify(e.user.id),this.flags.setContext({userId:e.user.id}),this.startProactiveRefreshCron(),this.startVisibilityRefreshReset(),this.emitAuthState({event:"signed_in",user:e.user}),e.retiredAnonUserId&&this.fireDeviceTakeover(e.retiredAnonUserId,e.user.id),e.anonRetirement&&(this.lastAnonRetirement=e.anonRetirement),e.reactivatedFromDeletion&&this.fireDeletionCancelled(e.user.id)}async replaceLocalIdentity(){this.user!==null&&(await this.analytics.flush(),this.wipeLocalIdentity("replaced"))}tokenStillCurrent(e){return this.storage.get(m)===e}wipeLocalIdentity(e="replaced"){this.user=null,this.accessToken=null,this.accessExpiresAt=0,this.lastTakeover=null,this.lastDeletionCancelled=null,this.lastAnonRetirement=null,this.pendingAnonTakeover=null,this.storage.remove(_),this.storage.remove(T),this.storage.remove(R),this.storage.remove(m),this.analytics.reset(),this.analytics.dropQueue(),this.flags.reset(),this.chatbot.newSession(),this.stopProactiveRefreshCron(),this.stopVisibilityRefreshReset(),this.refreshFailureCount=0,this.refreshCooldownUntil=0,e!=="replaced"&&this.emitAuthState({event:"signed_out",user:null,reason:e})}};function it(s){return s?s==="INVALID_REFRESH_TOKEN":!1}function j(s){let e=(4-s.length%4)%4,t=(s+"===".slice(0,e)).replace(/-/g,"+").replace(/_/g,"/"),r=atob(t),n=new ArrayBuffer(r.length),i=new Uint8Array(n);for(let o=0;o<r.length;o++)i[o]=r.charCodeAt(o);return n}function b(s){let e=s instanceof Uint8Array?s:new Uint8Array(s),t="";for(let r=0;r<e.length;r++)t+=String.fromCharCode(e[r]);return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function ot(s){return{challenge:j(s.challenge),rp:s.rp,user:{id:j(s.user.id),name:s.user.name,displayName:s.user.displayName},pubKeyCredParams:s.pubKeyCredParams,timeout:s.timeout,attestation:s.attestation,excludeCredentials:s.excludeCredentials?.map(e=>({id:j(e.id),type:"public-key",transports:e.transports})),authenticatorSelection:s.authenticatorSelection}}function at(s){return{challenge:j(s.challenge),timeout:s.timeout,rpId:s.rpId,allowCredentials:s.allowCredentials?.map(e=>({id:j(e.id),type:"public-key",transports:e.transports})),userVerification:s.userVerification}}function ct(s){let e=s.response;return{id:s.id,rawId:b(s.rawId),type:s.type,response:{clientDataJSON:b(e.clientDataJSON),attestationObject:b(e.attestationObject),transports:typeof e.getTransports=="function"?e.getTransports():void 0},clientExtensionResults:s.getClientExtensionResults()}}function Te(s){if(!s||typeof s!="object")return null;let e=s.tokens,t=e&&typeof e=="object"?e:s;return typeof t.accessToken!="string"||!t.accessToken||typeof t.refreshToken!="string"||!t.refreshToken||typeof t.expiresIn!="number"||t.expiresIn<=0?null:{accessToken:t.accessToken,refreshToken:t.refreshToken,expiresIn:t.expiresIn}}function ut(s){if(!s||typeof s!="object")return null;let e=s.user;if(!e||typeof e!="object")return null;let t=e.id;return typeof t!="string"||t.length===0?null:e}function lt(s){try{let e=s.split(".");if(e.length!==3)return null;let t=e[1].replace(/-/g,"+").replace(/_/g,"/"),r=atob(t+"===".slice(0,(4-t.length%4)%4));return JSON.parse(r)}catch{return null}}function dt(s){let e=s.response;return{id:s.id,rawId:b(s.rawId),type:s.type,response:{clientDataJSON:b(e.clientDataJSON),authenticatorData:b(e.authenticatorData),signature:b(e.signature),userHandle:e.userHandle?b(e.userHandle):null},clientExtensionResults:s.getClientExtensionResults()}}function ht(s){return s===!1?{pageViews:!1,sessionStart:!1,appOpen:!1,appBackground:!1,engagement:!1}:s===!0||s===void 0?{pageViews:!0,sessionStart:!0,appOpen:!0,appBackground:!0,engagement:!0}:{pageViews:s.pageViews??!0,sessionStart:s.sessionStart??!0,appOpen:s.appOpen??!0,appBackground:s.appBackground??!0,engagement:s.engagement??!0}}var pt=250,Re=360*60*1e3;function Pe(s,e){if(typeof window>"u")return()=>{};let t=ht(e.autoTrack),r=e.sessionIdleMs??18e5,n=[],i="sendora_session_id",o="sendora_session_last_activity";function a(){return _e(i)??""}function c(d){Ce(i,d)}function l(){let d=_e(o);return d&&Number(d)||0}function g(d){Ce(o,String(d))}function v(){return`sess_${Date.now().toString(36)}_${Math.random().toString(36).slice(2,10)}`}function w(d){let I=Date.now(),q=l();if(!a()||q&&I-q>r){let G=v();c(G),d&&t.sessionStart&&s.track("session.start",{sessionId:G})}g(I)}w(t.sessionStart),t.appOpen&&s.track("app.opened",{sessionId:a()||void 0,url:window.location.origin+window.location.pathname,referrer:document.referrer||void 0});let k=window.location.pathname,L=0,A=document.visibilityState==="visible"?Date.now():0;function ne(){A>0&&(L+=Date.now()-A,A=0)}function z(){if(!t.engagement)return;ne();let d=L;if(L=0,!(d<pt)){d>Re&&(d=Re);try{s.track("app.engagement",{durationMs:Math.round(d),screen:k,sessionId:a()||void 0})}catch{}}}function De(d){k=d,L=0,A=document.visibilityState==="visible"?Date.now():0}if(t.pageViews||t.engagement){t.pageViews&&s.page();let d=()=>{z(),w(!0),t.pageViews&&s.page(),De(window.location.pathname)},I=()=>d();window.addEventListener("popstate",I),n.push(()=>window.removeEventListener("popstate",I));let q=window;if(!q.__sendoraHistoryPatched){q.__sendoraHistoryPatched=!0;let G=history.pushState,Ue=history.replaceState;history.pushState=function(...oe){let ae=G.apply(this,oe);return window.dispatchEvent(new Event("sendora:locationchange")),ae},history.replaceState=function(...oe){let ae=Ue.apply(this,oe);return window.dispatchEvent(new Event("sendora:locationchange")),ae}}let ie=()=>d();window.addEventListener("sendora:locationchange",ie),n.push(()=>window.removeEventListener("sendora:locationchange",ie))}if(t.appBackground||t.engagement){let d=()=>{if(document.visibilityState==="hidden"){z(),t.appBackground&&(w(!0),s.track("app.backgrounded",{sessionId:a()||void 0}));try{s.flush()}catch{}}else document.visibilityState==="visible"&&(t.appBackground&&(w(!0),s.track("app.foregrounded",{sessionId:a()||void 0})),t.engagement&&A===0&&(A=Date.now()))};if(document.addEventListener("visibilitychange",d),n.push(()=>document.removeEventListener("visibilitychange",d)),t.engagement){let I=()=>{z();try{s.flush()}catch{}};window.addEventListener("pagehide",I),n.push(()=>window.removeEventListener("pagehide",I))}}let ge=0,D=()=>{let d=Date.now();d-ge<5e3||(ge=d,w(!0))};return window.addEventListener("click",D,{passive:!0}),window.addEventListener("keydown",D,{passive:!0}),window.addEventListener("scroll",D,{passive:!0}),n.push(()=>{window.removeEventListener("click",D),window.removeEventListener("keydown",D),window.removeEventListener("scroll",D)}),()=>{z();for(let d of n)try{d()}catch{}}}function _e(s){try{return window.sessionStorage?.getItem(s)??null}catch{return null}}function Ce(s,e){try{window.sessionStorage?.setItem(s,e)}catch{}}var W=class s{constructor(e){this.http=e}static isSupported(){return typeof window>"u"?!1:"serviceWorker"in navigator&&"PushManager"in window&&"Notification"in window}static permission(){return typeof Notification>"u"?"denied":Notification.permission}async subscribe(e){if(!s.isSupported())throw new Error("[sendoracloud] Web Push not supported in this browser.");let t=Notification.permission;if(t==="default"&&(t=await Notification.requestPermission()),t!=="granted")throw new Error(`[sendoracloud] Notification permission ${t}.`);let r=await navigator.serviceWorker.getRegistration(e.scope??"/");r||(r=await navigator.serviceWorker.register(e.serviceWorkerPath,{scope:e.scope??"/"})),await navigator.serviceWorker.ready;let i=(await this.http.get("/push/vapid"))?.data?.publicKey;if(!i)throw new Error("[sendoracloud] backend returned no VAPID public key.");let a=await r.pushManager.getSubscription()??await r.pushManager.subscribe({userVisibleOnly:!0,applicationServerKey:gt(i)}),c=a.toJSON(),l=c.endpoint,g=c.keys?.p256dh??"",v=c.keys?.auth??"",w=await ft(l),k=await this.http.post("/push/tokens",{platform:"web",token:w,webEndpoint:l,webP256dh:g,webAuth:v,userAgent:typeof navigator<"u"?navigator.userAgent:void 0,locale:typeof navigator<"u"?navigator.language:void 0,timezone:Intl?.DateTimeFormat?.().resolvedOptions?.().timeZone,...e.userId?{userId:e.userId}:{}});if(!k?.data?.tokenId)throw new Error("[sendoracloud] backend returned no tokenId on subscribe.");return{tokenId:k.data.tokenId,subscription:a}}async unsubscribe(e="/"){if(!s.isSupported())return!1;let t=await navigator.serviceWorker.getRegistration(e);if(!t)return!1;let r=await t.pushManager.getSubscription();return r?await r.unsubscribe():!1}async trackOpen(e,t){let r={sendId:e};t!==void 0&&(r.clickAction=t);let n=await this.http.post("/push/track-open",r);if(!n.success)throw new Error(n.error?.message??"push.trackOpen failed")}};function gt(s){let e="=".repeat((4-s.length%4)%4),t=(s+e).replace(/-/g,"+").replace(/_/g,"/"),r=atob(t),n=new Uint8Array(r.length);for(let i=0;i<r.length;i++)n[i]=r.charCodeAt(i);return n}async function ft(s){let e=new TextEncoder().encode(s),t=await crypto.subtle.digest("SHA-256",e);return Array.from(new Uint8Array(t)).map(n=>n.toString(16).padStart(2,"0")).join("")}var $=class{constructor(e){}unsupported(){throw new Error("[SendoraCloud] sendora.audiences is not available in the browser SDK. Audiences require a secret (sk_*) key with the audiences:read/write scopes and must be managed from a trusted server, not the browser.")}list(){return this.unsupported()}get(e){return this.unsupported()}create(e){return this.unsupported()}update(e,t){return this.unsupported()}delete(e){return this.unsupported()}};var h=class extends Error{constructor(e,t,r){super(t),this.name="LinkError",this.code=e,this.statusCode=r}};function he(s,e){let t=(s??"").toUpperCase();return t==="NOT_FOUND"?new h("NOT_FOUND",e):t==="FORBIDDEN"||t==="UNAUTHORIZED"?new h("UNAUTHORIZED",e):t==="RATE_LIMITED"?new h("RATE_LIMITED",e):t==="ENTITLEMENT_ERROR"||/plan limit/i.test(e)?new h("PLAN_LIMIT",e):t==="VALIDATION_ERROR"?/iOS bundle|Android package/i.test(e)?new h("BUNDLE_MISMATCH",e):/2KB|10KB|linkData/i.test(e)?new h("DATA_TOO_LARGE",e):/fallbackUrl/i.test(e)&&/apps/i.test(e)?new h("FALLBACK_REQUIRED",e):new h("INVALID_INPUT",e):t==="PARSE_ERROR"||t==="HTTP_ERROR"||/HTTP \d{3}/.test(e)?new h("NETWORK",e):new h("UNKNOWN",e||`Unknown LinkError (code=${s??"none"})`)}var pe=/^[a-z0-9-]{3,20}$/;function Le(s){try{let t=new URL(s).pathname.split("/").filter(Boolean);if(t.length===0)return null;let r=t[t.length-1];return pe.test(r)?r:null}catch{return null}}var mt=5*6e4,yt=50,vt=5,B=class{constructor(e){this.http=e;this.prewarmCache=new Map;this.prewarmInflight=0}cacheKey(e,t){if(t)return`k:${t}`;let r={},n=e;for(let i of Object.keys(n).sort())r[i]=n[i];return`s:${JSON.stringify(r)}`}evictExpired(){let e=Date.now();for(let[t,r]of this.prewarmCache)e-r.ts>mt&&this.prewarmCache.delete(t);for(;this.prewarmCache.size>yt;){let t=this.prewarmCache.keys().next().value;if(t===void 0)break;this.prewarmCache.delete(t)}}prewarm(e,t){this.evictExpired();let r=this.cacheKey(e,t?.key);if(this.prewarmCache.has(r)||this.prewarmInflight>=vt)return;this.prewarmInflight++;let n=this.doCreate(e).catch(i=>{throw this.prewarmCache.delete(r),i}).finally(()=>{this.prewarmInflight--});this.prewarmCache.set(r,{promise:n,ts:Date.now()})}async create(e,t){if(!e.title)throw new h("INVALID_INPUT","links.create: title is required");this.evictExpired();let r=this.cacheKey(e,t?.key),n=this.prewarmCache.get(r);return n?(this.prewarmCache.delete(r),n.promise):this.doCreate(e)}async doCreate(e){let t={title:e.title};e.fallbackUrl&&(t.fallbackUrl=e.fallbackUrl),e.iosDeepLinkPath&&(t.iosDeepLinkPath=e.iosDeepLinkPath),e.androidDeepLinkPath&&(t.androidDeepLinkPath=e.androidDeepLinkPath),e.linkData&&(t.linkData=e.linkData),e.ogTitle&&(t.ogTitle=e.ogTitle),e.ogDescription&&(t.ogDescription=e.ogDescription),e.ogImageUrl&&(t.ogImageUrl=e.ogImageUrl),e.campaign&&(t.campaign=e.campaign),e.source&&(t.source=e.source),e.medium&&(t.medium=e.medium),e.channel&&(t.channel=e.channel),e.tags&&(t.tags=e.tags),e.expiresAt&&(t.expiresAt=e.expiresAt),e.iosBundleId&&(t.iosBundleId=e.iosBundleId),e.androidPackageName&&(t.androidPackageName=e.androidPackageName);let r=await this.http.post("/sdk/links",t);if(!r.success||!r.data)throw he(r.error?.code,r.error?.message??"links.create failed");return r.data}async revoke(e){if(!pe.test(e))throw new h("INVALID_INPUT",`links.revoke: '${e}' is not a valid shortcode`);let t=await this.http.post(`/sdk/links/${encodeURIComponent(e)}/revoke`,{});if(!t.success||!t.data)throw he(t.error?.code,t.error?.message??"links.revoke failed");return{shortcode:t.data.shortcode,revoked:!0}}async getStats(e){if(!pe.test(e))throw new h("INVALID_INPUT",`links.getStats: '${e}' is not a valid shortcode`);let t=await this.http.get(`/sdk/links/${encodeURIComponent(e)}/stats`);if(!t.success||!t.data)throw he(t.error?.code,t.error?.message??"links.getStats failed");return t.data}};var Z=class{constructor(e){this.http=e}async reportInstall(e={}){let t=await this.http.post("/attribution/install",e);if(!t.success||!t.data)throw new Error(t.error?.message??"attribution.reportInstall failed");return t.data}async checkDeferred(e={}){let t=await this.http.post("/attribution/deferred",e);if(!t.success)throw new Error(t.error?.message??"attribution.checkDeferred failed");return t.data??null}};var ee=class{constructor(e){this.http=e}async createTicket(e){let t=await this.http.post("/support/tickets",e);if(!t.success||!t.data)throw new Error(t.error?.message??"support.createTicket failed");return t.data}async submitCsat(e){let t=await this.http.post("/support/csat",e);if(!t.success||!t.data)throw new Error(t.error?.message??"support.submitCsat failed");return t.data}};var te=class{constructor(e){this.http=e}async search(e){if(!e.query||e.query.trim().length===0)return[];let t=new URLSearchParams({query:e.query});e.limit&&t.set("limit",String(e.limit));let r=await this.http.get(`/kb/search?${t.toString()}`);if(!r.success||!Array.isArray(r.data))throw new Error(r.error?.message??"kb.search failed");return r.data}};var se=class se{constructor(e){this.autoTrackCleanup=null;this.http=new Y(e),this.storage=new Q,this.consent=new F(e.defaultConsent===!0,this.http),this.analytics=new N(e,this.http,this.storage,this.consent),this.flags=new x(e,this.http,this.storage),this.surveys=new O(e,this.http,this.storage),this.messages=new M(e,this.http,this.storage),this.chatbot=new H(e,this.http,this.storage),this.auth=new V(e,this.http,this.storage,this.analytics,this.flags,this.chatbot),this.webPush=new W(this.http),this.audiences=new $(this.http),this.links=new B(this.http),this.attribution=new Z(this.http),this.support=new ee(this.http),this.kb=new te(this.http),e.autoTrack!==!1&&(this.autoTrackCleanup=Pe(this.analytics,e))}static init(e){return ke(e.apiKey),new se(e)}static sanitizeHtml(e){return J(e)}static safeUrl(e){return S(e)}track(e,t){this.analytics.track(e,t)}page(e){this.analytics.page(e)}identify(e,t,r){this.analytics.identify(e,t,r),this.flags.setContext({userId:e})}reset(){this.analytics.reset(),this.flags.reset(),this.chatbot.newSession()}async flush(){await this.analytics.flush()}destroy(){this.autoTrackCleanup&&(this.autoTrackCleanup(),this.autoTrackCleanup=null),this.analytics.destroy(),this.flags.destroy(),this.messages.destroy()}};se.version=E;var re=se,wt=re;0&&(module.exports={AlreadyIdentifiedError,Analytics,Audiences,Auth,AuthError,Chatbot,Consent,CredentialInUseError,EmailAlreadyTakenError,FeatureFlags,InAppMessaging,LinkError,Links,SendoraCloud,Surveys,ValidationError,WebPush,extractShortcodeFromUrl,safeUrl,sanitizeHtml});
package/dist/index.d.cts CHANGED
@@ -457,6 +457,8 @@ type DeviceTakeoverListener = (evt: DeviceTakeoverEvent) => void;
457
457
  * per sign-in that cancelled a pending self-service deletion within grace —
458
458
  * the account is restored with the SAME user_id.
459
459
  */
460
+ /** @see AuthApiResponse.anonRetirement */
461
+ type AnonRetirementOutcome = "retired" | "preserved" | "none";
460
462
  interface DeletionCancelledEvent {
461
463
  userId: string;
462
464
  at: Date;
@@ -632,6 +634,7 @@ declare class Auth {
632
634
  private lastTakeover;
633
635
  private deletionCancelledListeners;
634
636
  private lastDeletionCancelled;
637
+ private lastAnonRetirement;
635
638
  private authStateListeners;
636
639
  /**
637
640
  * Anon refresh token captured at `signIn()` time, stashed keyed to the
@@ -727,6 +730,15 @@ declare class Auth {
727
730
  onDeletionCancelled(listener: DeletionCancelledListener): () => void;
728
731
  /** The most recent deletion-cancelled event this session, or null. Cleared on signOut. */
729
732
  getLastDeletionCancelled(): DeletionCancelledEvent | null;
733
+ /**
734
+ * What the last sign-in did with this device's guest account, or null if no
735
+ * sign-in has happened this session (s58.278).
736
+ *
737
+ * The one worth acting on is `"preserved"`: a guest account was presented,
738
+ * was NOT retired, and is therefore still alive server-side — so "recover
739
+ * your other account" is a real offer rather than a guess. Cleared on signOut.
740
+ */
741
+ getLastAnonRetirement(): AnonRetirementOutcome | null;
730
742
  private fireDeletionCancelled;
731
743
  /**
732
744
  * Returns the stored anon refresh token iff the local subject is
@@ -1670,4 +1682,4 @@ declare class SendoraCloud {
1670
1682
  destroy(): void;
1671
1683
  }
1672
1684
 
1673
- export { AlreadyIdentifiedError, Analytics, type Audience, type AudienceRule, type AudienceRuleGroup, Audiences, Auth, AuthError, type AuthErrorKind, type AuthSignedOutReason, type AuthStateChange, type AuthTokens, type AuthUser, Chatbot, Consent, type CreateAudienceInput, type CredentialCollisionPolicy, CredentialInUseError, type DeletionCancelledEvent, type DeviceTakeoverEvent, EmailAlreadyTakenError, FeatureFlags, type FlagValue, type IdentifyOptions, type InAppMessage, InAppMessaging, type LinkCreateInput, type LinkCreateResult, type LinkData, LinkError, type LinkErrorCode, type LinkStats, type LinkedIdentitiesResponse, type LinkedIdentity, Links, SendoraCloud, type SendoraConfig, type SurveyConfig, type SurveyQuestion, Surveys, type TrackEvent, type UpdateAudienceInput, type UserTraits, ValidationError, WebPush, SendoraCloud as default, extractShortcodeFromUrl, safeUrl, sanitizeHtml };
1685
+ export { AlreadyIdentifiedError, Analytics, type AnonRetirementOutcome, type Audience, type AudienceRule, type AudienceRuleGroup, Audiences, Auth, AuthError, type AuthErrorKind, type AuthSignedOutReason, type AuthStateChange, type AuthTokens, type AuthUser, Chatbot, Consent, type CreateAudienceInput, type CredentialCollisionPolicy, CredentialInUseError, type DeletionCancelledEvent, type DeviceTakeoverEvent, EmailAlreadyTakenError, FeatureFlags, type FlagValue, type IdentifyOptions, type InAppMessage, InAppMessaging, type LinkCreateInput, type LinkCreateResult, type LinkData, LinkError, type LinkErrorCode, type LinkStats, type LinkedIdentitiesResponse, type LinkedIdentity, Links, SendoraCloud, type SendoraConfig, type SurveyConfig, type SurveyQuestion, Surveys, type TrackEvent, type UpdateAudienceInput, type UserTraits, ValidationError, WebPush, SendoraCloud as default, extractShortcodeFromUrl, safeUrl, sanitizeHtml };
package/dist/index.d.ts CHANGED
@@ -457,6 +457,8 @@ type DeviceTakeoverListener = (evt: DeviceTakeoverEvent) => void;
457
457
  * per sign-in that cancelled a pending self-service deletion within grace —
458
458
  * the account is restored with the SAME user_id.
459
459
  */
460
+ /** @see AuthApiResponse.anonRetirement */
461
+ type AnonRetirementOutcome = "retired" | "preserved" | "none";
460
462
  interface DeletionCancelledEvent {
461
463
  userId: string;
462
464
  at: Date;
@@ -632,6 +634,7 @@ declare class Auth {
632
634
  private lastTakeover;
633
635
  private deletionCancelledListeners;
634
636
  private lastDeletionCancelled;
637
+ private lastAnonRetirement;
635
638
  private authStateListeners;
636
639
  /**
637
640
  * Anon refresh token captured at `signIn()` time, stashed keyed to the
@@ -727,6 +730,15 @@ declare class Auth {
727
730
  onDeletionCancelled(listener: DeletionCancelledListener): () => void;
728
731
  /** The most recent deletion-cancelled event this session, or null. Cleared on signOut. */
729
732
  getLastDeletionCancelled(): DeletionCancelledEvent | null;
733
+ /**
734
+ * What the last sign-in did with this device's guest account, or null if no
735
+ * sign-in has happened this session (s58.278).
736
+ *
737
+ * The one worth acting on is `"preserved"`: a guest account was presented,
738
+ * was NOT retired, and is therefore still alive server-side — so "recover
739
+ * your other account" is a real offer rather than a guess. Cleared on signOut.
740
+ */
741
+ getLastAnonRetirement(): AnonRetirementOutcome | null;
730
742
  private fireDeletionCancelled;
731
743
  /**
732
744
  * Returns the stored anon refresh token iff the local subject is
@@ -1670,4 +1682,4 @@ declare class SendoraCloud {
1670
1682
  destroy(): void;
1671
1683
  }
1672
1684
 
1673
- export { AlreadyIdentifiedError, Analytics, type Audience, type AudienceRule, type AudienceRuleGroup, Audiences, Auth, AuthError, type AuthErrorKind, type AuthSignedOutReason, type AuthStateChange, type AuthTokens, type AuthUser, Chatbot, Consent, type CreateAudienceInput, type CredentialCollisionPolicy, CredentialInUseError, type DeletionCancelledEvent, type DeviceTakeoverEvent, EmailAlreadyTakenError, FeatureFlags, type FlagValue, type IdentifyOptions, type InAppMessage, InAppMessaging, type LinkCreateInput, type LinkCreateResult, type LinkData, LinkError, type LinkErrorCode, type LinkStats, type LinkedIdentitiesResponse, type LinkedIdentity, Links, SendoraCloud, type SendoraConfig, type SurveyConfig, type SurveyQuestion, Surveys, type TrackEvent, type UpdateAudienceInput, type UserTraits, ValidationError, WebPush, SendoraCloud as default, extractShortcodeFromUrl, safeUrl, sanitizeHtml };
1685
+ export { AlreadyIdentifiedError, Analytics, type AnonRetirementOutcome, type Audience, type AudienceRule, type AudienceRuleGroup, Audiences, Auth, AuthError, type AuthErrorKind, type AuthSignedOutReason, type AuthStateChange, type AuthTokens, type AuthUser, Chatbot, Consent, type CreateAudienceInput, type CredentialCollisionPolicy, CredentialInUseError, type DeletionCancelledEvent, type DeviceTakeoverEvent, EmailAlreadyTakenError, FeatureFlags, type FlagValue, type IdentifyOptions, type InAppMessage, InAppMessaging, type LinkCreateInput, type LinkCreateResult, type LinkData, LinkError, type LinkErrorCode, type LinkStats, type LinkedIdentitiesResponse, type LinkedIdentity, Links, SendoraCloud, type SendoraConfig, type SurveyConfig, type SurveyQuestion, Surveys, type TrackEvent, type UpdateAudienceInput, type UserTraits, ValidationError, WebPush, SendoraCloud as default, extractShortcodeFromUrl, safeUrl, sanitizeHtml };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- var ge=/^[a-zA-Z][a-zA-Z0-9._:-]{0,127}$/;var De=new Set(["__proto__","constructor","prototype"]),f=class extends Error{constructor(e){super(`[SendoraCloud] ${e}`),this.name="SendoraValidationError"}};function fe(s){if(typeof s!="string"||!ge.test(s))throw new f(`Invalid event name: must match ${ge}, got "${String(s)}".`)}function me(s){if(s==null)return;if(typeof s!="object"||Array.isArray(s))throw new f("Properties must be a plain object, not an array or primitive.");let e=ye(s);if(e>5)throw new f(`Properties nested too deeply (${e} levels, max 5).`);ve(s);let t;try{t=JSON.stringify(s).length}catch{throw new f("Properties contain a circular reference or unserializable value.")}if(t>32768)throw new f(`Properties too large (${t} bytes, max 32768).`)}function ye(s,e=0){if(e>6||s==null||typeof s!="object")return e;let t=e;for(let r of Object.keys(s)){let n=ye(s[r],e+1);n>t&&(t=n)}return t}function ve(s){if(!(s==null||typeof s!="object"))for(let e of Object.keys(s)){if(De.has(e))throw new f(`Forbidden property key "${e}" (prototype pollution guard).`);ve(s[e])}}var Ue=/^pk_[A-Za-z0-9_-]{16,}$/;function we(s){if(!s||typeof s!="string")throw new f("apiKey is required.");if(s.startsWith("sk_")||s.startsWith("sendora_secret_"))throw new f("Secret keys must NEVER be used in browser SDK. Use a publishable key (pk_...).");if(!Ue.test(s))throw new f("Invalid publishable key format. Expected pk_... (17+ chars).")}function ke(s){let e=s.replace(/\/$/,"");if(!(e.startsWith("http://localhost")||e.startsWith("http://127.0.0.1"))&&!e.startsWith("https://"))throw new f("apiUrl must be https:// (only http://localhost is allowed for dev).")}var Ae={name:"@sendoracloud/sdk-web",version:"3.16.0",description:"Sendora Cloud Web SDK \u2014 analytics, feature flags, surveys, in-app messaging, chatbot, auth, deep links (Branch / Firebase parity). Zero dependencies, TypeScript, SSR-safe.",type:"module",main:"./dist/index.cjs",module:"./dist/index.js",types:"./dist/index.d.ts",exports:{".":{types:"./dist/index.d.ts",import:"./dist/index.js",require:"./dist/index.cjs"}},files:["dist","README.md","LICENSE"],keywords:["sendora","analytics","feature-flags","surveys","in-app-messaging","chatbot","tracking","customer-engagement"],homepage:"https://sendoracloud.com/sdks",repository:{type:"git",url:"git+https://github.com/sendoracloud/sdk-web.git"},bugs:{url:"https://github.com/sendoracloud/sdk-web/issues"},author:"Sendora Cloud <hello@sendoracloud.com>",license:"Apache-2.0",publishConfig:{access:"public"},sideEffects:!1,scripts:{build:"tsup src/index.ts --format cjs,esm --dts --clean --minify",typecheck:"tsc --noEmit",prepublishOnly:"npm run build"},devDependencies:{typescript:"^5.4.0",tsup:"^8.0.0"}};var b=Ae.version;var xe="https://api.sendoracloud.com",Oe=1e4,Me="@sendoracloud/sdk-web",Y=class{constructor(e){let t=(e.apiUrl??xe).replace(/\/$/,"");ke(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,r){return this.request("POST",e,t,r)}patch(e,t,r){return this.request("PATCH",e,t,r)}delete(e,t){return this.request("DELETE",e,void 0,t)}async request(e,t,r,n){let i=`${this.baseUrl}/api/v1${t}`,o=typeof AbortController<"u"?new AbortController:null,a=o?setTimeout(()=>o.abort(),Oe):null;try{let c=await fetch(i,{method:e,headers:{"Content-Type":"application/json","X-API-Key":this.apiKey,"X-Sendora-SDK-Name":Me,"X-Sendora-SDK-Version":b,...n??{}},body:r?JSON.stringify(r):void 0,signal:o?o.signal:void 0});a&&clearTimeout(a);let l;try{l=await c.json()}catch{l={success:!1,error:{code:"PARSE_ERROR",message:`HTTP ${c.status}`}}}if(this.debug&&console.debug(`[SendoraCloud] ${e} ${t} \u2192 ${c.status}`),!c.ok){if(t.startsWith("/auth-service/")&&!this.debug)console.warn(`[SendoraCloud] ${e} ${t} \u2192 HTTP ${c.status}`);else{let v=l?.error?.code??"HTTP_ERROR",w=l?.error?.message??`HTTP ${c.status}`,k=l?.error?.details?.fieldErrors,C=k?` (${Object.entries(k).map(([A,re])=>`${A}: ${(re??[]).join(", ")}`).join("; ")})`:"";console.warn(`[SendoraCloud] ${e} ${t} \u2192 HTTP ${c.status} \u2014 ${v}: ${w}${C}`)}l&&l.error&&(l.error={...l.error,status:c.status})}return l}catch(c){a&&clearTimeout(a);let l=c instanceof Error&&c.name==="AbortError"?"timeout":"error";return console.warn(`[SendoraCloud] ${e} ${t} network ${l}`),{success:!1,error:{code:"NETWORK_ERROR",message:l}}}}};var X="sendora_",Q=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 r=X+e;if(this.useLocalStorage)try{localStorage.setItem(r,t);return}catch{}this.memoryStore.set(r,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 r=localStorage.length-1;r>=0;r--){let n=localStorage.key(r);n&&n.startsWith(t)&&localStorage.removeItem(n)}}catch{}for(let r of Array.from(this.memoryStore.keys()))r.startsWith(t)&&this.memoryStore.delete(r)}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 L(s=""){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 r="";for(let n of e)r+=n.toString(16).padStart(2,"0");return s+r}var Ie=500,oe=10,ae=6e4,x=class{constructor(e,t,r,n){this.queue=[];this.timer=null;this.userId=null;this.identityToken=null;this.consecutiveFailures=0;this.nextFlushAfter=0;this.breakerOpenAt=0;this.breakerWarned=!1;this.http=t,this.storage=r,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,this.appVersion=e.appVersion,this.storage.get("schema_version")===null&&this.storage.set("schema_version","1");let i=this.storage.get("anonymous_id");if(i?this.anonymousId=i:(this.anonymousId=L("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(e,t){fe(e),t&&me(t),this.queue.length>=Ie&&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,r){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=r?.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=L("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;this.consecutiveFailures>oe&&Date.now()-this.breakerOpenAt>=ae&&(this.consecutiveFailures=0,this.breakerOpenAt=0,this.breakerWarned=!1);let e=this.queue.splice(0,100),t=this.getSessionId(),r=e.map(a=>({projectId:this.projectId,module:"custom",eventType:a.eventType,userId:a.userId,anonymousId:a.anonymousId,identityToken:a.identityToken,timestamp:a.timestamp??new Date().toISOString(),properties:a.properties??{},context:this.getContext(),...t?{sessionId:t}:{},consent:["analytics"]}));if((await this.http.post("/events/batch",{events:r})).success){this.consecutiveFailures=0,this.breakerOpenAt=0,this.breakerWarned=!1;return}if(this.consecutiveFailures++,this.consecutiveFailures>oe){this.breakerWarned||(this.breakerOpenAt=Date.now(),this.breakerWarned=!0,console.warn(`[SendoraCloud] Analytics circuit breaker open after ${oe} consecutive failed flushes \u2014 events will be dropped until a flush succeeds or ${ae}ms elapse.`));return}let i=Math.min(ae,1e3*2**this.consecutiveFailures);this.nextFlushAfter=Date.now()+i;let o=Math.max(0,Ie-this.queue.length);o>0&&this.queue.unshift(...e.slice(0,o))}destroy(){this.timer&&clearInterval(this.timer),this.timer=null,this.flush()}getSessionId(){try{return typeof sessionStorage>"u"?void 0:sessionStorage.getItem("sendora_session_id")||void 0}catch{return}}getContext(){return typeof window>"u"?{sdk:{name:"@sendoracloud/sdk-web",version:b}}:{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,device:this.buildDevice(),sdk:{name:"@sendoracloud/sdk-web",version:b}}}buildDevice(){if(typeof navigator>"u")return;let e=navigator.userAgent||"",r=navigator.userAgentData?.mobile??/Mobi|Android|iPhone|iPod/i.test(e);return{type:/iPad|Tablet|(?:Android(?!.*Mobile))/i.test(e)?"tablet":r?"mobile":"desktop",os:"Web",osVersion:"",model:"",appVersion:this.resolveAppVersion(),browser:He(e)}}resolveAppVersion(){if(this.appVersion)return this.appVersion;if(typeof document<"u"){let r=document.querySelector('meta[name="sendora:app-version"]')?.getAttribute("content");if(r)return r}let e=globalThis.__SENDORA_APP_VERSION__;return typeof e=="string"?e:""}};function He(s){let e=[["Edge",/Edg\/(\d+)/],["Opera",/OPR\/(\d+)/],["Firefox",/Firefox\/(\d+)/],["Samsung Internet",/SamsungBrowser\/(\d+)/],["Chrome",/Chrome\/(\d+)/],["Safari",/Version\/(\d+)[^ ]* Safari/]];for(let[t,r]of e){let n=r.exec(s);if(n)return`${t} ${n[1]}`}return"Unknown"}var O=class{constructor(e,t,r){this.cache=new Map;this.refreshInterval=null;this.http=t,this.storage=r;let n=this.storage.get("flags");if(n)try{let i=JSON.parse(n);if(Array.isArray(i))for(let o of i)ce(o)&&this.cache.set(o.key,o)}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 r=await this.http.post("/feature-flags/evaluate",{key:e,userId:this.userId,entityId:this.entityId,environment:this.environment});return r.success&&r.data&&ce(r.data)?(this.cache.set(e,r.data),this.persistCache(),r.data.value):null}async isEnabled(e,t=!1){let r=await this.getFlag(e);return typeof r=="boolean"?r: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)ce(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,r]of this.cache)e[t]=r.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 ce(s){if(s==null||typeof s!="object")return!1;let e=s;return Object.prototype.hasOwnProperty.call(e,"__proto__")||Object.prototype.hasOwnProperty.call(e,"constructor")?!1:typeof e.key=="string"&&"value"in e}var Fe=32*1024,M=class{constructor(e,t,r){this.activeSurvey=null;this.http=t,this.storage=r}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 r=await this.http.get(`/surveys/${encodeURIComponent(e)}/config`);return!r.success||!r.data||!Ke(r.data)?null:(this.activeSurvey=r.data,this.storage.set(t,"1"),this.onSurveyDisplay&&this.onSurveyDisplay(r.data),r.data)}async submitResponse(e,t,r=!0){let n=0;try{n=JSON.stringify(t).length}catch{return!1}if(n>Fe)return!1;let i=await this.http.post("/surveys/responses",{surveyId:e,answers:t,completed:r});return i.success&&(this.activeSurvey=null),i.success}dismiss(){this.activeSurvey=null}getActiveSurvey(){return this.activeSurvey}};function Ke(s){if(s==null||typeof s!="object")return!1;let e=s;return typeof e.id!="string"||typeof e.name!="string"||!Array.isArray(e.questions)?!1:e.questions.every(Ve)}function Ve(s){if(s==null||typeof s!="object")return!1;let e=s;return typeof e.id=="string"&&typeof e.text=="string"}function E(s){if(!(!s||typeof s!="string"))try{let e=typeof window<"u"?window.location.href:"https://local/",t=new URL(s,e);return t.protocol==="http:"||t.protocol==="https:"?t.href:void 0}catch{return}}var We=new Set(["b","i","u","em","strong","a","p","br","span","ul","ol","li","h1","h2","h3","h4"]),$e={a:new Set(["href","target","rel"])};function ue(s){if(typeof DOMParser>"u")return"";let t=new DOMParser().parseFromString(`<div>${s}</div>`,"text/html").body.firstElementChild;return t?(Se(t),t.innerHTML):""}function Se(s){let e=Array.from(s.children);for(let t of e){let r=t.tagName.toLowerCase();if(!We.has(r)){let i=document.createTextNode(t.textContent??"");t.replaceWith(i);continue}let n=$e[r]??new Set;for(let i of Array.from(t.attributes)){if(!n.has(i.name.toLowerCase())){t.removeAttribute(i.name);continue}if(i.name.toLowerCase()==="href"){let o=E(i.value);o?t.setAttribute("href",o):t.removeAttribute(i.name)}}r==="a"&&(t.setAttribute("rel","noopener noreferrer"),t.setAttribute("target","_blank")),Se(t)}}var H=class{constructor(e,t,r){this.messages=[];this.pollInterval=null;this.http=t,this.storage=r}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(Be);for(let t of this.messages){let r=`iam_shown_${t.id}`;if(!this.storage.get(r))return this.storage.set(r,"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 Be(s){return{...s,content:{...s.content,ctaUrl:E(s.content.ctaUrl),imageUrl:E(s.content.imageUrl)}}}var qe=4e3,F=class{constructor(e,t,r){this.chatbotId=null;this.messages=[];this.http=t,this.storage=r;let n=this.storage.get("chat_session_id");this.sessionId=n??L("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>qe)return null;let r={role:"user",content:e,timestamp:new Date().toISOString()};this.messages.push(r);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 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=L("chat_"),this.storage.set("chat_session_id",this.sessionId)}getSessionId(){return this.sessionId}};var K=class{constructor(e,t){this.listeners=new Set;this.granted=e,this.http=t}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)}async record(e){if(!this.http)throw new Error("consent.record requires SDK init (HttpClient unavailable)");let t=await this.http.post("/consent/record",e);if(!t.success||!t.data)throw new Error(t.error?.message??"consent.record failed");return t.data}async requestDeletion(e){if(!this.http)throw new Error("consent.requestDeletion requires SDK init (HttpClient unavailable)");let t=await this.http.post("/consent/deletion-request",e);if(!t.success||!t.data)throw new Error(t.error?.message??"consent.requestDeletion failed");return t.data}emit(){for(let e of this.listeners)try{e(this.granted)}catch{}}};var T="auth_access_token",R="auth_access_expires",m="auth_refresh_token",_="auth_user",je=3e4,ze=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function y(s){if(!s||typeof s!="object")return!1;let e=s,t=e.user,r=e.tokens;return!(!t||typeof t.id!="string"||t.id.length===0||!r||typeof r.accessToken!="string"||r.accessToken.length===0||typeof r.refreshToken!="string"||r.refreshToken.length===0||typeof r.expiresIn!="number"||r.expiresIn<=0||t.isAnonymous!==void 0&&typeof t.isAnonymous!="boolean"||t.email!==void 0&&t.email!==null&&typeof t.email!="string"||t.emailVerified!==void 0&&typeof t.emailVerified!="boolean"||t.name!==void 0&&t.name!==null&&typeof t.name!="string"||t.signupMethod!==void 0&&t.signupMethod!==null&&typeof t.signupMethod!="string"||t.lastLoginMethod!==void 0&&t.lastLoginMethod!==null&&typeof t.lastLoginMethod!="string"||e.retiredAnonUserId!==void 0&&typeof e.retiredAnonUserId!="string")}var D=class extends Error{constructor(e="An account with this email already exists"){super(e),this.name="EmailAlreadyTakenError"}},W=class extends Error{constructor(e="Already signed in with an identity; use link*() to add a credential, or sign out first"){super(e),this.name="AlreadyIdentifiedError"}},U=class extends Error{constructor(t="This credential is already linked to a different account",r){super(t);this.code="CREDENTIAL_IN_USE";this.kind="credential_in_use";this.retryable=!1;this.name="CredentialInUseError",this.provider=r?.provider,this.collision=r?.collision,this.status=r?.status}};function be(s){let e=s?.error?.details;if(!e||typeof e!="object")return{};let t=e;return{provider:typeof t.provider=="string"?t.provider:void 0,collision:t.collision==="identity"||t.collision==="email"?t.collision:void 0}}var Ge=new Set(["network","server","rate_limited","account_locked"]);function Ye(s,e){switch(s){case"NETWORK_ERROR":case"NETWORK_TIMEOUT":return"network";case"PARSE_ERROR":return"server";case"RATE_LIMITED":case"RATE_LIMIT":case"RATE_LIMIT_EXCEEDED":case"QUOTA_EXCEEDED":return"rate_limited";case"ACCOUNT_LOCKED":return"account_locked";case"CREDENTIAL_IN_USE":return"credential_in_use";case"NOT_ANONYMOUS":case"FORBIDDEN_NON_ANONYMOUS":return"already_identified";case"PASSKEY_USER_CANCELLED":case"GAME_CENTER_CANCELLED":case"PLAY_GAMES_CANCELLED":case"SSO_CANCELLED":return"cancelled";case"GAME_CENTER_UNAVAILABLE":case"PLAY_GAMES_UNAVAILABLE":case"ENTITLEMENT_ERROR":case"tier_required":return"config";case"UNAUTHORIZED":case"INVALID_CREDENTIALS":case"INVALID_REFRESH_TOKEN":case"EMAIL_OTP_INVALID":case"MAGIC_LINK_INVALID":case"PASSKEY_RP_MISMATCH":case"NOT_SIGNED_IN":return"invalid_credential";default:break}if(typeof e=="number"){if(e===429)return"rate_limited";if(e>=500)return"server";if(e>=400)return"invalid_credential"}return"unknown"}var u=class extends Error{constructor(e,t,r){super(t),this.name="AuthError",this.code=e,this.kind=r?.kind??Ye(e,r?.status),this.retryable=Ge.has(this.kind),this.retryAfterSeconds=r?.retryAfterSeconds,this.status=r?.status}};function Xe(s){if(s instanceof u||s instanceof D||s instanceof W||s instanceof U)return s;let e=s?.name;return e==="AbortError"||e==="TimeoutError"||e==="NetworkTimeoutError"?new u("NETWORK_TIMEOUT",s instanceof Error?s.message:"timeout",{kind:"network"}):new u("UNKNOWN_ERROR",s instanceof Error?s.message:String(s),{kind:"unknown"})}function h(s){let e=s.error?.status,t=s.error?.details?.retryAfterSeconds;return{status:typeof e=="number"?e:void 0,retryAfterSeconds:typeof t=="number"&&Number.isFinite(t)&&t>0?t:void 0}}var $=class{constructor(e,t,r,n,i,o){this.user=null;this.accessToken=null;this.accessExpiresAt=0;this.inflight=Promise.resolve();this.refreshInflight=null;this.refreshFailureCount=0;this.refreshCooldownUntil=0;this.visibilityResetUnsub=null;this.proactiveRefreshTimer=null;this.proactiveRefreshCleanup=null;this.takeoverListeners=new Set;this.lastTakeover=null;this.deletionCancelledListeners=new Set;this.lastDeletionCancelled=null;this.authStateListeners=new Set;this.pendingAnonTakeover=null;this.hydrated=!1;this.http=t,this.storage=r,this.analytics=n,this.flags=i,this.chatbot=o;let a=this.storage.get(_),c=this.storage.get(T),l=this.storage.get(m),g=this.storage.get(R);if(a&&(c||l))try{let v=JSON.parse(a);if(typeof v.id!="string"||v.id.length===0)throw new Error("invalid cache");this.user=v,c?(this.accessToken=c,this.accessExpiresAt=g?Number(g):0):(this.accessToken=null,this.accessExpiresAt=0),this.analytics.identify(this.user.id)}catch{this.storage.remove(_),this.storage.remove(T),this.storage.remove(R)}this.hydrated=!0}getCurrentUser(){return this.user}onDeviceTakeover(e){return this.takeoverListeners.add(e),()=>{this.takeoverListeners.delete(e)}}getLastDeviceTakeover(){return this.lastTakeover}onAuthStateChanged(e){if(this.authStateListeners.add(e),this.hydrated&&this.user){let t=this.user;try{e({event:"signed_in",user:t})}catch{}}return()=>{this.authStateListeners.delete(e)}}emitAuthState(e){for(let t of this.authStateListeners)try{t(e)}catch{}}fireDeviceTakeover(e,t){if(!e||!ze.test(e))return;let r={retiredAnonUserId:e,identifiedUserId:t,at:new Date};this.lastTakeover=r;for(let n of this.takeoverListeners)try{n(r)}catch{}this.emitAuthState({event:"device_takeover",user:this.user,retiredAnonUserId:e})}onDeletionCancelled(e){return this.deletionCancelledListeners.add(e),()=>{this.deletionCancelledListeners.delete(e)}}getLastDeletionCancelled(){return this.lastDeletionCancelled}fireDeletionCancelled(e){let t={userId:e,at:new Date};this.lastDeletionCancelled=t;for(let r of this.deletionCancelledListeners)try{r(t)}catch{}this.emitAuthState({event:"deletion_cancelled",user:this.user})}takeoverHint(){if(this.user?.isAnonymous)return this.storage.get(m)??void 0}async getAccessToken(){return this.accessToken&&this.accessExpiresAt&&Date.now()<this.accessExpiresAt-je?this.accessToken:this.storage.get(m)?this.refreshInflight?this.refreshInflight:Date.now()<this.refreshCooldownUntil?null:this.refreshAccessToken():null}getAccessTokenSync(){return this.accessToken}signInAnonymously(e){return this.serialize(async()=>{let t=this.user;if(!e?.forceNew&&t?.isAnonymous&&this.storage.get(m))return t;let r=await this.http.post("/auth-service/anonymous",{name:e?.name,metadata:e?.metadata});if(!r.success||!y(r.data))throw new u(r.error?.code??"AUTH_ERROR",r.error?.message??"Anonymous signup failed",h(r));return this.persist(r.data),r.data.user})}signUp(e,t,r){return this.serialize(async()=>{let n=this.storage.get(m);if(n){let o=await this.http.post("/auth-service/upgrade",{refreshToken:n,email:e,password:t,name:r?.name});if(o.success&&y(o.data))return this.persist(o.data),o.data.user;let a=o.error?.code??"",c=h(o),l=c.status;if(a==="NOT_ANONYMOUS"||a==="FORBIDDEN_NON_ANONYMOUS")throw new W(o.error?.message);if(a==="CONFLICT"||a==="EMAIL_ALREADY_TAKEN"||l===409)throw new D(o.error?.message);if(a==="INVALID_REFRESH"||a==="UNAUTHORIZED"||a==="REFRESH_EXPIRED")await this.analytics.flush(),this.wipeLocalIdentity("session_expired");else throw new u(a||"AUTH_ERROR",o.error?.message??"Upgrade failed",c)}let i=await this.http.post("/auth-service/signup",{email:e,password:t,name:r?.name,metadata:r?.metadata});if(!i.success||!y(i.data)){let o=i.error?.code??"";throw o==="CONFLICT"?new D(i.error?.message):new u(o||"AUTH_ERROR",i.error?.message??"Signup failed",h(i))}return this.persist(i.data),i.data.user})}signIn(e,t){return this.serialize(async()=>{let r=this.takeoverHint(),n=await this.http.post("/auth-service/login",{email:e,password:t,...r?{prevAnonRefreshToken:r}:{}});if(!n.success||!n.data)throw new u(n.error?.code??"AUTH_ERROR",n.error?.message??"Login failed",h(n));let i=n.data;if(i.mfaRequired===!0&&typeof i.mfaChallengeToken=="string"){r&&(this.pendingAnonTakeover={challengeToken:i.mfaChallengeToken,prevAnonRefreshToken:r});let o=i.user;return{mfaRequired:!0,mfaChallengeToken:i.mfaChallengeToken,userId:o?.id??""}}if(!y(n.data))throw new u(n.error?.code??"AUTH_ERROR",n.error?.message??"Login failed",h(n));return await this.replaceLocalIdentity(),this.persist(n.data),n.data.user})}loginSocial(e){return this.serialize(async()=>{let t=this.takeoverHint(),{link:r,onCredentialInUse:n,...i}=e,o=await this.http.post("/auth-service/login/social",{...i,...t?{prevAnonRefreshToken:t}:{},...r?{linkAnonymous:!0}:{},...n?{onCredentialInUse:n}:{}});if(!o.success||!y(o.data))throw o.error?.code==="CREDENTIAL_IN_USE"?new U(o.error.message,{...be(o),status:h(o).status}):new u(o.error?.code??"SOCIAL_LOGIN_FAILED",o.error?.message??"Social login failed",h(o));return await this.replaceLocalIdentity(),this.persist(o.data),o.data.user})}signInWithGoogle(e,t,r){return this.loginSocial({provider:"google",code:e,redirectUri:t,link:r})}signInWithGitHub(e,t){return this.loginSocial({provider:"github",code:e,redirectUri:t})}signInWithApple(e){return this.loginSocial({provider:"apple",...e})}signInWithMicrosoft(e,t){return this.loginSocial({provider:"microsoft",code:e,redirectUri:t})}signInWithLinkedIn(e,t){return this.loginSocial({provider:"linkedin",code:e,redirectUri:t})}signInWithFacebook(e,t){return this.loginSocial({provider:"facebook",code:e,redirectUri:t})}linkEmailPassword(e,t){return this.linkCredential("/auth-service/me/link/email",{email:e,password:t})}linkSocial(e){return this.linkCredential("/auth-service/me/link/social",e)}linkGoogle(e){return this.linkSocial({provider:"google",...e})}linkApple(e){return this.linkSocial({provider:"apple",...e})}async linkCredential(e,t){let r=await this.getAccessToken();if(!r)throw new u("NOT_SIGNED_IN","Sign in before linking a credential");let n=await this.http.post(e,t,{Authorization:`Bearer ${r}`});if(!n.success||!n.data?.user){let i=n.error?.code??"LINK_FAILED";throw i==="CREDENTIAL_IN_USE"?new U(n.error?.message,{...be(n),status:h(n).status}):new u(i,n.error?.message??"Failed to link credential",h(n))}return n.data.upgraded&&y(n.data)?(this.persist(n.data),n.data.user):(this.updateLocalUser(n.data.user),n.data.user)}updateLocalUser(e){this.user=e,this.storage.set(_,JSON.stringify(e))}async listLinkedIdentities(){let e=await this.getAccessToken();if(!e)throw new u("NOT_SIGNED_IN","Sign in before listing linked identities");let t=await this.http.get("/auth-service/me/identities",{Authorization:`Bearer ${e}`});if(!t.success||!t.data)throw new u(t.error?.code??"IDENTITIES_FAILED",t.error?.message??"Failed to list linked identities",h(t));return{identities:t.data.identities??[],hasPassword:!!t.data.hasPassword}}signInWithDiscord(e,t){return this.loginSocial({provider:"discord",code:e,redirectUri:t})}challengeMfa(e,t){return this.serialize(async()=>{let n=(this.pendingAnonTakeover?.challengeToken===e?this.pendingAnonTakeover.prevAnonRefreshToken:void 0)??this.takeoverHint(),i=await this.http.post("/auth-service/mfa/challenge",{challengeToken:e,code:t,...n?{prevAnonRefreshToken:n}:{}});if(!i.success||!y(i.data))throw new u(i.error?.code??"MFA_CHALLENGE_FAILED",i.error?.message??"Invalid MFA code",h(i));return await this.replaceLocalIdentity(),this.persist(i.data),i.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()=>{let r=this.takeoverHint(),n=await this.http.post("/auth-service/email-otp/verify",{email:e,code:t,...r?{prevAnonRefreshToken:r}:{}});if(!n.success||!y(n.data))throw new u(n.error?.code??"EMAIL_OTP_INVALID",n.error?.message??"Wrong or expired code",h(n));return await this.replaceLocalIdentity(),this.persist(n.data),n.data.user})}async requestPasswordReset(e){let t=await this.http.post("/auth-service/password/forgot",{email:e});if(!t.success)throw new u(t.error?.code??"PASSWORD_RESET_REQUEST_FAILED",t.error?.message??"Failed to send reset email")}async resetPassword(e,t){let r=await this.http.post("/auth-service/password/reset",{token:e,newPassword:t});if(!r.success)throw new u(r.error?.code??"PASSWORD_RESET_FAILED",r.error?.message??"Reset failed")}async verifyEmail(e){let t=await this.http.post("/auth-service/email/verify",{token:e});if(!t.success)throw new u(t.error?.code??"EMAIL_VERIFICATION_FAILED",t.error?.message??"Verification failed")}async sendVerificationEmail(){let e=await this.getAccessToken();if(!e)throw new u("UNAUTHORIZED","Sign in before requesting a verification email");let t=await this.http.post("/auth-service/email/verify/resend",{},{Authorization:`Bearer ${e}`});if(!t.success)throw new u(t.error?.code??"VERIFICATION_RESEND_FAILED",t.error?.message??"Failed to send verification email")}verifyMagicLink(e){return this.serialize(async()=>{let t=this.takeoverHint(),r=await this.http.post("/auth-service/magic-link/verify",{token:e,...t?{prevAnonRefreshToken:t}:{}});if(!r.success||!y(r.data))throw new u(r.error?.code??"MAGIC_LINK_INVALID",r.error?.message??"Invalid or expired magic link",h(r));return await this.replaceLocalIdentity(),this.persist(r.data),r.data.user})}async enrollMfa(){let e=await this.http.post("/auth-service/mfa/enroll/start",{},await 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},await this.bearerHeaders());return!!(t.success&&t.data?.confirmed)}async disableMfa(){await this.http.post("/auth-service/mfa/disable",{},await 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",{},await this.bearerHeaders());if(!t.success||!t.data)throw new u(t.error?.code??"PASSKEY_START_FAILED",t.error?.message??"Passkey start failed");let r=Je(t.data),n;try{let a=await navigator.credentials.create({publicKey:r});if(!a)throw new Error("create returned null");n=a}catch(a){throw new u("PASSKEY_USER_CANCELLED",a instanceof Error?a.message:"User cancelled passkey registration")}let i=et(n),o=await this.http.post("/auth-service/passkeys/register/finish",{response:i,name:e},await this.bearerHeaders());if(!o.success||!o.data)throw new u(o.error?.code??"PASSKEY_FINISH_FAILED",o.error?.message??"Passkey verification failed");return o.data}signInWithPasskey(e){return this.serialize(async()=>{if(typeof navigator>"u"||!navigator.credentials)throw new u("PASSKEY_UNSUPPORTED","WebAuthn not supported in this environment");let t=this.takeoverHint(),r=await this.http.post("/auth-service/passkeys/authenticate/start",{userId:e??null});if(!r.success||!r.data)throw new u(r.error?.code??"PASSKEY_START_FAILED",r.error?.message??"Passkey start failed",h(r));let n=Ze(r.data),i;try{let c=await navigator.credentials.get({publicKey:n});if(!c)throw new Error("get returned null");i=c}catch(c){throw new u("PASSKEY_USER_CANCELLED",c instanceof Error?c.message:"User cancelled passkey signin")}let o=st(i),a=await this.http.post("/auth-service/passkeys/authenticate/finish",{response:o,userId:e??null,...t?{prevAnonRefreshToken:t}:{}});if(!a.success||!y(a.data))throw new u(a.error?.code??"PASSKEY_AUTH_FAILED",a.error?.message??"Passkey signin failed",h(a));return await this.replaceLocalIdentity(),this.persist(a.data),a.data.user})}async listPasskeys(){let e=await this.http.get("/auth-service/passkeys",await this.bearerHeaders());return!e.success||!Array.isArray(e.data)?[]:e.data}async deletePasskey(e){await this.http.delete(`/auth-service/passkeys/${e}`,await this.bearerHeaders())}async listMySessions(){let e=await this.http.get("/auth-service/sessions/me",await this.bearerHeaders());return!e.success||!Array.isArray(e.data)?[]:e.data}async revokeSession(e){await this.http.delete(`/auth-service/sessions/me/${e}`,await this.bearerHeaders())}async revokeAllSessions(){await this.http.delete("/auth-service/sessions/me",await this.bearerHeaders())}async startSso(e){let t=this.takeoverHint(),r=await this.http.post("/auth-service/sso/oidc/start",{returnTo:e,...t?{prevAnonRefreshToken:t}:{}});if(!r.success||!r.data)throw new u(r.error?.code??"SSO_START_FAILED",r.error?.message??"SSO start failed");return r.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 r=await this.startSso(t);typeof window<"u"&&(window.location.href=r.authorizationUrl)}async startSaml(e){let t=this.takeoverHint(),r=await this.http.post("/auth-service/sso/saml/start",{returnTo:e,...t?{prevAnonRefreshToken:t}:{}});if(!r.success||!r.data)throw new u(r.error?.code??"SAML_START_FAILED",r.error?.message??"SAML start failed");return r.data}async signInWithSaml(e){let t=e??(typeof window<"u"?window.location.origin:"");if(!t)throw new u("SAML_NO_RETURN_TO","returnTo required outside browser context");let r=await this.startSaml(t);typeof window<"u"&&(window.location.href=r.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),r=t.get("sendora_oidc_error")??t.get("sendora_saml_error");if(r)throw window.history.replaceState(null,"",window.location.pathname+window.location.search),new u("SSO_FAILED",decodeURIComponent(r));let n=t.get("sendora_oidc_token")??t.get("sendora_saml_token");if(!n)return null;let i=t.get("sendora_retired_anon");window.history.replaceState(null,"",window.location.pathname+window.location.search);let o=await this.http.post("/auth-service/token/refresh",{refreshToken:n}),a=o.success?Ee(o.data):null;if(!a)throw new u(o.error?.code??"SSO_EXCHANGE_FAILED",o.error?.message??"Could not exchange SSO token",h(o));let c={...a,tokenType:"Bearer"};await this.replaceLocalIdentity(),this.accessToken=c.accessToken,this.accessExpiresAt=Date.now()+c.expiresIn*1e3,this.storage.set(T,c.accessToken),this.storage.set(R,String(this.accessExpiresAt)),this.storage.set(m,c.refreshToken);let l=rt(c.accessToken),g={id:l?.sub??"",email:l?.email??null,emailVerified:l?.email_verified??!0,name:l?.name??null,isAnonymous:!1};return this.user=g,this.storage.set(_,JSON.stringify(g)),this.analytics.identify(g.id),this.flags.setContext({userId:g.id}),this.emitAuthState({event:"signed_in",user:g}),i&&this.fireDeviceTakeover(i,g.id),g})}async bearerHeaders(){let e=await this.getAccessToken();if(!e)throw new u("NOT_SIGNED_IN","Not signed in");return{Authorization:`Bearer ${e}`}}signOut(){return this.serialize(async()=>{let e=this.storage.get(m);this.wipeLocalIdentity("user"),e&&this.http.post("/auth-service/token/revoke",{refreshToken:e}).catch(()=>{})})}serialize(e){let t=async()=>{try{return await e()}catch(n){throw Xe(n)}},r=this.inflight.then(t,t);return this.inflight=r.catch(()=>{}),r}async refreshAccessToken(){if(this.refreshInflight)return this.refreshInflight;let e=this.storage.get(m);if(!e)return null;let t=typeof navigator<"u"&&"locks"in navigator,r=async()=>{let n=this.storage.get(m);if(n&&n!==e){let i=this.storage.get(T),o=this.storage.get(R);if(i&&o){let a=Number(o);if(Number.isFinite(a)&&a>Date.now())return this.accessToken=i,this.accessExpiresAt=a,i}}try{let i=n??e,o=await this.http.post("/auth-service/token/refresh",{refreshToken:i});if(!o.success)return Qe(o.error?.code)&&this.tokenStillCurrent(i)?(this.wipeLocalIdentity("session_expired"),this.refreshFailureCount=0,this.refreshCooldownUntil=0,null):(this.armBackoff(),null);let a=Ee(o.data);if(!a)return this.armBackoff(),null;if(!this.tokenStillCurrent(i))return null;if(this.accessToken=a.accessToken,this.accessExpiresAt=Date.now()+a.expiresIn*1e3,this.storage.set(T,a.accessToken),this.storage.set(R,String(this.accessExpiresAt)),this.storage.set(m,a.refreshToken),this.user===null){let c=tt(o.data);c&&(this.user=c,this.storage.set(_,JSON.stringify(c)),this.analytics.identify(c.id),this.emitAuthState({event:"signed_in",user:c}))}return this.refreshFailureCount=0,this.refreshCooldownUntil=0,a.accessToken}catch{return this.armBackoff(),null}};return this.refreshInflight=(async()=>{try{return t?await navigator.locks.request("sendora-authservice-refresh",{mode:"exclusive"},r):await r()}finally{this.refreshInflight=null}})(),this.refreshInflight}armBackoff(){this.refreshFailureCount+=1;let e=Math.min(3e4,1e3*Math.pow(2,this.refreshFailureCount-1));this.refreshCooldownUntil=Date.now()+e}resetRefreshState(){this.refreshFailureCount=0,this.refreshCooldownUntil=0}startVisibilityRefreshReset(){if(this.visibilityResetUnsub||typeof document>"u")return;let e=()=>{document.visibilityState==="visible"&&this.resetRefreshState()};document.addEventListener("visibilitychange",e),this.visibilityResetUnsub=()=>document.removeEventListener("visibilitychange",e)}stopVisibilityRefreshReset(){this.visibilityResetUnsub&&(this.visibilityResetUnsub(),this.visibilityResetUnsub=null)}startProactiveRefreshCron(){if(typeof window>"u"||this.proactiveRefreshTimer)return;let e=.8,t=6e4,r=3e4,n=async()=>{if(!this.accessToken||!this.accessExpiresAt)return;let o=Date.now(),a=this.accessExpiresAt-o;if(a<=0)return;let l=Math.max(a,5*6e4)*(1-e)+(Math.random()*r*2-r);a<=l&&await this.refreshAccessToken()};this.proactiveRefreshTimer=window.setInterval(()=>{n()},t);let i=()=>{document.visibilityState==="visible"&&n()};document.addEventListener("visibilitychange",i),this.proactiveRefreshCleanup=()=>{this.proactiveRefreshTimer&&(window.clearInterval(this.proactiveRefreshTimer),this.proactiveRefreshTimer=null),document.removeEventListener("visibilitychange",i)}}stopProactiveRefreshCron(){this.proactiveRefreshCleanup&&(this.proactiveRefreshCleanup(),this.proactiveRefreshCleanup=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(T,e.tokens.accessToken),this.storage.set(R,String(this.accessExpiresAt)),this.storage.set(m,e.tokens.refreshToken),this.analytics.identify(e.user.id),this.flags.setContext({userId:e.user.id}),this.startProactiveRefreshCron(),this.startVisibilityRefreshReset(),this.emitAuthState({event:"signed_in",user:e.user}),e.retiredAnonUserId&&this.fireDeviceTakeover(e.retiredAnonUserId,e.user.id),e.reactivatedFromDeletion&&this.fireDeletionCancelled(e.user.id)}async replaceLocalIdentity(){this.user!==null&&(await this.analytics.flush(),this.wipeLocalIdentity("replaced"))}tokenStillCurrent(e){return this.storage.get(m)===e}wipeLocalIdentity(e="replaced"){this.user=null,this.accessToken=null,this.accessExpiresAt=0,this.lastTakeover=null,this.lastDeletionCancelled=null,this.pendingAnonTakeover=null,this.storage.remove(_),this.storage.remove(T),this.storage.remove(R),this.storage.remove(m),this.analytics.reset(),this.analytics.dropQueue(),this.flags.reset(),this.chatbot.newSession(),this.stopProactiveRefreshCron(),this.stopVisibilityRefreshReset(),this.refreshFailureCount=0,this.refreshCooldownUntil=0,e!=="replaced"&&this.emitAuthState({event:"signed_out",user:null,reason:e})}};function Qe(s){return s?s==="INVALID_REFRESH_TOKEN":!1}function V(s){let e=(4-s.length%4)%4,t=(s+"===".slice(0,e)).replace(/-/g,"+").replace(/_/g,"/"),r=atob(t),n=new ArrayBuffer(r.length),i=new Uint8Array(n);for(let o=0;o<r.length;o++)i[o]=r.charCodeAt(o);return n}function S(s){let e=s instanceof Uint8Array?s:new Uint8Array(s),t="";for(let r=0;r<e.length;r++)t+=String.fromCharCode(e[r]);return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function Je(s){return{challenge:V(s.challenge),rp:s.rp,user:{id:V(s.user.id),name:s.user.name,displayName:s.user.displayName},pubKeyCredParams:s.pubKeyCredParams,timeout:s.timeout,attestation:s.attestation,excludeCredentials:s.excludeCredentials?.map(e=>({id:V(e.id),type:"public-key",transports:e.transports})),authenticatorSelection:s.authenticatorSelection}}function Ze(s){return{challenge:V(s.challenge),timeout:s.timeout,rpId:s.rpId,allowCredentials:s.allowCredentials?.map(e=>({id:V(e.id),type:"public-key",transports:e.transports})),userVerification:s.userVerification}}function et(s){let e=s.response;return{id:s.id,rawId:S(s.rawId),type:s.type,response:{clientDataJSON:S(e.clientDataJSON),attestationObject:S(e.attestationObject),transports:typeof e.getTransports=="function"?e.getTransports():void 0},clientExtensionResults:s.getClientExtensionResults()}}function Ee(s){if(!s||typeof s!="object")return null;let e=s.tokens,t=e&&typeof e=="object"?e:s;return typeof t.accessToken!="string"||!t.accessToken||typeof t.refreshToken!="string"||!t.refreshToken||typeof t.expiresIn!="number"||t.expiresIn<=0?null:{accessToken:t.accessToken,refreshToken:t.refreshToken,expiresIn:t.expiresIn}}function tt(s){if(!s||typeof s!="object")return null;let e=s.user;if(!e||typeof e!="object")return null;let t=e.id;return typeof t!="string"||t.length===0?null:e}function rt(s){try{let e=s.split(".");if(e.length!==3)return null;let t=e[1].replace(/-/g,"+").replace(/_/g,"/"),r=atob(t+"===".slice(0,(4-t.length%4)%4));return JSON.parse(r)}catch{return null}}function st(s){let e=s.response;return{id:s.id,rawId:S(s.rawId),type:s.type,response:{clientDataJSON:S(e.clientDataJSON),authenticatorData:S(e.authenticatorData),signature:S(e.signature),userHandle:e.userHandle?S(e.userHandle):null},clientExtensionResults:s.getClientExtensionResults()}}function nt(s){return s===!1?{pageViews:!1,sessionStart:!1,appOpen:!1,appBackground:!1,engagement:!1}:s===!0||s===void 0?{pageViews:!0,sessionStart:!0,appOpen:!0,appBackground:!0,engagement:!0}:{pageViews:s.pageViews??!0,sessionStart:s.sessionStart??!0,appOpen:s.appOpen??!0,appBackground:s.appBackground??!0,engagement:s.engagement??!0}}var it=250,Te=360*60*1e3;function Ce(s,e){if(typeof window>"u")return()=>{};let t=nt(e.autoTrack),r=e.sessionIdleMs??18e5,n=[],i="sendora_session_id",o="sendora_session_last_activity";function a(){return Re(i)??""}function c(d){_e(i,d)}function l(){let d=Re(o);return d&&Number(d)||0}function g(d){_e(o,String(d))}function v(){return`sess_${Date.now().toString(36)}_${Math.random().toString(36).slice(2,10)}`}function w(d){let I=Date.now(),N=l();if(!a()||N&&I-N>r){let G=v();c(G),d&&t.sessionStart&&s.track("session.start",{sessionId:G})}g(I)}w(t.sessionStart),t.appOpen&&s.track("app.opened",{sessionId:a()||void 0,url:window.location.origin+window.location.pathname,referrer:document.referrer||void 0});let k=window.location.pathname,C=0,A=document.visibilityState==="visible"?Date.now():0;function re(){A>0&&(C+=Date.now()-A,A=0)}function z(){if(!t.engagement)return;re();let d=C;if(C=0,!(d<it)){d>Te&&(d=Te);try{s.track("app.engagement",{durationMs:Math.round(d),screen:k,sessionId:a()||void 0})}catch{}}}function Pe(d){k=d,C=0,A=document.visibilityState==="visible"?Date.now():0}if(t.pageViews||t.engagement){t.pageViews&&s.page();let d=()=>{z(),w(!0),t.pageViews&&s.page(),Pe(window.location.pathname)},I=()=>d();window.addEventListener("popstate",I),n.push(()=>window.removeEventListener("popstate",I));let N=window;if(!N.__sendoraHistoryPatched){N.__sendoraHistoryPatched=!0;let G=history.pushState,Le=history.replaceState;history.pushState=function(...ne){let ie=G.apply(this,ne);return window.dispatchEvent(new Event("sendora:locationchange")),ie},history.replaceState=function(...ne){let ie=Le.apply(this,ne);return window.dispatchEvent(new Event("sendora:locationchange")),ie}}let se=()=>d();window.addEventListener("sendora:locationchange",se),n.push(()=>window.removeEventListener("sendora:locationchange",se))}if(t.appBackground||t.engagement){let d=()=>{if(document.visibilityState==="hidden"){z(),t.appBackground&&(w(!0),s.track("app.backgrounded",{sessionId:a()||void 0}));try{s.flush()}catch{}}else document.visibilityState==="visible"&&(t.appBackground&&(w(!0),s.track("app.foregrounded",{sessionId:a()||void 0})),t.engagement&&A===0&&(A=Date.now()))};if(document.addEventListener("visibilitychange",d),n.push(()=>document.removeEventListener("visibilitychange",d)),t.engagement){let I=()=>{z();try{s.flush()}catch{}};window.addEventListener("pagehide",I),n.push(()=>window.removeEventListener("pagehide",I))}}let pe=0,P=()=>{let d=Date.now();d-pe<5e3||(pe=d,w(!0))};return window.addEventListener("click",P,{passive:!0}),window.addEventListener("keydown",P,{passive:!0}),window.addEventListener("scroll",P,{passive:!0}),n.push(()=>{window.removeEventListener("click",P),window.removeEventListener("keydown",P),window.removeEventListener("scroll",P)}),()=>{z();for(let d of n)try{d()}catch{}}}function Re(s){try{return window.sessionStorage?.getItem(s)??null}catch{return null}}function _e(s,e){try{window.sessionStorage?.setItem(s,e)}catch{}}var B=class s{constructor(e){this.http=e}static isSupported(){return typeof window>"u"?!1:"serviceWorker"in navigator&&"PushManager"in window&&"Notification"in window}static permission(){return typeof Notification>"u"?"denied":Notification.permission}async subscribe(e){if(!s.isSupported())throw new Error("[sendoracloud] Web Push not supported in this browser.");let t=Notification.permission;if(t==="default"&&(t=await Notification.requestPermission()),t!=="granted")throw new Error(`[sendoracloud] Notification permission ${t}.`);let r=await navigator.serviceWorker.getRegistration(e.scope??"/");r||(r=await navigator.serviceWorker.register(e.serviceWorkerPath,{scope:e.scope??"/"})),await navigator.serviceWorker.ready;let i=(await this.http.get("/push/vapid"))?.data?.publicKey;if(!i)throw new Error("[sendoracloud] backend returned no VAPID public key.");let a=await r.pushManager.getSubscription()??await r.pushManager.subscribe({userVisibleOnly:!0,applicationServerKey:ot(i)}),c=a.toJSON(),l=c.endpoint,g=c.keys?.p256dh??"",v=c.keys?.auth??"",w=await at(l),k=await this.http.post("/push/tokens",{platform:"web",token:w,webEndpoint:l,webP256dh:g,webAuth:v,userAgent:typeof navigator<"u"?navigator.userAgent:void 0,locale:typeof navigator<"u"?navigator.language:void 0,timezone:Intl?.DateTimeFormat?.().resolvedOptions?.().timeZone,...e.userId?{userId:e.userId}:{}});if(!k?.data?.tokenId)throw new Error("[sendoracloud] backend returned no tokenId on subscribe.");return{tokenId:k.data.tokenId,subscription:a}}async unsubscribe(e="/"){if(!s.isSupported())return!1;let t=await navigator.serviceWorker.getRegistration(e);if(!t)return!1;let r=await t.pushManager.getSubscription();return r?await r.unsubscribe():!1}async trackOpen(e,t){let r={sendId:e};t!==void 0&&(r.clickAction=t);let n=await this.http.post("/push/track-open",r);if(!n.success)throw new Error(n.error?.message??"push.trackOpen failed")}};function ot(s){let e="=".repeat((4-s.length%4)%4),t=(s+e).replace(/-/g,"+").replace(/_/g,"/"),r=atob(t),n=new Uint8Array(r.length);for(let i=0;i<r.length;i++)n[i]=r.charCodeAt(i);return n}async function at(s){let e=new TextEncoder().encode(s),t=await crypto.subtle.digest("SHA-256",e);return Array.from(new Uint8Array(t)).map(n=>n.toString(16).padStart(2,"0")).join("")}var q=class{constructor(e){}unsupported(){throw new Error("[SendoraCloud] sendora.audiences is not available in the browser SDK. Audiences require a secret (sk_*) key with the audiences:read/write scopes and must be managed from a trusted server, not the browser.")}list(){return this.unsupported()}get(e){return this.unsupported()}create(e){return this.unsupported()}update(e,t){return this.unsupported()}delete(e){return this.unsupported()}};var p=class extends Error{constructor(e,t,r){super(t),this.name="LinkError",this.code=e,this.statusCode=r}};function le(s,e){let t=(s??"").toUpperCase();return t==="NOT_FOUND"?new p("NOT_FOUND",e):t==="FORBIDDEN"||t==="UNAUTHORIZED"?new p("UNAUTHORIZED",e):t==="RATE_LIMITED"?new p("RATE_LIMITED",e):t==="ENTITLEMENT_ERROR"||/plan limit/i.test(e)?new p("PLAN_LIMIT",e):t==="VALIDATION_ERROR"?/iOS bundle|Android package/i.test(e)?new p("BUNDLE_MISMATCH",e):/2KB|10KB|linkData/i.test(e)?new p("DATA_TOO_LARGE",e):/fallbackUrl/i.test(e)&&/apps/i.test(e)?new p("FALLBACK_REQUIRED",e):new p("INVALID_INPUT",e):t==="PARSE_ERROR"||t==="HTTP_ERROR"||/HTTP \d{3}/.test(e)?new p("NETWORK",e):new p("UNKNOWN",e||`Unknown LinkError (code=${s??"none"})`)}var de=/^[a-z0-9-]{3,20}$/;function ct(s){try{let t=new URL(s).pathname.split("/").filter(Boolean);if(t.length===0)return null;let r=t[t.length-1];return de.test(r)?r:null}catch{return null}}var ut=5*6e4,lt=50,dt=5,j=class{constructor(e){this.http=e;this.prewarmCache=new Map;this.prewarmInflight=0}cacheKey(e,t){if(t)return`k:${t}`;let r={},n=e;for(let i of Object.keys(n).sort())r[i]=n[i];return`s:${JSON.stringify(r)}`}evictExpired(){let e=Date.now();for(let[t,r]of this.prewarmCache)e-r.ts>ut&&this.prewarmCache.delete(t);for(;this.prewarmCache.size>lt;){let t=this.prewarmCache.keys().next().value;if(t===void 0)break;this.prewarmCache.delete(t)}}prewarm(e,t){this.evictExpired();let r=this.cacheKey(e,t?.key);if(this.prewarmCache.has(r)||this.prewarmInflight>=dt)return;this.prewarmInflight++;let n=this.doCreate(e).catch(i=>{throw this.prewarmCache.delete(r),i}).finally(()=>{this.prewarmInflight--});this.prewarmCache.set(r,{promise:n,ts:Date.now()})}async create(e,t){if(!e.title)throw new p("INVALID_INPUT","links.create: title is required");this.evictExpired();let r=this.cacheKey(e,t?.key),n=this.prewarmCache.get(r);return n?(this.prewarmCache.delete(r),n.promise):this.doCreate(e)}async doCreate(e){let t={title:e.title};e.fallbackUrl&&(t.fallbackUrl=e.fallbackUrl),e.iosDeepLinkPath&&(t.iosDeepLinkPath=e.iosDeepLinkPath),e.androidDeepLinkPath&&(t.androidDeepLinkPath=e.androidDeepLinkPath),e.linkData&&(t.linkData=e.linkData),e.ogTitle&&(t.ogTitle=e.ogTitle),e.ogDescription&&(t.ogDescription=e.ogDescription),e.ogImageUrl&&(t.ogImageUrl=e.ogImageUrl),e.campaign&&(t.campaign=e.campaign),e.source&&(t.source=e.source),e.medium&&(t.medium=e.medium),e.channel&&(t.channel=e.channel),e.tags&&(t.tags=e.tags),e.expiresAt&&(t.expiresAt=e.expiresAt),e.iosBundleId&&(t.iosBundleId=e.iosBundleId),e.androidPackageName&&(t.androidPackageName=e.androidPackageName);let r=await this.http.post("/sdk/links",t);if(!r.success||!r.data)throw le(r.error?.code,r.error?.message??"links.create failed");return r.data}async revoke(e){if(!de.test(e))throw new p("INVALID_INPUT",`links.revoke: '${e}' is not a valid shortcode`);let t=await this.http.post(`/sdk/links/${encodeURIComponent(e)}/revoke`,{});if(!t.success||!t.data)throw le(t.error?.code,t.error?.message??"links.revoke failed");return{shortcode:t.data.shortcode,revoked:!0}}async getStats(e){if(!de.test(e))throw new p("INVALID_INPUT",`links.getStats: '${e}' is not a valid shortcode`);let t=await this.http.get(`/sdk/links/${encodeURIComponent(e)}/stats`);if(!t.success||!t.data)throw le(t.error?.code,t.error?.message??"links.getStats failed");return t.data}};var J=class{constructor(e){this.http=e}async reportInstall(e={}){let t=await this.http.post("/attribution/install",e);if(!t.success||!t.data)throw new Error(t.error?.message??"attribution.reportInstall failed");return t.data}async checkDeferred(e={}){let t=await this.http.post("/attribution/deferred",e);if(!t.success)throw new Error(t.error?.message??"attribution.checkDeferred failed");return t.data??null}};var Z=class{constructor(e){this.http=e}async createTicket(e){let t=await this.http.post("/support/tickets",e);if(!t.success||!t.data)throw new Error(t.error?.message??"support.createTicket failed");return t.data}async submitCsat(e){let t=await this.http.post("/support/csat",e);if(!t.success||!t.data)throw new Error(t.error?.message??"support.submitCsat failed");return t.data}};var ee=class{constructor(e){this.http=e}async search(e){if(!e.query||e.query.trim().length===0)return[];let t=new URLSearchParams({query:e.query});e.limit&&t.set("limit",String(e.limit));let r=await this.http.get(`/kb/search?${t.toString()}`);if(!r.success||!Array.isArray(r.data))throw new Error(r.error?.message??"kb.search failed");return r.data}};var te=class te{constructor(e){this.autoTrackCleanup=null;this.http=new Y(e),this.storage=new Q,this.consent=new K(e.defaultConsent===!0,this.http),this.analytics=new x(e,this.http,this.storage,this.consent),this.flags=new O(e,this.http,this.storage),this.surveys=new M(e,this.http,this.storage),this.messages=new H(e,this.http,this.storage),this.chatbot=new F(e,this.http,this.storage),this.auth=new $(e,this.http,this.storage,this.analytics,this.flags,this.chatbot),this.webPush=new B(this.http),this.audiences=new q(this.http),this.links=new j(this.http),this.attribution=new J(this.http),this.support=new Z(this.http),this.kb=new ee(this.http),e.autoTrack!==!1&&(this.autoTrackCleanup=Ce(this.analytics,e))}static init(e){return we(e.apiKey),new te(e)}static sanitizeHtml(e){return ue(e)}static safeUrl(e){return E(e)}track(e,t){this.analytics.track(e,t)}page(e){this.analytics.page(e)}identify(e,t,r){this.analytics.identify(e,t,r),this.flags.setContext({userId:e})}reset(){this.analytics.reset(),this.flags.reset(),this.chatbot.newSession()}async flush(){await this.analytics.flush()}destroy(){this.autoTrackCleanup&&(this.autoTrackCleanup(),this.autoTrackCleanup=null),this.analytics.destroy(),this.flags.destroy(),this.messages.destroy()}};te.version=b;var he=te,or=he;export{W as AlreadyIdentifiedError,x as Analytics,q as Audiences,$ as Auth,u as AuthError,F as Chatbot,K as Consent,U as CredentialInUseError,D as EmailAlreadyTakenError,O as FeatureFlags,H as InAppMessaging,p as LinkError,j as Links,he as SendoraCloud,M as Surveys,f as ValidationError,B as WebPush,or as default,ct as extractShortcodeFromUrl,E as safeUrl,ue as sanitizeHtml};
1
+ var ge=/^[a-zA-Z][a-zA-Z0-9._:-]{0,127}$/;var De=new Set(["__proto__","constructor","prototype"]),f=class extends Error{constructor(e){super(`[SendoraCloud] ${e}`),this.name="SendoraValidationError"}};function fe(s){if(typeof s!="string"||!ge.test(s))throw new f(`Invalid event name: must match ${ge}, got "${String(s)}".`)}function me(s){if(s==null)return;if(typeof s!="object"||Array.isArray(s))throw new f("Properties must be a plain object, not an array or primitive.");let e=ye(s);if(e>5)throw new f(`Properties nested too deeply (${e} levels, max 5).`);ve(s);let t;try{t=JSON.stringify(s).length}catch{throw new f("Properties contain a circular reference or unserializable value.")}if(t>32768)throw new f(`Properties too large (${t} bytes, max 32768).`)}function ye(s,e=0){if(e>6||s==null||typeof s!="object")return e;let t=e;for(let r of Object.keys(s)){let n=ye(s[r],e+1);n>t&&(t=n)}return t}function ve(s){if(!(s==null||typeof s!="object"))for(let e of Object.keys(s)){if(De.has(e))throw new f(`Forbidden property key "${e}" (prototype pollution guard).`);ve(s[e])}}var Ue=/^pk_[A-Za-z0-9_-]{16,}$/;function we(s){if(!s||typeof s!="string")throw new f("apiKey is required.");if(s.startsWith("sk_")||s.startsWith("sendora_secret_"))throw new f("Secret keys must NEVER be used in browser SDK. Use a publishable key (pk_...).");if(!Ue.test(s))throw new f("Invalid publishable key format. Expected pk_... (17+ chars).")}function ke(s){let e=s.replace(/\/$/,"");if(!(e.startsWith("http://localhost")||e.startsWith("http://127.0.0.1"))&&!e.startsWith("https://"))throw new f("apiUrl must be https:// (only http://localhost is allowed for dev).")}var Ae={name:"@sendoracloud/sdk-web",version:"3.17.0",description:"Sendora Cloud Web SDK \u2014 analytics, feature flags, surveys, in-app messaging, chatbot, auth, deep links (Branch / Firebase parity). Zero dependencies, TypeScript, SSR-safe.",type:"module",main:"./dist/index.cjs",module:"./dist/index.js",types:"./dist/index.d.ts",exports:{".":{types:"./dist/index.d.ts",import:"./dist/index.js",require:"./dist/index.cjs"}},files:["dist","README.md","LICENSE"],keywords:["sendora","analytics","feature-flags","surveys","in-app-messaging","chatbot","tracking","customer-engagement"],homepage:"https://sendoracloud.com/sdks",repository:{type:"git",url:"git+https://github.com/sendoracloud/sdk-web.git"},bugs:{url:"https://github.com/sendoracloud/sdk-web/issues"},author:"Sendora Cloud <hello@sendoracloud.com>",license:"Apache-2.0",publishConfig:{access:"public"},sideEffects:!1,scripts:{build:"tsup src/index.ts --format cjs,esm --dts --clean --minify",typecheck:"tsc --noEmit",prepublishOnly:"npm run build"},devDependencies:{typescript:"^5.4.0",tsup:"^8.0.0"}};var b=Ae.version;var xe="https://api.sendoracloud.com",Oe=1e4,Me="@sendoracloud/sdk-web",Y=class{constructor(e){let t=(e.apiUrl??xe).replace(/\/$/,"");ke(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,r){return this.request("POST",e,t,r)}patch(e,t,r){return this.request("PATCH",e,t,r)}delete(e,t){return this.request("DELETE",e,void 0,t)}async request(e,t,r,n){let i=`${this.baseUrl}/api/v1${t}`,o=typeof AbortController<"u"?new AbortController:null,a=o?setTimeout(()=>o.abort(),Oe):null;try{let c=await fetch(i,{method:e,headers:{"Content-Type":"application/json","X-API-Key":this.apiKey,"X-Sendora-SDK-Name":Me,"X-Sendora-SDK-Version":b,...n??{}},body:r?JSON.stringify(r):void 0,signal:o?o.signal:void 0});a&&clearTimeout(a);let l;try{l=await c.json()}catch{l={success:!1,error:{code:"PARSE_ERROR",message:`HTTP ${c.status}`}}}if(this.debug&&console.debug(`[SendoraCloud] ${e} ${t} \u2192 ${c.status}`),!c.ok){if(t.startsWith("/auth-service/")&&!this.debug)console.warn(`[SendoraCloud] ${e} ${t} \u2192 HTTP ${c.status}`);else{let v=l?.error?.code??"HTTP_ERROR",w=l?.error?.message??`HTTP ${c.status}`,k=l?.error?.details?.fieldErrors,C=k?` (${Object.entries(k).map(([A,re])=>`${A}: ${(re??[]).join(", ")}`).join("; ")})`:"";console.warn(`[SendoraCloud] ${e} ${t} \u2192 HTTP ${c.status} \u2014 ${v}: ${w}${C}`)}l&&l.error&&(l.error={...l.error,status:c.status})}return l}catch(c){a&&clearTimeout(a);let l=c instanceof Error&&c.name==="AbortError"?"timeout":"error";return console.warn(`[SendoraCloud] ${e} ${t} network ${l}`),{success:!1,error:{code:"NETWORK_ERROR",message:l}}}}};var X="sendora_",Q=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 r=X+e;if(this.useLocalStorage)try{localStorage.setItem(r,t);return}catch{}this.memoryStore.set(r,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 r=localStorage.length-1;r>=0;r--){let n=localStorage.key(r);n&&n.startsWith(t)&&localStorage.removeItem(n)}}catch{}for(let r of Array.from(this.memoryStore.keys()))r.startsWith(t)&&this.memoryStore.delete(r)}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 L(s=""){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 r="";for(let n of e)r+=n.toString(16).padStart(2,"0");return s+r}var Ie=500,oe=10,ae=6e4,x=class{constructor(e,t,r,n){this.queue=[];this.timer=null;this.userId=null;this.identityToken=null;this.consecutiveFailures=0;this.nextFlushAfter=0;this.breakerOpenAt=0;this.breakerWarned=!1;this.http=t,this.storage=r,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,this.appVersion=e.appVersion,this.storage.get("schema_version")===null&&this.storage.set("schema_version","1");let i=this.storage.get("anonymous_id");if(i?this.anonymousId=i:(this.anonymousId=L("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(e,t){fe(e),t&&me(t),this.queue.length>=Ie&&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,r){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=r?.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=L("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;this.consecutiveFailures>oe&&Date.now()-this.breakerOpenAt>=ae&&(this.consecutiveFailures=0,this.breakerOpenAt=0,this.breakerWarned=!1);let e=this.queue.splice(0,100),t=this.getSessionId(),r=e.map(a=>({projectId:this.projectId,module:"custom",eventType:a.eventType,userId:a.userId,anonymousId:a.anonymousId,identityToken:a.identityToken,timestamp:a.timestamp??new Date().toISOString(),properties:a.properties??{},context:this.getContext(),...t?{sessionId:t}:{},consent:["analytics"]}));if((await this.http.post("/events/batch",{events:r})).success){this.consecutiveFailures=0,this.breakerOpenAt=0,this.breakerWarned=!1;return}if(this.consecutiveFailures++,this.consecutiveFailures>oe){this.breakerWarned||(this.breakerOpenAt=Date.now(),this.breakerWarned=!0,console.warn(`[SendoraCloud] Analytics circuit breaker open after ${oe} consecutive failed flushes \u2014 events will be dropped until a flush succeeds or ${ae}ms elapse.`));return}let i=Math.min(ae,1e3*2**this.consecutiveFailures);this.nextFlushAfter=Date.now()+i;let o=Math.max(0,Ie-this.queue.length);o>0&&this.queue.unshift(...e.slice(0,o))}destroy(){this.timer&&clearInterval(this.timer),this.timer=null,this.flush()}getSessionId(){try{return typeof sessionStorage>"u"?void 0:sessionStorage.getItem("sendora_session_id")||void 0}catch{return}}getContext(){return typeof window>"u"?{sdk:{name:"@sendoracloud/sdk-web",version:b}}:{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,device:this.buildDevice(),sdk:{name:"@sendoracloud/sdk-web",version:b}}}buildDevice(){if(typeof navigator>"u")return;let e=navigator.userAgent||"",r=navigator.userAgentData?.mobile??/Mobi|Android|iPhone|iPod/i.test(e);return{type:/iPad|Tablet|(?:Android(?!.*Mobile))/i.test(e)?"tablet":r?"mobile":"desktop",os:"Web",osVersion:"",model:"",appVersion:this.resolveAppVersion(),browser:He(e)}}resolveAppVersion(){if(this.appVersion)return this.appVersion;if(typeof document<"u"){let r=document.querySelector('meta[name="sendora:app-version"]')?.getAttribute("content");if(r)return r}let e=globalThis.__SENDORA_APP_VERSION__;return typeof e=="string"?e:""}};function He(s){let e=[["Edge",/Edg\/(\d+)/],["Opera",/OPR\/(\d+)/],["Firefox",/Firefox\/(\d+)/],["Samsung Internet",/SamsungBrowser\/(\d+)/],["Chrome",/Chrome\/(\d+)/],["Safari",/Version\/(\d+)[^ ]* Safari/]];for(let[t,r]of e){let n=r.exec(s);if(n)return`${t} ${n[1]}`}return"Unknown"}var O=class{constructor(e,t,r){this.cache=new Map;this.refreshInterval=null;this.http=t,this.storage=r;let n=this.storage.get("flags");if(n)try{let i=JSON.parse(n);if(Array.isArray(i))for(let o of i)ce(o)&&this.cache.set(o.key,o)}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 r=await this.http.post("/feature-flags/evaluate",{key:e,userId:this.userId,entityId:this.entityId,environment:this.environment});return r.success&&r.data&&ce(r.data)?(this.cache.set(e,r.data),this.persistCache(),r.data.value):null}async isEnabled(e,t=!1){let r=await this.getFlag(e);return typeof r=="boolean"?r: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)ce(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,r]of this.cache)e[t]=r.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 ce(s){if(s==null||typeof s!="object")return!1;let e=s;return Object.prototype.hasOwnProperty.call(e,"__proto__")||Object.prototype.hasOwnProperty.call(e,"constructor")?!1:typeof e.key=="string"&&"value"in e}var Fe=32*1024,M=class{constructor(e,t,r){this.activeSurvey=null;this.http=t,this.storage=r}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 r=await this.http.get(`/surveys/${encodeURIComponent(e)}/config`);return!r.success||!r.data||!Ke(r.data)?null:(this.activeSurvey=r.data,this.storage.set(t,"1"),this.onSurveyDisplay&&this.onSurveyDisplay(r.data),r.data)}async submitResponse(e,t,r=!0){let n=0;try{n=JSON.stringify(t).length}catch{return!1}if(n>Fe)return!1;let i=await this.http.post("/surveys/responses",{surveyId:e,answers:t,completed:r});return i.success&&(this.activeSurvey=null),i.success}dismiss(){this.activeSurvey=null}getActiveSurvey(){return this.activeSurvey}};function Ke(s){if(s==null||typeof s!="object")return!1;let e=s;return typeof e.id!="string"||typeof e.name!="string"||!Array.isArray(e.questions)?!1:e.questions.every(Ve)}function Ve(s){if(s==null||typeof s!="object")return!1;let e=s;return typeof e.id=="string"&&typeof e.text=="string"}function E(s){if(!(!s||typeof s!="string"))try{let e=typeof window<"u"?window.location.href:"https://local/",t=new URL(s,e);return t.protocol==="http:"||t.protocol==="https:"?t.href:void 0}catch{return}}var We=new Set(["b","i","u","em","strong","a","p","br","span","ul","ol","li","h1","h2","h3","h4"]),$e={a:new Set(["href","target","rel"])};function ue(s){if(typeof DOMParser>"u")return"";let t=new DOMParser().parseFromString(`<div>${s}</div>`,"text/html").body.firstElementChild;return t?(Se(t),t.innerHTML):""}function Se(s){let e=Array.from(s.children);for(let t of e){let r=t.tagName.toLowerCase();if(!We.has(r)){let i=document.createTextNode(t.textContent??"");t.replaceWith(i);continue}let n=$e[r]??new Set;for(let i of Array.from(t.attributes)){if(!n.has(i.name.toLowerCase())){t.removeAttribute(i.name);continue}if(i.name.toLowerCase()==="href"){let o=E(i.value);o?t.setAttribute("href",o):t.removeAttribute(i.name)}}r==="a"&&(t.setAttribute("rel","noopener noreferrer"),t.setAttribute("target","_blank")),Se(t)}}var H=class{constructor(e,t,r){this.messages=[];this.pollInterval=null;this.http=t,this.storage=r}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(Be);for(let t of this.messages){let r=`iam_shown_${t.id}`;if(!this.storage.get(r))return this.storage.set(r,"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 Be(s){return{...s,content:{...s.content,ctaUrl:E(s.content.ctaUrl),imageUrl:E(s.content.imageUrl)}}}var qe=4e3,F=class{constructor(e,t,r){this.chatbotId=null;this.messages=[];this.http=t,this.storage=r;let n=this.storage.get("chat_session_id");this.sessionId=n??L("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>qe)return null;let r={role:"user",content:e,timestamp:new Date().toISOString()};this.messages.push(r);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 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=L("chat_"),this.storage.set("chat_session_id",this.sessionId)}getSessionId(){return this.sessionId}};var K=class{constructor(e,t){this.listeners=new Set;this.granted=e,this.http=t}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)}async record(e){if(!this.http)throw new Error("consent.record requires SDK init (HttpClient unavailable)");let t=await this.http.post("/consent/record",e);if(!t.success||!t.data)throw new Error(t.error?.message??"consent.record failed");return t.data}async requestDeletion(e){if(!this.http)throw new Error("consent.requestDeletion requires SDK init (HttpClient unavailable)");let t=await this.http.post("/consent/deletion-request",e);if(!t.success||!t.data)throw new Error(t.error?.message??"consent.requestDeletion failed");return t.data}emit(){for(let e of this.listeners)try{e(this.granted)}catch{}}};var T="auth_access_token",R="auth_access_expires",m="auth_refresh_token",_="auth_user",je=3e4,ze=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function y(s){if(!s||typeof s!="object")return!1;let e=s,t=e.user,r=e.tokens;return!(!t||typeof t.id!="string"||t.id.length===0||!r||typeof r.accessToken!="string"||r.accessToken.length===0||typeof r.refreshToken!="string"||r.refreshToken.length===0||typeof r.expiresIn!="number"||r.expiresIn<=0||t.isAnonymous!==void 0&&typeof t.isAnonymous!="boolean"||t.email!==void 0&&t.email!==null&&typeof t.email!="string"||t.emailVerified!==void 0&&typeof t.emailVerified!="boolean"||t.name!==void 0&&t.name!==null&&typeof t.name!="string"||t.signupMethod!==void 0&&t.signupMethod!==null&&typeof t.signupMethod!="string"||t.lastLoginMethod!==void 0&&t.lastLoginMethod!==null&&typeof t.lastLoginMethod!="string"||e.retiredAnonUserId!==void 0&&typeof e.retiredAnonUserId!="string")}var D=class extends Error{constructor(e="An account with this email already exists"){super(e),this.name="EmailAlreadyTakenError"}},W=class extends Error{constructor(e="Already signed in with an identity; use link*() to add a credential, or sign out first"){super(e),this.name="AlreadyIdentifiedError"}},U=class extends Error{constructor(t="This credential is already linked to a different account",r){super(t);this.code="CREDENTIAL_IN_USE";this.kind="credential_in_use";this.retryable=!1;this.name="CredentialInUseError",this.provider=r?.provider,this.collision=r?.collision,this.status=r?.status}};function be(s){let e=s?.error?.details;if(!e||typeof e!="object")return{};let t=e;return{provider:typeof t.provider=="string"?t.provider:void 0,collision:t.collision==="identity"||t.collision==="email"?t.collision:void 0}}var Ge=new Set(["network","server","rate_limited","account_locked"]);function Ye(s,e){switch(s){case"NETWORK_ERROR":case"NETWORK_TIMEOUT":return"network";case"PARSE_ERROR":return"server";case"RATE_LIMITED":case"RATE_LIMIT":case"RATE_LIMIT_EXCEEDED":case"QUOTA_EXCEEDED":return"rate_limited";case"ACCOUNT_LOCKED":return"account_locked";case"CREDENTIAL_IN_USE":return"credential_in_use";case"NOT_ANONYMOUS":case"FORBIDDEN_NON_ANONYMOUS":return"already_identified";case"PASSKEY_USER_CANCELLED":case"GAME_CENTER_CANCELLED":case"PLAY_GAMES_CANCELLED":case"SSO_CANCELLED":return"cancelled";case"GAME_CENTER_UNAVAILABLE":case"PLAY_GAMES_UNAVAILABLE":case"ENTITLEMENT_ERROR":case"tier_required":return"config";case"UNAUTHORIZED":case"INVALID_CREDENTIALS":case"INVALID_REFRESH_TOKEN":case"EMAIL_OTP_INVALID":case"MAGIC_LINK_INVALID":case"PASSKEY_RP_MISMATCH":case"NOT_SIGNED_IN":return"invalid_credential";default:break}if(typeof e=="number"){if(e===429)return"rate_limited";if(e>=500)return"server";if(e>=400)return"invalid_credential"}return"unknown"}var u=class extends Error{constructor(e,t,r){super(t),this.name="AuthError",this.code=e,this.kind=r?.kind??Ye(e,r?.status),this.retryable=Ge.has(this.kind),this.retryAfterSeconds=r?.retryAfterSeconds,this.status=r?.status}};function Xe(s){if(s instanceof u||s instanceof D||s instanceof W||s instanceof U)return s;let e=s?.name;return e==="AbortError"||e==="TimeoutError"||e==="NetworkTimeoutError"?new u("NETWORK_TIMEOUT",s instanceof Error?s.message:"timeout",{kind:"network"}):new u("UNKNOWN_ERROR",s instanceof Error?s.message:String(s),{kind:"unknown"})}function h(s){let e=s.error?.status,t=s.error?.details?.retryAfterSeconds;return{status:typeof e=="number"?e:void 0,retryAfterSeconds:typeof t=="number"&&Number.isFinite(t)&&t>0?t:void 0}}var $=class{constructor(e,t,r,n,i,o){this.user=null;this.accessToken=null;this.accessExpiresAt=0;this.inflight=Promise.resolve();this.refreshInflight=null;this.refreshFailureCount=0;this.refreshCooldownUntil=0;this.visibilityResetUnsub=null;this.proactiveRefreshTimer=null;this.proactiveRefreshCleanup=null;this.takeoverListeners=new Set;this.lastTakeover=null;this.deletionCancelledListeners=new Set;this.lastDeletionCancelled=null;this.lastAnonRetirement=null;this.authStateListeners=new Set;this.pendingAnonTakeover=null;this.hydrated=!1;this.http=t,this.storage=r,this.analytics=n,this.flags=i,this.chatbot=o;let a=this.storage.get(_),c=this.storage.get(T),l=this.storage.get(m),g=this.storage.get(R);if(a&&(c||l))try{let v=JSON.parse(a);if(typeof v.id!="string"||v.id.length===0)throw new Error("invalid cache");this.user=v,c?(this.accessToken=c,this.accessExpiresAt=g?Number(g):0):(this.accessToken=null,this.accessExpiresAt=0),this.analytics.identify(this.user.id)}catch{this.storage.remove(_),this.storage.remove(T),this.storage.remove(R)}this.hydrated=!0}getCurrentUser(){return this.user}onDeviceTakeover(e){return this.takeoverListeners.add(e),()=>{this.takeoverListeners.delete(e)}}getLastDeviceTakeover(){return this.lastTakeover}onAuthStateChanged(e){if(this.authStateListeners.add(e),this.hydrated&&this.user){let t=this.user;try{e({event:"signed_in",user:t})}catch{}}return()=>{this.authStateListeners.delete(e)}}emitAuthState(e){for(let t of this.authStateListeners)try{t(e)}catch{}}fireDeviceTakeover(e,t){if(!e||!ze.test(e))return;let r={retiredAnonUserId:e,identifiedUserId:t,at:new Date};this.lastTakeover=r;for(let n of this.takeoverListeners)try{n(r)}catch{}this.emitAuthState({event:"device_takeover",user:this.user,retiredAnonUserId:e})}onDeletionCancelled(e){return this.deletionCancelledListeners.add(e),()=>{this.deletionCancelledListeners.delete(e)}}getLastDeletionCancelled(){return this.lastDeletionCancelled}getLastAnonRetirement(){return this.lastAnonRetirement}fireDeletionCancelled(e){let t={userId:e,at:new Date};this.lastDeletionCancelled=t;for(let r of this.deletionCancelledListeners)try{r(t)}catch{}this.emitAuthState({event:"deletion_cancelled",user:this.user})}takeoverHint(){if(this.user?.isAnonymous)return this.storage.get(m)??void 0}async getAccessToken(){return this.accessToken&&this.accessExpiresAt&&Date.now()<this.accessExpiresAt-je?this.accessToken:this.storage.get(m)?this.refreshInflight?this.refreshInflight:Date.now()<this.refreshCooldownUntil?null:this.refreshAccessToken():null}getAccessTokenSync(){return this.accessToken}signInAnonymously(e){return this.serialize(async()=>{let t=this.user;if(!e?.forceNew&&t?.isAnonymous&&this.storage.get(m))return t;let r=await this.http.post("/auth-service/anonymous",{name:e?.name,metadata:e?.metadata});if(!r.success||!y(r.data))throw new u(r.error?.code??"AUTH_ERROR",r.error?.message??"Anonymous signup failed",h(r));return this.persist(r.data),r.data.user})}signUp(e,t,r){return this.serialize(async()=>{let n=this.storage.get(m);if(n){let o=await this.http.post("/auth-service/upgrade",{refreshToken:n,email:e,password:t,name:r?.name});if(o.success&&y(o.data))return this.persist(o.data),o.data.user;let a=o.error?.code??"",c=h(o),l=c.status;if(a==="NOT_ANONYMOUS"||a==="FORBIDDEN_NON_ANONYMOUS")throw new W(o.error?.message);if(a==="CONFLICT"||a==="EMAIL_ALREADY_TAKEN"||l===409)throw new D(o.error?.message);if(a==="INVALID_REFRESH"||a==="UNAUTHORIZED"||a==="REFRESH_EXPIRED")await this.analytics.flush(),this.wipeLocalIdentity("session_expired");else throw new u(a||"AUTH_ERROR",o.error?.message??"Upgrade failed",c)}let i=await this.http.post("/auth-service/signup",{email:e,password:t,name:r?.name,metadata:r?.metadata});if(!i.success||!y(i.data)){let o=i.error?.code??"";throw o==="CONFLICT"?new D(i.error?.message):new u(o||"AUTH_ERROR",i.error?.message??"Signup failed",h(i))}return this.persist(i.data),i.data.user})}signIn(e,t){return this.serialize(async()=>{let r=this.takeoverHint(),n=await this.http.post("/auth-service/login",{email:e,password:t,...r?{prevAnonRefreshToken:r}:{}});if(!n.success||!n.data)throw new u(n.error?.code??"AUTH_ERROR",n.error?.message??"Login failed",h(n));let i=n.data;if(i.mfaRequired===!0&&typeof i.mfaChallengeToken=="string"){r&&(this.pendingAnonTakeover={challengeToken:i.mfaChallengeToken,prevAnonRefreshToken:r});let o=i.user;return{mfaRequired:!0,mfaChallengeToken:i.mfaChallengeToken,userId:o?.id??""}}if(!y(n.data))throw new u(n.error?.code??"AUTH_ERROR",n.error?.message??"Login failed",h(n));return await this.replaceLocalIdentity(),this.persist(n.data),n.data.user})}loginSocial(e){return this.serialize(async()=>{let t=this.takeoverHint(),{link:r,onCredentialInUse:n,...i}=e,o=await this.http.post("/auth-service/login/social",{...i,...t?{prevAnonRefreshToken:t}:{},...r?{linkAnonymous:!0}:{},...n?{onCredentialInUse:n}:{}});if(!o.success||!y(o.data))throw o.error?.code==="CREDENTIAL_IN_USE"?new U(o.error.message,{...be(o),status:h(o).status}):new u(o.error?.code??"SOCIAL_LOGIN_FAILED",o.error?.message??"Social login failed",h(o));return await this.replaceLocalIdentity(),this.persist(o.data),o.data.user})}signInWithGoogle(e,t,r){return this.loginSocial({provider:"google",code:e,redirectUri:t,link:r})}signInWithGitHub(e,t){return this.loginSocial({provider:"github",code:e,redirectUri:t})}signInWithApple(e){return this.loginSocial({provider:"apple",...e})}signInWithMicrosoft(e,t){return this.loginSocial({provider:"microsoft",code:e,redirectUri:t})}signInWithLinkedIn(e,t){return this.loginSocial({provider:"linkedin",code:e,redirectUri:t})}signInWithFacebook(e,t){return this.loginSocial({provider:"facebook",code:e,redirectUri:t})}linkEmailPassword(e,t){return this.linkCredential("/auth-service/me/link/email",{email:e,password:t})}linkSocial(e){return this.linkCredential("/auth-service/me/link/social",e)}linkGoogle(e){return this.linkSocial({provider:"google",...e})}linkApple(e){return this.linkSocial({provider:"apple",...e})}async linkCredential(e,t){let r=await this.getAccessToken();if(!r)throw new u("NOT_SIGNED_IN","Sign in before linking a credential");let n=await this.http.post(e,t,{Authorization:`Bearer ${r}`});if(!n.success||!n.data?.user){let i=n.error?.code??"LINK_FAILED";throw i==="CREDENTIAL_IN_USE"?new U(n.error?.message,{...be(n),status:h(n).status}):new u(i,n.error?.message??"Failed to link credential",h(n))}return n.data.upgraded&&y(n.data)?(this.persist(n.data),n.data.user):(this.updateLocalUser(n.data.user),n.data.user)}updateLocalUser(e){this.user=e,this.storage.set(_,JSON.stringify(e))}async listLinkedIdentities(){let e=await this.getAccessToken();if(!e)throw new u("NOT_SIGNED_IN","Sign in before listing linked identities");let t=await this.http.get("/auth-service/me/identities",{Authorization:`Bearer ${e}`});if(!t.success||!t.data)throw new u(t.error?.code??"IDENTITIES_FAILED",t.error?.message??"Failed to list linked identities",h(t));return{identities:t.data.identities??[],hasPassword:!!t.data.hasPassword}}signInWithDiscord(e,t){return this.loginSocial({provider:"discord",code:e,redirectUri:t})}challengeMfa(e,t){return this.serialize(async()=>{let n=(this.pendingAnonTakeover?.challengeToken===e?this.pendingAnonTakeover.prevAnonRefreshToken:void 0)??this.takeoverHint(),i=await this.http.post("/auth-service/mfa/challenge",{challengeToken:e,code:t,...n?{prevAnonRefreshToken:n}:{}});if(!i.success||!y(i.data))throw new u(i.error?.code??"MFA_CHALLENGE_FAILED",i.error?.message??"Invalid MFA code",h(i));return await this.replaceLocalIdentity(),this.persist(i.data),i.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()=>{let r=this.takeoverHint(),n=await this.http.post("/auth-service/email-otp/verify",{email:e,code:t,...r?{prevAnonRefreshToken:r}:{}});if(!n.success||!y(n.data))throw new u(n.error?.code??"EMAIL_OTP_INVALID",n.error?.message??"Wrong or expired code",h(n));return await this.replaceLocalIdentity(),this.persist(n.data),n.data.user})}async requestPasswordReset(e){let t=await this.http.post("/auth-service/password/forgot",{email:e});if(!t.success)throw new u(t.error?.code??"PASSWORD_RESET_REQUEST_FAILED",t.error?.message??"Failed to send reset email")}async resetPassword(e,t){let r=await this.http.post("/auth-service/password/reset",{token:e,newPassword:t});if(!r.success)throw new u(r.error?.code??"PASSWORD_RESET_FAILED",r.error?.message??"Reset failed")}async verifyEmail(e){let t=await this.http.post("/auth-service/email/verify",{token:e});if(!t.success)throw new u(t.error?.code??"EMAIL_VERIFICATION_FAILED",t.error?.message??"Verification failed")}async sendVerificationEmail(){let e=await this.getAccessToken();if(!e)throw new u("UNAUTHORIZED","Sign in before requesting a verification email");let t=await this.http.post("/auth-service/email/verify/resend",{},{Authorization:`Bearer ${e}`});if(!t.success)throw new u(t.error?.code??"VERIFICATION_RESEND_FAILED",t.error?.message??"Failed to send verification email")}verifyMagicLink(e){return this.serialize(async()=>{let t=this.takeoverHint(),r=await this.http.post("/auth-service/magic-link/verify",{token:e,...t?{prevAnonRefreshToken:t}:{}});if(!r.success||!y(r.data))throw new u(r.error?.code??"MAGIC_LINK_INVALID",r.error?.message??"Invalid or expired magic link",h(r));return await this.replaceLocalIdentity(),this.persist(r.data),r.data.user})}async enrollMfa(){let e=await this.http.post("/auth-service/mfa/enroll/start",{},await 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},await this.bearerHeaders());return!!(t.success&&t.data?.confirmed)}async disableMfa(){await this.http.post("/auth-service/mfa/disable",{},await 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",{},await this.bearerHeaders());if(!t.success||!t.data)throw new u(t.error?.code??"PASSKEY_START_FAILED",t.error?.message??"Passkey start failed");let r=Je(t.data),n;try{let a=await navigator.credentials.create({publicKey:r});if(!a)throw new Error("create returned null");n=a}catch(a){throw new u("PASSKEY_USER_CANCELLED",a instanceof Error?a.message:"User cancelled passkey registration")}let i=et(n),o=await this.http.post("/auth-service/passkeys/register/finish",{response:i,name:e},await this.bearerHeaders());if(!o.success||!o.data)throw new u(o.error?.code??"PASSKEY_FINISH_FAILED",o.error?.message??"Passkey verification failed");return o.data}signInWithPasskey(e){return this.serialize(async()=>{if(typeof navigator>"u"||!navigator.credentials)throw new u("PASSKEY_UNSUPPORTED","WebAuthn not supported in this environment");let t=this.takeoverHint(),r=await this.http.post("/auth-service/passkeys/authenticate/start",{userId:e??null});if(!r.success||!r.data)throw new u(r.error?.code??"PASSKEY_START_FAILED",r.error?.message??"Passkey start failed",h(r));let n=Ze(r.data),i;try{let c=await navigator.credentials.get({publicKey:n});if(!c)throw new Error("get returned null");i=c}catch(c){throw new u("PASSKEY_USER_CANCELLED",c instanceof Error?c.message:"User cancelled passkey signin")}let o=st(i),a=await this.http.post("/auth-service/passkeys/authenticate/finish",{response:o,userId:e??null,...t?{prevAnonRefreshToken:t}:{}});if(!a.success||!y(a.data))throw new u(a.error?.code??"PASSKEY_AUTH_FAILED",a.error?.message??"Passkey signin failed",h(a));return await this.replaceLocalIdentity(),this.persist(a.data),a.data.user})}async listPasskeys(){let e=await this.http.get("/auth-service/passkeys",await this.bearerHeaders());return!e.success||!Array.isArray(e.data)?[]:e.data}async deletePasskey(e){await this.http.delete(`/auth-service/passkeys/${e}`,await this.bearerHeaders())}async listMySessions(){let e=await this.http.get("/auth-service/sessions/me",await this.bearerHeaders());return!e.success||!Array.isArray(e.data)?[]:e.data}async revokeSession(e){await this.http.delete(`/auth-service/sessions/me/${e}`,await this.bearerHeaders())}async revokeAllSessions(){await this.http.delete("/auth-service/sessions/me",await this.bearerHeaders())}async startSso(e){let t=this.takeoverHint(),r=await this.http.post("/auth-service/sso/oidc/start",{returnTo:e,...t?{prevAnonRefreshToken:t}:{}});if(!r.success||!r.data)throw new u(r.error?.code??"SSO_START_FAILED",r.error?.message??"SSO start failed");return r.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 r=await this.startSso(t);typeof window<"u"&&(window.location.href=r.authorizationUrl)}async startSaml(e){let t=this.takeoverHint(),r=await this.http.post("/auth-service/sso/saml/start",{returnTo:e,...t?{prevAnonRefreshToken:t}:{}});if(!r.success||!r.data)throw new u(r.error?.code??"SAML_START_FAILED",r.error?.message??"SAML start failed");return r.data}async signInWithSaml(e){let t=e??(typeof window<"u"?window.location.origin:"");if(!t)throw new u("SAML_NO_RETURN_TO","returnTo required outside browser context");let r=await this.startSaml(t);typeof window<"u"&&(window.location.href=r.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),r=t.get("sendora_oidc_error")??t.get("sendora_saml_error");if(r)throw window.history.replaceState(null,"",window.location.pathname+window.location.search),new u("SSO_FAILED",decodeURIComponent(r));let n=t.get("sendora_oidc_token")??t.get("sendora_saml_token");if(!n)return null;let i=t.get("sendora_retired_anon");window.history.replaceState(null,"",window.location.pathname+window.location.search);let o=await this.http.post("/auth-service/token/refresh",{refreshToken:n}),a=o.success?Ee(o.data):null;if(!a)throw new u(o.error?.code??"SSO_EXCHANGE_FAILED",o.error?.message??"Could not exchange SSO token",h(o));let c={...a,tokenType:"Bearer"};await this.replaceLocalIdentity(),this.accessToken=c.accessToken,this.accessExpiresAt=Date.now()+c.expiresIn*1e3,this.storage.set(T,c.accessToken),this.storage.set(R,String(this.accessExpiresAt)),this.storage.set(m,c.refreshToken);let l=rt(c.accessToken),g={id:l?.sub??"",email:l?.email??null,emailVerified:l?.email_verified??!0,name:l?.name??null,isAnonymous:!1};return this.user=g,this.storage.set(_,JSON.stringify(g)),this.analytics.identify(g.id),this.flags.setContext({userId:g.id}),this.emitAuthState({event:"signed_in",user:g}),i&&this.fireDeviceTakeover(i,g.id),g})}async bearerHeaders(){let e=await this.getAccessToken();if(!e)throw new u("NOT_SIGNED_IN","Not signed in");return{Authorization:`Bearer ${e}`}}signOut(){return this.serialize(async()=>{let e=this.storage.get(m);this.wipeLocalIdentity("user"),e&&this.http.post("/auth-service/token/revoke",{refreshToken:e}).catch(()=>{})})}serialize(e){let t=async()=>{try{return await e()}catch(n){throw Xe(n)}},r=this.inflight.then(t,t);return this.inflight=r.catch(()=>{}),r}async refreshAccessToken(){if(this.refreshInflight)return this.refreshInflight;let e=this.storage.get(m);if(!e)return null;let t=typeof navigator<"u"&&"locks"in navigator,r=async()=>{let n=this.storage.get(m);if(n&&n!==e){let i=this.storage.get(T),o=this.storage.get(R);if(i&&o){let a=Number(o);if(Number.isFinite(a)&&a>Date.now())return this.accessToken=i,this.accessExpiresAt=a,i}}try{let i=n??e,o=await this.http.post("/auth-service/token/refresh",{refreshToken:i});if(!o.success)return Qe(o.error?.code)&&this.tokenStillCurrent(i)?(this.wipeLocalIdentity("session_expired"),this.refreshFailureCount=0,this.refreshCooldownUntil=0,null):(this.armBackoff(),null);let a=Ee(o.data);if(!a)return this.armBackoff(),null;if(!this.tokenStillCurrent(i))return null;if(this.accessToken=a.accessToken,this.accessExpiresAt=Date.now()+a.expiresIn*1e3,this.storage.set(T,a.accessToken),this.storage.set(R,String(this.accessExpiresAt)),this.storage.set(m,a.refreshToken),this.user===null){let c=tt(o.data);c&&(this.user=c,this.storage.set(_,JSON.stringify(c)),this.analytics.identify(c.id),this.emitAuthState({event:"signed_in",user:c}))}return this.refreshFailureCount=0,this.refreshCooldownUntil=0,a.accessToken}catch{return this.armBackoff(),null}};return this.refreshInflight=(async()=>{try{return t?await navigator.locks.request("sendora-authservice-refresh",{mode:"exclusive"},r):await r()}finally{this.refreshInflight=null}})(),this.refreshInflight}armBackoff(){this.refreshFailureCount+=1;let e=Math.min(3e4,1e3*Math.pow(2,this.refreshFailureCount-1));this.refreshCooldownUntil=Date.now()+e}resetRefreshState(){this.refreshFailureCount=0,this.refreshCooldownUntil=0}startVisibilityRefreshReset(){if(this.visibilityResetUnsub||typeof document>"u")return;let e=()=>{document.visibilityState==="visible"&&this.resetRefreshState()};document.addEventListener("visibilitychange",e),this.visibilityResetUnsub=()=>document.removeEventListener("visibilitychange",e)}stopVisibilityRefreshReset(){this.visibilityResetUnsub&&(this.visibilityResetUnsub(),this.visibilityResetUnsub=null)}startProactiveRefreshCron(){if(typeof window>"u"||this.proactiveRefreshTimer)return;let e=.8,t=6e4,r=3e4,n=async()=>{if(!this.accessToken||!this.accessExpiresAt)return;let o=Date.now(),a=this.accessExpiresAt-o;if(a<=0)return;let l=Math.max(a,5*6e4)*(1-e)+(Math.random()*r*2-r);a<=l&&await this.refreshAccessToken()};this.proactiveRefreshTimer=window.setInterval(()=>{n()},t);let i=()=>{document.visibilityState==="visible"&&n()};document.addEventListener("visibilitychange",i),this.proactiveRefreshCleanup=()=>{this.proactiveRefreshTimer&&(window.clearInterval(this.proactiveRefreshTimer),this.proactiveRefreshTimer=null),document.removeEventListener("visibilitychange",i)}}stopProactiveRefreshCron(){this.proactiveRefreshCleanup&&(this.proactiveRefreshCleanup(),this.proactiveRefreshCleanup=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(T,e.tokens.accessToken),this.storage.set(R,String(this.accessExpiresAt)),this.storage.set(m,e.tokens.refreshToken),this.analytics.identify(e.user.id),this.flags.setContext({userId:e.user.id}),this.startProactiveRefreshCron(),this.startVisibilityRefreshReset(),this.emitAuthState({event:"signed_in",user:e.user}),e.retiredAnonUserId&&this.fireDeviceTakeover(e.retiredAnonUserId,e.user.id),e.anonRetirement&&(this.lastAnonRetirement=e.anonRetirement),e.reactivatedFromDeletion&&this.fireDeletionCancelled(e.user.id)}async replaceLocalIdentity(){this.user!==null&&(await this.analytics.flush(),this.wipeLocalIdentity("replaced"))}tokenStillCurrent(e){return this.storage.get(m)===e}wipeLocalIdentity(e="replaced"){this.user=null,this.accessToken=null,this.accessExpiresAt=0,this.lastTakeover=null,this.lastDeletionCancelled=null,this.lastAnonRetirement=null,this.pendingAnonTakeover=null,this.storage.remove(_),this.storage.remove(T),this.storage.remove(R),this.storage.remove(m),this.analytics.reset(),this.analytics.dropQueue(),this.flags.reset(),this.chatbot.newSession(),this.stopProactiveRefreshCron(),this.stopVisibilityRefreshReset(),this.refreshFailureCount=0,this.refreshCooldownUntil=0,e!=="replaced"&&this.emitAuthState({event:"signed_out",user:null,reason:e})}};function Qe(s){return s?s==="INVALID_REFRESH_TOKEN":!1}function V(s){let e=(4-s.length%4)%4,t=(s+"===".slice(0,e)).replace(/-/g,"+").replace(/_/g,"/"),r=atob(t),n=new ArrayBuffer(r.length),i=new Uint8Array(n);for(let o=0;o<r.length;o++)i[o]=r.charCodeAt(o);return n}function S(s){let e=s instanceof Uint8Array?s:new Uint8Array(s),t="";for(let r=0;r<e.length;r++)t+=String.fromCharCode(e[r]);return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function Je(s){return{challenge:V(s.challenge),rp:s.rp,user:{id:V(s.user.id),name:s.user.name,displayName:s.user.displayName},pubKeyCredParams:s.pubKeyCredParams,timeout:s.timeout,attestation:s.attestation,excludeCredentials:s.excludeCredentials?.map(e=>({id:V(e.id),type:"public-key",transports:e.transports})),authenticatorSelection:s.authenticatorSelection}}function Ze(s){return{challenge:V(s.challenge),timeout:s.timeout,rpId:s.rpId,allowCredentials:s.allowCredentials?.map(e=>({id:V(e.id),type:"public-key",transports:e.transports})),userVerification:s.userVerification}}function et(s){let e=s.response;return{id:s.id,rawId:S(s.rawId),type:s.type,response:{clientDataJSON:S(e.clientDataJSON),attestationObject:S(e.attestationObject),transports:typeof e.getTransports=="function"?e.getTransports():void 0},clientExtensionResults:s.getClientExtensionResults()}}function Ee(s){if(!s||typeof s!="object")return null;let e=s.tokens,t=e&&typeof e=="object"?e:s;return typeof t.accessToken!="string"||!t.accessToken||typeof t.refreshToken!="string"||!t.refreshToken||typeof t.expiresIn!="number"||t.expiresIn<=0?null:{accessToken:t.accessToken,refreshToken:t.refreshToken,expiresIn:t.expiresIn}}function tt(s){if(!s||typeof s!="object")return null;let e=s.user;if(!e||typeof e!="object")return null;let t=e.id;return typeof t!="string"||t.length===0?null:e}function rt(s){try{let e=s.split(".");if(e.length!==3)return null;let t=e[1].replace(/-/g,"+").replace(/_/g,"/"),r=atob(t+"===".slice(0,(4-t.length%4)%4));return JSON.parse(r)}catch{return null}}function st(s){let e=s.response;return{id:s.id,rawId:S(s.rawId),type:s.type,response:{clientDataJSON:S(e.clientDataJSON),authenticatorData:S(e.authenticatorData),signature:S(e.signature),userHandle:e.userHandle?S(e.userHandle):null},clientExtensionResults:s.getClientExtensionResults()}}function nt(s){return s===!1?{pageViews:!1,sessionStart:!1,appOpen:!1,appBackground:!1,engagement:!1}:s===!0||s===void 0?{pageViews:!0,sessionStart:!0,appOpen:!0,appBackground:!0,engagement:!0}:{pageViews:s.pageViews??!0,sessionStart:s.sessionStart??!0,appOpen:s.appOpen??!0,appBackground:s.appBackground??!0,engagement:s.engagement??!0}}var it=250,Te=360*60*1e3;function Ce(s,e){if(typeof window>"u")return()=>{};let t=nt(e.autoTrack),r=e.sessionIdleMs??18e5,n=[],i="sendora_session_id",o="sendora_session_last_activity";function a(){return Re(i)??""}function c(d){_e(i,d)}function l(){let d=Re(o);return d&&Number(d)||0}function g(d){_e(o,String(d))}function v(){return`sess_${Date.now().toString(36)}_${Math.random().toString(36).slice(2,10)}`}function w(d){let I=Date.now(),N=l();if(!a()||N&&I-N>r){let G=v();c(G),d&&t.sessionStart&&s.track("session.start",{sessionId:G})}g(I)}w(t.sessionStart),t.appOpen&&s.track("app.opened",{sessionId:a()||void 0,url:window.location.origin+window.location.pathname,referrer:document.referrer||void 0});let k=window.location.pathname,C=0,A=document.visibilityState==="visible"?Date.now():0;function re(){A>0&&(C+=Date.now()-A,A=0)}function z(){if(!t.engagement)return;re();let d=C;if(C=0,!(d<it)){d>Te&&(d=Te);try{s.track("app.engagement",{durationMs:Math.round(d),screen:k,sessionId:a()||void 0})}catch{}}}function Pe(d){k=d,C=0,A=document.visibilityState==="visible"?Date.now():0}if(t.pageViews||t.engagement){t.pageViews&&s.page();let d=()=>{z(),w(!0),t.pageViews&&s.page(),Pe(window.location.pathname)},I=()=>d();window.addEventListener("popstate",I),n.push(()=>window.removeEventListener("popstate",I));let N=window;if(!N.__sendoraHistoryPatched){N.__sendoraHistoryPatched=!0;let G=history.pushState,Le=history.replaceState;history.pushState=function(...ne){let ie=G.apply(this,ne);return window.dispatchEvent(new Event("sendora:locationchange")),ie},history.replaceState=function(...ne){let ie=Le.apply(this,ne);return window.dispatchEvent(new Event("sendora:locationchange")),ie}}let se=()=>d();window.addEventListener("sendora:locationchange",se),n.push(()=>window.removeEventListener("sendora:locationchange",se))}if(t.appBackground||t.engagement){let d=()=>{if(document.visibilityState==="hidden"){z(),t.appBackground&&(w(!0),s.track("app.backgrounded",{sessionId:a()||void 0}));try{s.flush()}catch{}}else document.visibilityState==="visible"&&(t.appBackground&&(w(!0),s.track("app.foregrounded",{sessionId:a()||void 0})),t.engagement&&A===0&&(A=Date.now()))};if(document.addEventListener("visibilitychange",d),n.push(()=>document.removeEventListener("visibilitychange",d)),t.engagement){let I=()=>{z();try{s.flush()}catch{}};window.addEventListener("pagehide",I),n.push(()=>window.removeEventListener("pagehide",I))}}let pe=0,P=()=>{let d=Date.now();d-pe<5e3||(pe=d,w(!0))};return window.addEventListener("click",P,{passive:!0}),window.addEventListener("keydown",P,{passive:!0}),window.addEventListener("scroll",P,{passive:!0}),n.push(()=>{window.removeEventListener("click",P),window.removeEventListener("keydown",P),window.removeEventListener("scroll",P)}),()=>{z();for(let d of n)try{d()}catch{}}}function Re(s){try{return window.sessionStorage?.getItem(s)??null}catch{return null}}function _e(s,e){try{window.sessionStorage?.setItem(s,e)}catch{}}var B=class s{constructor(e){this.http=e}static isSupported(){return typeof window>"u"?!1:"serviceWorker"in navigator&&"PushManager"in window&&"Notification"in window}static permission(){return typeof Notification>"u"?"denied":Notification.permission}async subscribe(e){if(!s.isSupported())throw new Error("[sendoracloud] Web Push not supported in this browser.");let t=Notification.permission;if(t==="default"&&(t=await Notification.requestPermission()),t!=="granted")throw new Error(`[sendoracloud] Notification permission ${t}.`);let r=await navigator.serviceWorker.getRegistration(e.scope??"/");r||(r=await navigator.serviceWorker.register(e.serviceWorkerPath,{scope:e.scope??"/"})),await navigator.serviceWorker.ready;let i=(await this.http.get("/push/vapid"))?.data?.publicKey;if(!i)throw new Error("[sendoracloud] backend returned no VAPID public key.");let a=await r.pushManager.getSubscription()??await r.pushManager.subscribe({userVisibleOnly:!0,applicationServerKey:ot(i)}),c=a.toJSON(),l=c.endpoint,g=c.keys?.p256dh??"",v=c.keys?.auth??"",w=await at(l),k=await this.http.post("/push/tokens",{platform:"web",token:w,webEndpoint:l,webP256dh:g,webAuth:v,userAgent:typeof navigator<"u"?navigator.userAgent:void 0,locale:typeof navigator<"u"?navigator.language:void 0,timezone:Intl?.DateTimeFormat?.().resolvedOptions?.().timeZone,...e.userId?{userId:e.userId}:{}});if(!k?.data?.tokenId)throw new Error("[sendoracloud] backend returned no tokenId on subscribe.");return{tokenId:k.data.tokenId,subscription:a}}async unsubscribe(e="/"){if(!s.isSupported())return!1;let t=await navigator.serviceWorker.getRegistration(e);if(!t)return!1;let r=await t.pushManager.getSubscription();return r?await r.unsubscribe():!1}async trackOpen(e,t){let r={sendId:e};t!==void 0&&(r.clickAction=t);let n=await this.http.post("/push/track-open",r);if(!n.success)throw new Error(n.error?.message??"push.trackOpen failed")}};function ot(s){let e="=".repeat((4-s.length%4)%4),t=(s+e).replace(/-/g,"+").replace(/_/g,"/"),r=atob(t),n=new Uint8Array(r.length);for(let i=0;i<r.length;i++)n[i]=r.charCodeAt(i);return n}async function at(s){let e=new TextEncoder().encode(s),t=await crypto.subtle.digest("SHA-256",e);return Array.from(new Uint8Array(t)).map(n=>n.toString(16).padStart(2,"0")).join("")}var q=class{constructor(e){}unsupported(){throw new Error("[SendoraCloud] sendora.audiences is not available in the browser SDK. Audiences require a secret (sk_*) key with the audiences:read/write scopes and must be managed from a trusted server, not the browser.")}list(){return this.unsupported()}get(e){return this.unsupported()}create(e){return this.unsupported()}update(e,t){return this.unsupported()}delete(e){return this.unsupported()}};var p=class extends Error{constructor(e,t,r){super(t),this.name="LinkError",this.code=e,this.statusCode=r}};function le(s,e){let t=(s??"").toUpperCase();return t==="NOT_FOUND"?new p("NOT_FOUND",e):t==="FORBIDDEN"||t==="UNAUTHORIZED"?new p("UNAUTHORIZED",e):t==="RATE_LIMITED"?new p("RATE_LIMITED",e):t==="ENTITLEMENT_ERROR"||/plan limit/i.test(e)?new p("PLAN_LIMIT",e):t==="VALIDATION_ERROR"?/iOS bundle|Android package/i.test(e)?new p("BUNDLE_MISMATCH",e):/2KB|10KB|linkData/i.test(e)?new p("DATA_TOO_LARGE",e):/fallbackUrl/i.test(e)&&/apps/i.test(e)?new p("FALLBACK_REQUIRED",e):new p("INVALID_INPUT",e):t==="PARSE_ERROR"||t==="HTTP_ERROR"||/HTTP \d{3}/.test(e)?new p("NETWORK",e):new p("UNKNOWN",e||`Unknown LinkError (code=${s??"none"})`)}var de=/^[a-z0-9-]{3,20}$/;function ct(s){try{let t=new URL(s).pathname.split("/").filter(Boolean);if(t.length===0)return null;let r=t[t.length-1];return de.test(r)?r:null}catch{return null}}var ut=5*6e4,lt=50,dt=5,j=class{constructor(e){this.http=e;this.prewarmCache=new Map;this.prewarmInflight=0}cacheKey(e,t){if(t)return`k:${t}`;let r={},n=e;for(let i of Object.keys(n).sort())r[i]=n[i];return`s:${JSON.stringify(r)}`}evictExpired(){let e=Date.now();for(let[t,r]of this.prewarmCache)e-r.ts>ut&&this.prewarmCache.delete(t);for(;this.prewarmCache.size>lt;){let t=this.prewarmCache.keys().next().value;if(t===void 0)break;this.prewarmCache.delete(t)}}prewarm(e,t){this.evictExpired();let r=this.cacheKey(e,t?.key);if(this.prewarmCache.has(r)||this.prewarmInflight>=dt)return;this.prewarmInflight++;let n=this.doCreate(e).catch(i=>{throw this.prewarmCache.delete(r),i}).finally(()=>{this.prewarmInflight--});this.prewarmCache.set(r,{promise:n,ts:Date.now()})}async create(e,t){if(!e.title)throw new p("INVALID_INPUT","links.create: title is required");this.evictExpired();let r=this.cacheKey(e,t?.key),n=this.prewarmCache.get(r);return n?(this.prewarmCache.delete(r),n.promise):this.doCreate(e)}async doCreate(e){let t={title:e.title};e.fallbackUrl&&(t.fallbackUrl=e.fallbackUrl),e.iosDeepLinkPath&&(t.iosDeepLinkPath=e.iosDeepLinkPath),e.androidDeepLinkPath&&(t.androidDeepLinkPath=e.androidDeepLinkPath),e.linkData&&(t.linkData=e.linkData),e.ogTitle&&(t.ogTitle=e.ogTitle),e.ogDescription&&(t.ogDescription=e.ogDescription),e.ogImageUrl&&(t.ogImageUrl=e.ogImageUrl),e.campaign&&(t.campaign=e.campaign),e.source&&(t.source=e.source),e.medium&&(t.medium=e.medium),e.channel&&(t.channel=e.channel),e.tags&&(t.tags=e.tags),e.expiresAt&&(t.expiresAt=e.expiresAt),e.iosBundleId&&(t.iosBundleId=e.iosBundleId),e.androidPackageName&&(t.androidPackageName=e.androidPackageName);let r=await this.http.post("/sdk/links",t);if(!r.success||!r.data)throw le(r.error?.code,r.error?.message??"links.create failed");return r.data}async revoke(e){if(!de.test(e))throw new p("INVALID_INPUT",`links.revoke: '${e}' is not a valid shortcode`);let t=await this.http.post(`/sdk/links/${encodeURIComponent(e)}/revoke`,{});if(!t.success||!t.data)throw le(t.error?.code,t.error?.message??"links.revoke failed");return{shortcode:t.data.shortcode,revoked:!0}}async getStats(e){if(!de.test(e))throw new p("INVALID_INPUT",`links.getStats: '${e}' is not a valid shortcode`);let t=await this.http.get(`/sdk/links/${encodeURIComponent(e)}/stats`);if(!t.success||!t.data)throw le(t.error?.code,t.error?.message??"links.getStats failed");return t.data}};var J=class{constructor(e){this.http=e}async reportInstall(e={}){let t=await this.http.post("/attribution/install",e);if(!t.success||!t.data)throw new Error(t.error?.message??"attribution.reportInstall failed");return t.data}async checkDeferred(e={}){let t=await this.http.post("/attribution/deferred",e);if(!t.success)throw new Error(t.error?.message??"attribution.checkDeferred failed");return t.data??null}};var Z=class{constructor(e){this.http=e}async createTicket(e){let t=await this.http.post("/support/tickets",e);if(!t.success||!t.data)throw new Error(t.error?.message??"support.createTicket failed");return t.data}async submitCsat(e){let t=await this.http.post("/support/csat",e);if(!t.success||!t.data)throw new Error(t.error?.message??"support.submitCsat failed");return t.data}};var ee=class{constructor(e){this.http=e}async search(e){if(!e.query||e.query.trim().length===0)return[];let t=new URLSearchParams({query:e.query});e.limit&&t.set("limit",String(e.limit));let r=await this.http.get(`/kb/search?${t.toString()}`);if(!r.success||!Array.isArray(r.data))throw new Error(r.error?.message??"kb.search failed");return r.data}};var te=class te{constructor(e){this.autoTrackCleanup=null;this.http=new Y(e),this.storage=new Q,this.consent=new K(e.defaultConsent===!0,this.http),this.analytics=new x(e,this.http,this.storage,this.consent),this.flags=new O(e,this.http,this.storage),this.surveys=new M(e,this.http,this.storage),this.messages=new H(e,this.http,this.storage),this.chatbot=new F(e,this.http,this.storage),this.auth=new $(e,this.http,this.storage,this.analytics,this.flags,this.chatbot),this.webPush=new B(this.http),this.audiences=new q(this.http),this.links=new j(this.http),this.attribution=new J(this.http),this.support=new Z(this.http),this.kb=new ee(this.http),e.autoTrack!==!1&&(this.autoTrackCleanup=Ce(this.analytics,e))}static init(e){return we(e.apiKey),new te(e)}static sanitizeHtml(e){return ue(e)}static safeUrl(e){return E(e)}track(e,t){this.analytics.track(e,t)}page(e){this.analytics.page(e)}identify(e,t,r){this.analytics.identify(e,t,r),this.flags.setContext({userId:e})}reset(){this.analytics.reset(),this.flags.reset(),this.chatbot.newSession()}async flush(){await this.analytics.flush()}destroy(){this.autoTrackCleanup&&(this.autoTrackCleanup(),this.autoTrackCleanup=null),this.analytics.destroy(),this.flags.destroy(),this.messages.destroy()}};te.version=b;var he=te,or=he;export{W as AlreadyIdentifiedError,x as Analytics,q as Audiences,$ as Auth,u as AuthError,F as Chatbot,K as Consent,U as CredentialInUseError,D as EmailAlreadyTakenError,O as FeatureFlags,H as InAppMessaging,p as LinkError,j as Links,he as SendoraCloud,M as Surveys,f as ValidationError,B as WebPush,or as default,ct as extractShortcodeFromUrl,E as safeUrl,ue as sanitizeHtml};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sendoracloud/sdk-web",
3
- "version": "3.16.0",
3
+ "version": "3.17.0",
4
4
  "description": "Sendora Cloud Web SDK — analytics, feature flags, surveys, in-app messaging, chatbot, auth, deep links (Branch / Firebase parity). Zero dependencies, TypeScript, SSR-safe.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",