@cloudsignal/pwa-sdk 2.1.2 → 2.1.3

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
@@ -1150,6 +1150,14 @@ function markInstallationRegistered(organizationId, serviceId, registrationId) {
1150
1150
  setStorageItem(`install_id_${organizationId}_${serviceId}`, registrationId);
1151
1151
  return setStorageItem(key, true);
1152
1152
  }
1153
+ function getLastRegisteredIdentity(organizationId, serviceId) {
1154
+ const key = `last_identity_${organizationId}_${serviceId}`;
1155
+ return getStorageItem(key);
1156
+ }
1157
+ function setLastRegisteredIdentity(organizationId, serviceId, identity) {
1158
+ const key = `last_identity_${organizationId}_${serviceId}`;
1159
+ return setStorageItem(key, identity);
1160
+ }
1153
1161
  var IndexedDBStorage = class {
1154
1162
  constructor(dbName = "CloudSignalPWA", dbVersion = 1) {
1155
1163
  this.db = null;
@@ -1420,6 +1428,27 @@ var PushNotificationManager = class {
1420
1428
  this.onPermissionDenied?.();
1421
1429
  return null;
1422
1430
  }
1431
+ const incomingEmail = options.userEmail ?? null;
1432
+ const incomingUserId = options.userId ?? null;
1433
+ const stored = getLastRegisteredIdentity(this.organizationId, this.serviceId);
1434
+ if (stored) {
1435
+ const emailChanged = stored.email !== null && incomingEmail !== null && stored.email !== incomingEmail;
1436
+ const userIdChanged = stored.userId !== null && incomingUserId !== null && stored.userId !== incomingUserId;
1437
+ if (emailChanged || userIdChanged) {
1438
+ this.log(
1439
+ `Identity changed on this device (was ${stored.email || stored.userId}, now ${incomingEmail || incomingUserId}); dropping stale push subscription`
1440
+ );
1441
+ try {
1442
+ const stale = await this.serviceWorkerRegistration.pushManager.getSubscription();
1443
+ if (stale) {
1444
+ await stale.unsubscribe();
1445
+ this.pushSubscription = null;
1446
+ }
1447
+ } catch (err) {
1448
+ this.log(`Stale subscription unsubscribe failed: ${err}`, "error");
1449
+ }
1450
+ }
1451
+ }
1423
1452
  const subscription = await this.subscribeToPush();
1424
1453
  if (!subscription) {
1425
1454
  throw new Error("Failed to subscribe to push notifications");
@@ -1473,6 +1502,10 @@ var PushNotificationManager = class {
1473
1502
  const result = await response.json();
1474
1503
  this.registrationId = result.registration_id;
1475
1504
  setRegistrationId(this.organizationId, this.serviceId, this.registrationId);
1505
+ setLastRegisteredIdentity(this.organizationId, this.serviceId, {
1506
+ email: incomingEmail,
1507
+ userId: incomingUserId
1508
+ });
1476
1509
  const registration = {
1477
1510
  registrationId: result.registration_id,
1478
1511
  status: result.status || "active",
@@ -3,8 +3,8 @@ var CloudSignalPWA=(function(exports){'use strict';/**
3
3
  * https://cloudsignal.io
4
4
  * MIT License
5
5
  */
6
- var ne=Object.defineProperty;var re=(r,e)=>()=>(r&&(e=r(r=0)),e);var se=(r,e)=>{for(var t in e)ne(r,t,{get:e[t],enumerable:true});};var J={};se(J,{generateAuthHeaders:()=>H,generateHMACSignature:()=>$,isValidUUID:()=>v,makeAuthenticatedRequest:()=>X});function de(r){return Array.from(new Uint8Array(r)).map(e=>e.toString(16).padStart(2,"0")).join("")}function ge(r){return btoa(String.fromCharCode(...new Uint8Array(r)))}async function $(r,e,t,i,n,s=""){let o=new TextEncoder,l,c;try{let g=n.startsWith("http")?new URL(n):new URL(n,"https://pwa.cloudsignal.app");l=g.pathname,c=g.search?g.search.slice(1):"";}catch{let g=n.indexOf("?");g>-1?(l=n.slice(0,g),c=n.slice(g+1)):(l=n,c="");}let d=[i.toUpperCase(),l,c,e,t];if(s&&s.length>0){let g=await crypto.subtle.digest("SHA-256",o.encode(s)),G=de(g);d.push(G);}let a=d.join(`
7
- `),p=await crypto.subtle.importKey("raw",o.encode(r),{name:"HMAC",hash:"SHA-256"},false,["sign"]),I=await crypto.subtle.sign("HMAC",p,o.encode(a));return ge(I)}async function H(r,e,t,i,n){let s=Math.floor(Date.now()/1e3).toString(),o=await $(e,r,s,t,i,n||"");return {"X-CloudSignal-Organization-ID":r,"X-CloudSignal-Timestamp":s,"X-CloudSignal-Signature":o,"Content-Type":"application/json"}}async function X(r,e,t,i,n){let s=n?JSON.stringify(n):void 0,o=await H(r,e,t,i,s),l={method:t,headers:o};return s&&(l.body=s),fetch(i,l)}function v(r){return !r||typeof r!="string"?false:/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(r)}var P=re(()=>{});async function D(){try{let r=[];r.push(`${screen.width}x${screen.height}`),r.push(`${screen.colorDepth}`),r.push(Intl.DateTimeFormat().resolvedOptions().timeZone),r.push(navigator.language),r.push(navigator.platform),navigator.hardwareConcurrency&&r.push(navigator.hardwareConcurrency.toString()),navigator.deviceMemory&&r.push(navigator.deviceMemory.toString());let e=oe();e&&r.push(e);let t=ae();t&&r.push(t);let i=await le();i&&r.push(i);let n=r.join("|");return await ce(n)}catch(r){return console.warn("Browser fingerprint generation failed:",r),null}}function oe(){try{let r=document.createElement("canvas"),e=r.getContext("2d");return e?(r.width=200,r.height=50,e.textBaseline="alphabetic",e.fillStyle="#f60",e.fillRect(125,1,62,20),e.fillStyle="#069",e.font="11pt Arial",e.fillText("CloudSignal PWA",2,15),e.fillStyle="rgba(102, 204, 0, 0.7)",e.font="18pt Arial",e.fillText("CloudSignal PWA",4,45),r.toDataURL().slice(-50)):null}catch{return null}}function ae(){try{let r=document.createElement("canvas"),e=r.getContext("webgl")||r.getContext("experimental-webgl");if(!e)return null;let t=e,i=t.getExtension("WEBGL_debug_renderer_info");if(i){let o=t.getParameter(i.UNMASKED_VENDOR_WEBGL),l=t.getParameter(i.UNMASKED_RENDERER_WEBGL);return `${o}~${l}`}let n=t.getParameter(t.VENDOR),s=t.getParameter(t.RENDERER);return `${n}~${s}`}catch{return null}}async function le(){try{let r=window.AudioContext||window.webkitAudioContext;if(!r)return null;let e=new r,t=e.createOscillator(),i=e.createAnalyser(),n=e.createGain(),s=e.createScriptProcessor(4096,1,1);n.gain.value=0,t.type="triangle",t.connect(i),i.connect(s),s.connect(n),n.connect(e.destination),t.start(0),await new Promise(c=>setTimeout(c,100));let o=new Uint8Array(i.frequencyBinCount);i.getByteFrequencyData(o);let l=0;for(let c=0;c<o.length;c++)l+=o[c];return t.stop(),await e.close(),l.toString(36)}catch{return null}}async function ce(r){let t=new TextEncoder().encode(r),i=await crypto.subtle.digest("SHA-256",t);return Array.from(new Uint8Array(i)).map(s=>s.toString(16).padStart(2,"0")).join("")}function W(r,e,t,i,n){return [r,e,t,i,n].filter(Boolean).join("_").replace(/[^a-zA-Z0-9_]/g,"_")}var f=class{constructor(){this.cachedInfo=null;}getDeviceInfo(){if(this.cachedInfo)return this.cachedInfo;let e=this.getPlatformInfo(),t=this.getScreenInfo(),i=this.getNetworkInfo(),n=this.getCapabilities(),s={os:e.os,osVersion:e.osVersion,deviceType:e.deviceType,deviceModel:e.deviceModel,isMobile:this.isMobile(),isTablet:this.isTablet(),isDesktop:this.isDesktop(),isWebView:e.isWebView,browser:e.browser,browserVersion:e.browserVersion,isIOS:e.os==="iOS",isAndroid:e.os==="Android",isMacOS:e.os==="macOS",isWindows:e.os==="Windows",isLinux:e.os==="Linux",screenWidth:t.width,screenHeight:t.height,pixelRatio:t.pixelRatio,supportLevel:n.supportLevel,hasNotificationPermission:n.notifications,hasPushManager:n.push,hasServiceWorker:n.serviceWorker,hasShareAPI:n.share,hasBadgeAPI:n.badge,notificationPermission:this.getNotificationPermission(),isOnline:i.isOnline,connectionType:i.connectionType,platformIcon:this.getPlatformIcon(e.os,e.deviceType),userAgent:e.userAgent,trackingId:W(e.os,e.osVersion,e.browser,e.browserVersion,e.deviceModel)};return this.cachedInfo=s,s}clearCache(){this.cachedInfo=null;}getPlatformInfo(){let e=navigator.userAgent,t=navigator.platform,i="Unknown",n="Unknown",s="Unknown",o="Unknown",l="Unknown",c="Unknown",d=false;if(/iPad|iPhone|iPod/.test(e)&&!window.MSStream){i="iOS";let a=e.match(/OS (\d+)_(\d+)_?(\d+)?/);a&&(n=`${a[1]}.${a[2]}${a[3]?"."+a[3]:""}`),/iPad/.test(e)?(l="iPad",c=this.detectiPadModel(e)):/iPhone/.test(e)?(l="iPhone",c=this.detectiPhoneModel(e)):/iPod/.test(e)&&(l="iPod",c="iPod Touch");}else if(/Android/.test(e)){i="Android";let a=e.match(/Android (\d+\.?\d*\.?\d*)/);a&&(n=a[1]);let p=this.detectAndroidDevice(e);l=p.type,c=p.model;}else if(/Mac/.test(t)){i="macOS";let a=e.match(/Mac OS X (\d+)[_.](\d+)[_.]?(\d+)?/);a&&(n=`${a[1]}.${a[2]}${a[3]?"."+a[3]:""}`),l="Desktop",c="Mac";}else if(/Win/.test(t)){i="Windows";let a=e.match(/Windows NT (\d+\.\d+)/);a&&(n=this.getWindowsVersion(a[1])),l="Desktop",c="PC";}else /Linux/.test(t)&&(i="Linux",l="Desktop",c="Linux PC",/Ubuntu/.test(e)?n="Ubuntu":/Fedora/.test(e)?n="Fedora":/Debian/.test(e)&&(n="Debian"));if(/Chrome/.test(e)&&!/Edg/.test(e)&&!/OPR/.test(e)){s="Chrome";let a=e.match(/Chrome\/(\d+\.?\d*\.?\d*\.?\d*)/);a&&(o=a[1]);}else if(/Safari/.test(e)&&!/Chrome/.test(e)){s="Safari";let a=e.match(/Version\/(\d+\.?\d*\.?\d*)/);a&&(o=a[1]);}else if(/Firefox/.test(e)){s="Firefox";let a=e.match(/Firefox\/(\d+\.?\d*\.?\d*)/);a&&(o=a[1]);}else if(/Edg/.test(e)){s="Edge";let a=e.match(/Edg\/(\d+\.?\d*\.?\d*\.?\d*)/);a&&(o=a[1]);}else if(/OPR/.test(e)||/Opera/.test(e)){s="Opera";let a=e.match(/(?:OPR|Opera)\/(\d+\.?\d*\.?\d*)/);a&&(o=a[1]);}else if(/SamsungBrowser/.test(e)){s="Samsung Internet";let a=e.match(/SamsungBrowser\/(\d+\.?\d*)/);a&&(o=a[1]);}return d=this.detectWebView(e),{os:i,osVersion:n,browser:s,browserVersion:o,deviceType:l,deviceModel:c,isWebView:d,userAgent:e}}getScreenInfo(){return {width:screen.width,height:screen.height,pixelRatio:window.devicePixelRatio||1,orientation:screen.width>screen.height?"landscape":"portrait"}}getNetworkInfo(){let e=navigator.connection;return {isOnline:navigator.onLine,connectionType:e?.effectiveType||"unknown",effectiveType:e?.effectiveType,downlink:e?.downlink,rtt:e?.rtt,saveData:e?.saveData}}getCapabilities(){let e={serviceWorker:"serviceWorker"in navigator,push:"PushManager"in window,notifications:"Notification"in window,backgroundSync:"serviceWorker"in navigator&&"SyncManager"in window,badge:"setAppBadge"in navigator,share:"share"in navigator,shareTarget:"launchQueue"in window,fileSystemAccess:"showOpenFilePicker"in window,contactPicker:"ContactsManager"in window,periodicSync:"serviceWorker"in navigator&&"PeriodicSyncManager"in window,supportLevel:"none"};return e.serviceWorker&&e.push&&e.notifications?e.supportLevel="full":e.serviceWorker&&e.notifications?e.supportLevel="partial":e.serviceWorker&&(e.supportLevel="basic"),e}getNotificationPermission(){return "Notification"in window?Notification.permission:"denied"}isMobile(){let e=navigator.userAgent;return /Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(e)}isTablet(){let e=navigator.userAgent;return /iPad|Android(?!.*Mobile)|Tablet/i.test(e)}isDesktop(){return !this.isMobile()&&!this.isTablet()}getPlatformIcon(e,t){return e==="iOS"||t==="iPhone"||t==="iPad"?"\u{1F4F1}":e==="Android"?"\u{1F916}":e==="macOS"?"\u{1F4BB}":"\u{1F5A5}\uFE0F"}detectiPhoneModel(e){let t=screen.height,i=screen.width;return t===932||i===932?"iPhone 15 Pro Max/14 Pro Max":t===896||i===896?"iPhone 15 Pro/14 Pro/11 Pro Max":t===852||i===852?"iPhone 15/14":t===844||i===844?"iPhone 13/12":t===812||i===812?"iPhone X/XS/11 Pro":t===736||i===736?"iPhone 8 Plus":t===667||i===667?"iPhone 8/SE":t===568||i===568?"iPhone SE (1st)":"iPhone"}detectiPadModel(e){let t=screen.width,i=screen.height,n=window.devicePixelRatio||1;return (i===1366||t===1366)&&n===2?'iPad Pro 12.9"':(i===1194||t===1194)&&n===2?'iPad Pro 11"':(i===1180||t===1180)&&n===2?"iPad Air/10th Gen":(i===1133||t===1133)&&n===2?"iPad mini":"iPad"}detectAndroidDevice(e){let t="Phone",i="Android Device";if(/Tablet|SM-T|Tab|GT-P|MediaPad/i.test(e)&&(t="Tablet"),/SM-S9\d{2}/i.test(e))i="Samsung Galaxy S24";else if(/SM-S91\d/i.test(e))i="Samsung Galaxy S23";else if(/SM-S90\d/i.test(e))i="Samsung Galaxy S22";else if(/SM-G99\d/i.test(e))i="Samsung Galaxy S21";else if(/SM-N9\d{2}/i.test(e))i="Samsung Galaxy Note";else if(/SM-A\d{2}/i.test(e))i="Samsung Galaxy A Series";else if(/SM-/i.test(e)){let n=e.match(/SM-[A-Z]\d+/i);n&&(i=`Samsung ${n[0]}`);}if(/Pixel 8/i.test(e)?i="Google Pixel 8":/Pixel 7/i.test(e)?i="Google Pixel 7":/Pixel 6/i.test(e)?i="Google Pixel 6":/Pixel/i.test(e)&&(i="Google Pixel"),/OnePlus/i.test(e)){let n=e.match(/OnePlus[\s]?(\w+)/i);i=n?`OnePlus ${n[1]}`:"OnePlus";}return /Xiaomi|Redmi|POCO|Mi\s/i.test(e)&&(/Redmi/i.test(e)?i="Xiaomi Redmi":/POCO/i.test(e)?i="Xiaomi POCO":i="Xiaomi"),/HUAWEI|Honor/i.test(e)&&(i=/Honor/i.test(e)?"Honor":"Huawei"),/OPPO/i.test(e)&&(i="OPPO"),/vivo/i.test(e)&&(i="Vivo"),{type:t,model:i}}getWindowsVersion(e){return {"10.0":"Windows 10/11","6.3":"Windows 8.1","6.2":"Windows 8","6.1":"Windows 7","6.0":"Windows Vista","5.1":"Windows XP"}[e]||`Windows NT ${e}`}detectWebView(e){return !!(/FBAN|FBAV/i.test(e)||/Instagram/i.test(e)||/LinkedIn/i.test(e)||/Twitter/i.test(e)||/MicroMessenger/i.test(e)||/Snapchat/i.test(e)||/BytedanceWebview|TikTok/i.test(e)||/wv|WebView/i.test(e))}},ue=new f;var S=class{constructor(e={}){this.registration=null;this.config={path:"/service-worker.js",scope:"/",autoRegister:true,updateBehavior:"auto",...e.config},this.debug=e.debug??false,this.onRegistered=e.onRegistered,this.onUpdated=e.onUpdated,this.onError=e.onError;}isSupported(){return "serviceWorker"in navigator}getRegistration(){return this.registration}async register(){if(!this.isSupported())return this.log("Service workers not supported"),null;try{let{path:e,scope:t}=this.getPathAndScope();this.log(`Registering service worker: ${e} with scope ${t}`);let i=await navigator.serviceWorker.register(e,{scope:t});return this.registration=i,i.addEventListener("updatefound",()=>{this.handleUpdateFound(i);}),await navigator.serviceWorker.ready,this.log("Service worker registered successfully"),this.onRegistered?.(i),i}catch(e){let t=e instanceof Error?e:new Error(String(e));return this.log(`Service worker registration failed: ${t.message}`,"error"),this.onError?.(t),null}}async unregister(){if(!this.registration)return false;try{let e=await this.registration.unregister();return e&&(this.registration=null,this.log("Service worker unregistered")),e}catch(e){return this.log(`Failed to unregister service worker: ${e}`,"error"),false}}async checkForUpdate(){if(this.registration)try{await this.registration.update(),this.log("Checked for service worker update");}catch(e){this.log(`Failed to check for update: ${e}`,"error");}}async waitForReady(e=5e3){if(!this.isSupported())return null;let t=new Promise(i=>{setTimeout(()=>i(null),e);});try{let i=await Promise.race([navigator.serviceWorker.ready,t]);return i&&(this.registration=i),i}catch{return null}}postMessage(e){if(!this.registration?.active){this.log("No active service worker to send message to","warn");return}this.registration.active.postMessage(e);}clearBadge(){this.postMessage({type:"CLEAR_BADGE"});}setBadge(e){this.postMessage({type:"SET_BADGE",count:e});}handleUpdateFound(e){let t=e.installing;t&&t.addEventListener("statechange",()=>{t.state==="installed"&&navigator.serviceWorker.controller&&(this.log("New service worker available"),this.config.updateBehavior==="auto"&&t.postMessage({type:"SKIP_WAITING"}),this.onUpdated?.(e));});}getPathAndScope(){if(this.config.path&&this.config.scope)return {path:this.config.path,scope:this.config.scope};try{let e=window.location.pathname||"/";if(e.startsWith("/version-live"))return {path:"/version-live/service-worker.js",scope:"/version-live/"};if(e.startsWith("/version-test"))return {path:"/version-test/service-worker.js",scope:"/version-test/"}}catch{}return {path:this.config.path||"/service-worker.js",scope:this.config.scope||"/"}}log(e,t="log"){if(!this.debug)return;console[t](`[CloudSignal PWA SW] ${e}`);}};var k=class{constructor(e={}){this.deferredPrompt=null;this.isInstalled=false;this.debug=e.debug??false,this.onInstallAvailable=e.onInstallAvailable,this.onInstallAccepted=e.onInstallAccepted,this.onInstallDismissed=e.onInstallDismissed,this.onInstalled=e.onInstalled,this.detectInstallationStatus();}initialize(){window.addEventListener("beforeinstallprompt",e=>{e.preventDefault(),this.deferredPrompt=e,this.log("Install prompt available"),this.onInstallAvailable?.(this.deferredPrompt);}),window.addEventListener("appinstalled",()=>{this.isInstalled=true,this.deferredPrompt=null,this.log("PWA was installed"),this.onInstalled?.();}),window.matchMedia&&window.matchMedia("(display-mode: standalone)").addEventListener("change",t=>{t.matches&&(this.isInstalled=true,this.log("App is now running in standalone mode"));});}getState(){let e=this.getDisplayMode(),t=this.isIOSDevice(),i=this.isSafariBrowser();return {isInstalled:this.isInstalled||e!=="browser",canBeInstalled:this.deferredPrompt!==null,needsManualInstall:t&&i&&!this.isInstalled,showManualInstructions:t&&i&&!this.isInstalled,installSteps:this.getInstallSteps(),displayMode:e}}async showInstallPrompt(){if(!this.deferredPrompt)return this.log("No install prompt available","warn"),{accepted:false,outcome:"dismissed"};try{await this.deferredPrompt.prompt();let{outcome:e,platform:t}=await this.deferredPrompt.userChoice,i={accepted:e==="accepted",outcome:e,platform:t};return e==="accepted"?(this.log("User accepted install prompt"),this.isInstalled=!0,this.onInstallAccepted?.(i)):(this.log("User dismissed install prompt"),this.onInstallDismissed?.(i)),this.deferredPrompt=null,i}catch(e){return this.log(`Error showing install prompt: ${e}`,"error"),{accepted:false,outcome:"dismissed"}}}canInstall(){return this.deferredPrompt!==null}isPWAInstalled(){return this.isInstalled||this.getDisplayMode()!=="browser"}getDisplayMode(){return window.matchMedia("(display-mode: standalone)").matches?"standalone":window.matchMedia("(display-mode: minimal-ui)").matches?"minimal-ui":window.matchMedia("(display-mode: fullscreen)").matches?"fullscreen":navigator.standalone===true?"standalone":"browser"}getInstallSteps(){return this.isIOSDevice()?["Tap the Share button in Safari",'Scroll down and tap "Add to Home Screen"','Tap "Add" to install the app']:this.isAndroidDevice()?["Tap the menu (three dots) in your browser",'Tap "Install App" or "Add to Home Screen"',"Follow the prompts to install"]:["Click the install icon in the address bar","Or use the browser menu to install the app",'Click "Install" when prompted']}detectInstallationStatus(){if(this.getDisplayMode()!=="browser"){this.isInstalled=true;return}if(navigator.standalone===true){this.isInstalled=true;return}"getInstalledRelatedApps"in navigator&&navigator.getInstalledRelatedApps().then(e=>{e.length>0&&(this.isInstalled=true);}).catch(()=>{});}isIOSDevice(){return /iPad|iPhone|iPod/.test(navigator.userAgent)&&!window.MSStream}isAndroidDevice(){return /Android/i.test(navigator.userAgent)}isSafariBrowser(){let e=navigator.userAgent;return /Safari/i.test(e)&&!/Chrome|CriOS|FxiOS/i.test(e)}log(e,t="log"){if(!this.debug)return;console[t](`[CloudSignal PWA Install] ${e}`);}};P();function z(r,e){return {Authorization:`Bearer ${r}`,"X-CloudSignal-Organization-ID":e,"Content-Type":"application/json"}}async function Y(r,e,t,i){let n=i?JSON.stringify(i):void 0,s=z(r.token,r.organizationId),o={method:e,headers:s};n&&(o.body=n);let l=await fetch(t,o);if(l.status===401&&r.onTokenExpired)try{let c=await r.onTokenExpired();c&&(s=z(c,r.organizationId),o.headers=s,l=await fetch(t,o));}catch(c){console.warn("[CloudSignal PWA] Token refresh failed:",c);}return l}function y(r){if(r.userToken)return {mode:"jwt",organizationId:r.organizationId,userToken:r.userToken,onTokenExpired:r.onTokenExpired};if(!r.organizationPublishableKey)throw new Error("Either userToken or organizationPublishableKey is required");return {mode:"hmac",organizationId:r.organizationId,organizationPublishableKey:r.organizationPublishableKey}}async function h(r,e,t,i,n){if(r.mode==="jwt"){if(!r.userToken)throw new Error("userToken required for JWT auth mode");return Y({token:r.userToken,organizationId:r.organizationId,onTokenExpired:n||r.onTokenExpired},e,t,i)}let{makeAuthenticatedRequest:s}=await Promise.resolve().then(()=>(P(),J));if(!r.organizationPublishableKey)throw new Error("organizationPublishableKey required for HMAC auth mode");return s(r.organizationId,r.organizationPublishableKey,e,t,i)}function b(r,e){return r.mode!=="jwt"?{mode:"jwt",organizationId:r.organizationId,userToken:e,onTokenExpired:r.onTokenExpired}:{...r,userToken:e}}var Q="cloudsignal_pwa_";function _(r,e){try{let t=`${Q}${r}`,i=localStorage.getItem(t);return i===null?e??null:JSON.parse(i)}catch{return e??null}}function O(r,e){try{let t=`${Q}${r}`;return localStorage.setItem(t,JSON.stringify(e)),!0}catch{return false}}function Z(r){try{let e=`${Q}${r}`;return localStorage.removeItem(e),!0}catch{return false}}function U(r,e){let t=`registration_${r}_${e}`;return _(t)}function F(r,e,t){let i=`registration_${r}_${e}`;return O(i,t)}function M(r,e){let t=`registration_${r}_${e}`;return Z(t)}function ee(r,e){let t=`install_registered_${r}_${e}`;return _(t)===true}function te(r,e,t){let i=`install_registered_${r}_${e}`;return O(`install_id_${r}_${e}`,t),O(i,true)}var j=class{constructor(e="CloudSignalPWA",t=1){this.db=null;this.dbName=e,this.dbVersion=t;}async init(){return new Promise((e,t)=>{let i=indexedDB.open(this.dbName,this.dbVersion);i.onerror=()=>t(i.error),i.onsuccess=()=>{this.db=i.result,e(this.db);},i.onupgradeneeded=n=>{let s=n.target.result;if(s.objectStoreNames.contains("badge")||s.createObjectStore("badge"),!s.objectStoreNames.contains("notifications")){let o=s.createObjectStore("notifications",{keyPath:"id",autoIncrement:true});o.createIndex("timestamp","timestamp",{unique:false}),o.createIndex("read","read",{unique:false});}s.objectStoreNames.contains("userPreferences")||s.createObjectStore("userPreferences"),s.objectStoreNames.contains("syncQueue")||s.createObjectStore("syncQueue",{keyPath:"id",autoIncrement:true}).createIndex("timestamp","timestamp",{unique:false});};})}async ensureConnection(){this.db||await this.init();}promisifyRequest(e){return new Promise((t,i)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>i(e.error);})}async getBadgeCount(){await this.ensureConnection();let t=this.db.transaction(["badge"],"readonly").objectStore("badge");return await this.promisifyRequest(t.get("count"))||0}async setBadgeCount(e){await this.ensureConnection();let i=this.db.transaction(["badge"],"readwrite").objectStore("badge");await this.promisifyRequest(i.put(e,"count"));}async incrementBadgeCount(e=1){let t=await this.getBadgeCount(),i=Math.max(0,t+e);return await this.setBadgeCount(i),i}async saveNotification(e){await this.ensureConnection();let i=this.db.transaction(["notifications"],"readwrite").objectStore("notifications"),n={...e,timestamp:Date.now(),read:false};return await this.promisifyRequest(i.add(n))}async getRecentNotifications(e=50){await this.ensureConnection();let n=this.db.transaction(["notifications"],"readonly").objectStore("notifications").index("timestamp"),s=[],o=n.openCursor(null,"prev");return new Promise((l,c)=>{o.onsuccess=d=>{let a=d.target.result;a&&s.length<e?(s.push(a.value),a.continue()):l(s);},o.onerror=()=>c(o.error);})}async markNotificationAsRead(e){await this.ensureConnection();let i=this.db.transaction(["notifications"],"readwrite").objectStore("notifications"),n=await this.promisifyRequest(i.get(e));n&&(n.read=true,await this.promisifyRequest(i.put(n)));}async getUserPreference(e){await this.ensureConnection();let i=this.db.transaction(["userPreferences"],"readonly").objectStore("userPreferences");return this.promisifyRequest(i.get(e))}async setUserPreference(e,t){await this.ensureConnection();let n=this.db.transaction(["userPreferences"],"readwrite").objectStore("userPreferences");await this.promisifyRequest(n.put(t,e));}async addToSyncQueue(e){await this.ensureConnection();let i=this.db.transaction(["syncQueue"],"readwrite").objectStore("syncQueue"),n={...e,timestamp:Date.now(),retries:0};return await this.promisifyRequest(i.add(n))}async getSyncQueue(){await this.ensureConnection();let t=this.db.transaction(["syncQueue"],"readonly").objectStore("syncQueue");return this.promisifyRequest(t.getAll())}async removeFromSyncQueue(e){await this.ensureConnection();let i=this.db.transaction(["syncQueue"],"readwrite").objectStore("syncQueue");await this.promisifyRequest(i.delete(e));}};var x=class{constructor(e){this.serviceWorkerRegistration=null;this.pushSubscription=null;this.registrationId=null;this.vapidPublicKey=null;this.serviceUrl=e.serviceUrl,this.organizationId=e.organizationId,this.serviceId=e.serviceId,this.debug=e.debug??false,this.deviceDetector=new f,this.authContext=y({organizationId:e.organizationId,organizationPublishableKey:e.organizationPublishableKey,userToken:e.userToken}),this.onRegistered=e.onRegistered,this.onUnregistered=e.onUnregistered,this.onError=e.onError,this.onPermissionDenied=e.onPermissionDenied,this.onTokenExpired=e.onTokenExpired,this.registrationId=U(this.organizationId,this.serviceId);}setServiceWorkerRegistration(e){this.serviceWorkerRegistration=e;}setVapidPublicKey(e){this.vapidPublicKey=e;}getRegistrationId(){return this.registrationId}isRegistered(){return this.registrationId!==null}getAuthMode(){return this.authContext.mode}updateToken(e){this.authContext=b(this.authContext,e),this.log(`Auth mode: ${this.authContext.mode}`);}async register(e={}){try{if(!this.serviceWorkerRegistration)throw new Error("Service worker not registered");if(!this.vapidPublicKey)throw new Error("VAPID public key not set. Call downloadConfig() first.");if(await this.requestPermission()!=="granted")return this.log("Notification permission denied"),this.onPermissionDenied?.(),null;let i=await this.subscribeToPush();if(!i)throw new Error("Failed to subscribe to push notifications");this.pushSubscription=i;let n=await D(),s=this.deviceDetector.getDeviceInfo(),o=this.deviceDetector.getPlatformInfo(),l=i.toJSON(),c={serviceId:this.serviceId,userEmail:e.userEmail,userId:v(e.userId)?e.userId:void 0,endpoint:i.endpoint,keys:{p256dh:l.keys?.p256dh||"",auth:l.keys?.auth||""},browserFingerprint:n||void 0,deviceType:s.deviceType,deviceModel:s.deviceModel,browserName:o.browser,browserVersion:o.browserVersion,osName:o.os,osVersion:o.osVersion,userAgent:o.userAgent,displayMode:"standalone",isInstalled:!0,timezone:e.timezone||Intl.DateTimeFormat().resolvedOptions().timeZone,language:e.language||navigator.language||"en-US"},d=`${this.serviceUrl}/api/v1/registration/register`,a=await h(this.authContext,"POST",d,c,this.onTokenExpired);if(!a.ok){if(a.status===422&&(await a.clone().json().catch(()=>null))?.detail?.code==="user_identity_required")return this.log("Skipping push registration: service requires user identity"),null;let g=await a.text();throw new Error(`Registration failed: ${a.status} - ${g}`)}let p=await a.json();this.registrationId=p.registration_id,F(this.organizationId,this.serviceId,this.registrationId);let I={registrationId:p.registration_id,status:p.status||"active",createdAt:p.created_at||new Date().toISOString(),isActive:!0};return this.log(`Push registration successful: ${I.registrationId}`),this.onRegistered?.(I),I}catch(t){let i=t instanceof Error?t:new Error(String(t));return this.log(`Push registration failed: ${i.message}`,"error"),this.onError?.(i),null}}async registerInstallation(){try{if(ee(this.organizationId,this.serviceId))return this.log("Installation already registered, skipping"),null;let e=await D(),t=this.deviceDetector.getDeviceInfo(),i=this.deviceDetector.getPlatformInfo(),n={service_id:this.serviceId,browser_fingerprint:e||void 0,device_type:t.deviceType,device_model:t.deviceModel,browser_name:i.browser,browser_version:i.browserVersion,os_name:i.os,os_version:i.osVersion,user_agent:i.userAgent,display_mode:"standalone",is_installed:!0,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,language:navigator.language||"en-US"},s=`${this.serviceUrl}/api/v1/registration/install-only`,o=await h(this.authContext,"POST",s,n,this.onTokenExpired);if(!o.ok){if(o.status===422&&(await o.clone().json().catch(()=>null))?.detail?.code==="user_identity_required")return this.log("Skipping install-only registration: service requires user identity"),null;let c=await o.text();throw new Error(`Installation registration failed: ${o.status} - ${c}`)}let l=await o.json();return te(this.organizationId,this.serviceId,l.registration_id),this.log(`Installation registered: ${l.registration_id}`),{registrationId:l.registration_id}}catch(e){let t=e instanceof Error?e:new Error(String(e));return this.log(`Installation registration failed: ${t.message}`,"error"),this.onError?.(t),null}}async unregister(){try{if(!this.registrationId)return this.log("No registration to unregister"),!0;this.pushSubscription&&(await this.pushSubscription.unsubscribe(),this.pushSubscription=null);let e=`${this.serviceUrl}/api/v1/registration/unregister`,t=await h(this.authContext,"POST",e,{registration_id:this.registrationId},this.onTokenExpired);return t.ok||this.log(`Backend unregistration failed: ${t.status}`,"warn"),M(this.organizationId,this.serviceId),this.registrationId=null,this.log("Push unregistration successful"),this.onUnregistered?.(),!0}catch(e){let t=e instanceof Error?e:new Error(String(e));return this.log(`Push unregistration failed: ${t.message}`,"error"),this.onError?.(t),false}}async updatePreferences(e){try{if(!this.registrationId)throw new Error("Not registered for push notifications");let t=`${this.serviceUrl}/api/v1/registration/update`,i=await h(this.authContext,"POST",t,{registration_id:this.registrationId,notification_topics:e.topics,timezone:e.timezone,language:e.language,is_active:e.isActive},this.onTokenExpired);if(!i.ok)throw new Error(`Update failed: ${i.status}`);return this.log("Preferences updated successfully"),!0}catch(t){let i=t instanceof Error?t:new Error(String(t));return this.log(`Failed to update preferences: ${i.message}`,"error"),this.onError?.(i),false}}async checkStatus(){try{if(!this.registrationId)return null;let e=`${this.serviceUrl}/api/v1/registration/status/${this.registrationId}`,t=await h(this.authContext,"GET",e,void 0,this.onTokenExpired);if(!t.ok){if(t.status===404)return M(this.organizationId,this.serviceId),this.registrationId=null,null;throw new Error(`Status check failed: ${t.status}`)}let i=await t.json();return {registrationId:i.registration_id,status:i.status,isActive:i.is_active,isOnline:i.is_online,lastActive:i.last_active,lastSeenOnline:i.last_seen_online,lastHeartbeat:i.last_heartbeat,installationDate:i.installation_date,notificationCount:i.notification_count,deviceInfo:i.device_info}}catch(e){let t=e instanceof Error?e:new Error(String(e));return this.log(`Status check failed: ${t.message}`,"error"),null}}async requestPermission(){return "Notification"in window?Notification.permission==="granted"?"granted":Notification.permission==="denied"?"denied":await Notification.requestPermission():"denied"}async subscribeToPush(){if(!this.serviceWorkerRegistration||!this.vapidPublicKey)return null;try{let e=await this.serviceWorkerRegistration.pushManager.getSubscription();if(!e){let t=this.urlBase64ToUint8Array(this.vapidPublicKey);e=await this.serviceWorkerRegistration.pushManager.subscribe({userVisibleOnly:!0,applicationServerKey:t.buffer});}return e}catch(e){return this.log(`Push subscription failed: ${e}`,"error"),null}}urlBase64ToUint8Array(e){let t="=".repeat((4-e.length%4)%4),i=(e+t).replace(/-/g,"+").replace(/_/g,"/"),n=atob(i),s=new Uint8Array(n.length);for(let o=0;o<n.length;++o)s[o]=n.charCodeAt(o);return s}log(e,t="log"){if(!this.debug)return;console[t](`[CloudSignal PWA Push] ${e}`);}};P();var he={"4g":3e4,"3g":6e4,"2g":12e4,"slow-2g":18e4,unknown:3e4},R=class{constructor(e){this.registrationId=null;this.intervalId=null;this.isRunning=false;this.isPausedForBattery=false;this.visibilityHandler=null;this.connectionChangeHandler=null;this.batteryManager=null;this.serviceUrl=e.serviceUrl,this.organizationId=e.organizationId,this.debug=e.debug??false,this.onHeartbeatSent=e.onHeartbeatSent,this.onHeartbeatError=e.onHeartbeatError,this.onIntervalChanged=e.onIntervalChanged,this.onPausedForBattery=e.onPausedForBattery,this.onResumedFromBattery=e.onResumedFromBattery,this.onTokenExpired=e.onTokenExpired,this.authContext=y({organizationId:e.organizationId,organizationPublishableKey:e.organizationPublishableKey,userToken:e.userToken}),this.config={enabled:e.config?.enabled??true,interval:e.config?.interval??3e4,autoStart:e.config?.autoStart??true,stopOnHidden:e.config?.stopOnHidden??true},this.adaptiveConfig={adaptToNetwork:e.adaptiveConfig?.adaptToNetwork??true,adaptToBattery:e.adaptiveConfig?.adaptToBattery??true,batteryPauseThreshold:e.adaptiveConfig?.batteryPauseThreshold??.15,intervals:{...he,...e.adaptiveConfig?.intervals}},this.currentInterval=this.config.interval;}setRegistrationId(e){this.registrationId=e,this.config.autoStart&&!this.isRunning&&this.config.enabled&&this.start();}start(){if(this.isRunning){this.log("Heartbeat already running");return}if(!this.registrationId||!v(this.registrationId)){this.log("Cannot start heartbeat: no valid registration ID","warn");return}if(!this.config.enabled){this.log("Heartbeat is disabled");return}this.isRunning=true,this.updateIntervalForNetwork(),this.log(`Starting heartbeat with interval ${this.currentInterval}ms`),this.sendHeartbeat(),this.startInterval(),this.config.stopOnHidden&&this.setupVisibilityHandler(),this.adaptiveConfig.adaptToNetwork&&this.setupNetworkChangeHandler(),this.adaptiveConfig.adaptToBattery&&this.setupBatteryMonitoring();}stop(){if(this.isRunning){if(this.isRunning=false,this.isPausedForBattery=false,this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null),this.visibilityHandler&&(document.removeEventListener("visibilitychange",this.visibilityHandler),this.visibilityHandler=null),this.connectionChangeHandler){let e=navigator.connection;e&&e.removeEventListener("change",this.connectionChangeHandler),this.connectionChangeHandler=null;}this.log("Heartbeat stopped");}}isHeartbeatRunning(){return this.isRunning}getAuthMode(){return this.authContext.mode}updateToken(e){this.authContext=b(this.authContext,e),this.log(`Auth mode: ${this.authContext.mode}`);}async sendHeartbeat(){if(!this.registrationId||!v(this.registrationId))return this.log("Cannot send heartbeat: no valid registration ID","warn"),false;try{let e=`${this.serviceUrl}/api/v1/registration/heartbeat/${this.registrationId}`,t=await h(this.authContext,"POST",e,void 0,this.onTokenExpired);if(!t.ok)throw new Error(`Heartbeat failed: ${t.status}`);return this.log("Heartbeat sent successfully"),this.onHeartbeatSent?.(),!0}catch(e){let t=e instanceof Error?e:new Error(String(e));return this.log(`Heartbeat failed: ${t.message}`,"error"),this.onHeartbeatError?.(t),false}}updateConfig(e){let t=this.isRunning;t&&this.stop(),this.config={...this.config,...e},t&&this.config.enabled&&this.start();}getNetworkInfo(){let e=navigator.connection;return e?{effectiveType:e.effectiveType,saveData:e.saveData,downlink:e.downlink,rtt:e.rtt}:{}}async getBatteryInfo(){try{if(!("getBattery"in navigator))return null;let e=await navigator.getBattery();return {level:e.level,charging:e.charging}}catch{return null}}getCurrentInterval(){return this.currentInterval}isPausedDueToBattery(){return this.isPausedForBattery}startInterval(){this.intervalId&&clearInterval(this.intervalId),this.intervalId=setInterval(()=>{this.sendHeartbeat();},this.currentInterval);}updateIntervalForNetwork(){if(!this.adaptiveConfig.adaptToNetwork){this.currentInterval=this.config.interval;return}let e=navigator.connection;if(!e){this.currentInterval=this.config.interval;return}if(e.saveData){let n=Math.max(this.currentInterval,12e4);n!==this.currentInterval&&(this.currentInterval=n,this.log(`Interval increased to ${n}ms (saveData enabled)`),this.onIntervalChanged?.(n,"saveData"));return}let t=e.effectiveType||"unknown",i=this.adaptiveConfig.intervals[t]||this.config.interval;if(i!==this.currentInterval){let n=this.currentInterval;this.currentInterval=i,this.log(`Interval changed from ${n}ms to ${i}ms (${t})`),this.onIntervalChanged?.(i,t);}}setupNetworkChangeHandler(){let e=navigator.connection;e&&(this.connectionChangeHandler=()=>{let t=this.currentInterval;this.updateIntervalForNetwork(),t!==this.currentInterval&&this.isRunning&&!this.isPausedForBattery&&this.startInterval();},e.addEventListener("change",this.connectionChangeHandler));}async setupBatteryMonitoring(){try{if(!("getBattery"in navigator))return;this.batteryManager=await navigator.getBattery();let e=()=>{if(!this.batteryManager)return;let t=this.batteryManager.level,i=this.batteryManager.charging;t<=this.adaptiveConfig.batteryPauseThreshold&&!i?!this.isPausedForBattery&&this.isRunning&&(this.isPausedForBattery=!0,this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null),this.log(`Heartbeat paused (battery at ${Math.round(t*100)}%)`),this.onPausedForBattery?.()):this.isPausedForBattery&&this.isRunning&&(this.isPausedForBattery=!1,this.startInterval(),this.log(`Heartbeat resumed (battery at ${Math.round(t*100)}%)`),this.onResumedFromBattery?.());};e(),this.batteryManager.addEventListener("levelchange",e),this.batteryManager.addEventListener("chargingchange",e);}catch{}}setupVisibilityHandler(){this.visibilityHandler=()=>{document.hidden?(this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null),this.log("Heartbeat paused (page hidden)")):this.isRunning&&!this.intervalId&&!this.isPausedForBattery&&(this.sendHeartbeat(),this.startInterval(),this.log("Heartbeat resumed (page visible)"));},document.addEventListener("visibilitychange",this.visibilityHandler);}log(e,t="log"){if(!this.debug)return;console[t](`[CloudSignal PWA Heartbeat] ${e}`);}};var E=class{constructor(e={}){this.wakeLock=null;this.visibilityHandler=null;this.releaseHandler=null;this.shouldReacquire=false;this.debug=e.debug??false,this.onAcquired=e.onAcquired,this.onReleased=e.onReleased,this.onError=e.onError,this.config={enabled:e.config?.enabled??true,autoReacquire:e.config?.autoReacquire??true};}isSupported(){return "wakeLock"in navigator}getState(){return {isActive:this.wakeLock!==null&&!this.wakeLock.released,isSupported:this.isSupported(),type:"screen",acquiredAt:this.wakeLock&&!this.wakeLock.released?Date.now():void 0}}async acquire(){if(!this.config.enabled)return this.log("Wake lock is disabled"),false;if(!this.isSupported())return this.log("Wake Lock API not supported"),this.onError?.({timestamp:Date.now(),error:"Wake Lock API not supported",errorName:"NotSupportedError"}),false;if(this.wakeLock&&!this.wakeLock.released)return this.log("Wake lock already active"),true;try{return this.wakeLock=await navigator.wakeLock.request("screen"),this.shouldReacquire=!0,this.releaseHandler=()=>{this.handleRelease("system");},this.wakeLock.addEventListener("release",this.releaseHandler),this.config.autoReacquire&&this.setupVisibilityHandler(),this.log("Wake lock acquired"),this.onAcquired?.({timestamp:Date.now()}),!0}catch(e){let t=e instanceof Error?e:new Error(String(e));return this.log(`Failed to acquire wake lock: ${t.message}`,"error"),this.onError?.({timestamp:Date.now(),error:t.message,errorName:t.name}),false}}async release(){if(this.shouldReacquire=false,!!this.wakeLock){try{this.releaseHandler&&(this.wakeLock.removeEventListener("release",this.releaseHandler),this.releaseHandler=null),await this.wakeLock.release(),this.wakeLock=null,this.log("Wake lock released manually"),this.onReleased?.({timestamp:Date.now(),reason:"manual"});}catch(e){let t=e instanceof Error?e:new Error(String(e));this.log(`Failed to release wake lock: ${t.message}`,"error");}this.removeVisibilityHandler();}}async toggle(){return this.wakeLock&&!this.wakeLock.released?(await this.release(),false):this.acquire()}isActive(){return this.wakeLock!==null&&!this.wakeLock.released}async destroy(){await this.release(),this.removeVisibilityHandler();}handleRelease(e){this.wakeLock=null,this.log(`Wake lock released by ${e}`),this.onReleased?.({timestamp:Date.now(),reason:e});}setupVisibilityHandler(){this.visibilityHandler||(this.visibilityHandler=async()=>{document.visibilityState==="visible"&&this.shouldReacquire?(this.log("Page visible, attempting to reacquire wake lock"),await this.acquire()):document.visibilityState==="hidden"&&this.wakeLock&&this.log("Page hidden, wake lock will be released");},document.addEventListener("visibilitychange",this.visibilityHandler));}removeVisibilityHandler(){this.visibilityHandler&&(document.removeEventListener("visibilitychange",this.visibilityHandler),this.visibilityHandler=null);}log(e,t="log"){if(!this.debug&&t==="log")return;console[t](`[CloudSignal PWA WakeLock] ${e}`);}};var pe="CloudSignalPWA";var u="offlineQueue",T=class{constructor(e={}){this.db=null;this.isProcessing=false;this.onlineHandler=null;this.debug=e.debug??false,this.onRequestQueued=e.onRequestQueued,this.onRequestProcessed=e.onRequestProcessed,this.onQueueEmpty=e.onQueueEmpty,this.onError=e.onError,this.config={enabled:e.config?.enabled??true,maxQueueSize:e.config?.maxQueueSize??100,maxAge:e.config?.maxAge??1440*60*1e3,baseRetryDelay:e.config?.baseRetryDelay??1e3,maxRetryDelay:e.config?.maxRetryDelay??6e4,defaultMaxRetries:e.config?.defaultMaxRetries??5,processOnOnline:e.config?.processOnOnline??true};}async initialize(){if(!this.config.enabled){this.log("Offline queue is disabled");return}try{await this.openDatabase(),this.config.processOnOnline&&(this.onlineHandler=()=>{this.log("Network online, processing queue"),this.processQueue();},window.addEventListener("online",this.onlineHandler)),await this.cleanupOldRequests(),this.log("Offline queue manager initialized");}catch(e){let t=e instanceof Error?e:new Error(String(e));this.log(`Failed to initialize: ${t.message}`,"error"),this.onError?.(t);}}destroy(){this.onlineHandler&&(window.removeEventListener("online",this.onlineHandler),this.onlineHandler=null),this.db&&(this.db.close(),this.db=null);}async queueRequest(e,t,i={}){if(!this.config.enabled||!this.db)return null;try{(await this.getStats()).totalQueued>=this.config.maxQueueSize&&(this.log("Queue is full, removing oldest request"),await this.removeOldestRequest());let s={url:e,method:t,headers:i.headers,body:i.body,queuedAt:Date.now(),retryCount:0,maxRetries:i.maxRetries??this.config.defaultMaxRetries,requestType:i.requestType??"custom",priority:i.priority??0,metadata:i.metadata},o=await this.addToStore(s);return s.id=o,this.log(`Queued request: ${t} ${e} (id: ${o})`),this.onRequestQueued?.(s),o}catch(n){let s=n instanceof Error?n:new Error(String(n));return this.log(`Failed to queue request: ${s.message}`,"error"),this.onError?.(s),null}}async processQueue(){if(!this.config.enabled||!this.db||this.isProcessing)return [];if(!navigator.onLine)return this.log("Offline, skipping queue processing"),[];this.isProcessing=true;let e=[];try{let t=await this.getAllRequests();t.sort((n,s)=>n.priority!==s.priority?s.priority-n.priority:n.queuedAt-s.queuedAt),this.log(`Processing ${t.length} queued requests`);for(let n of t){if(!navigator.onLine){this.log("Went offline during processing, stopping");break}let s=await this.processRequest(n);e.push(s),s.success||!s.shouldRetry?await this.removeFromStore(n.id):await this.updateRetryCount(n),this.onRequestProcessed?.(s),await this.delay(100);}let i=await this.getQueueCount();i===0&&this.onQueueEmpty?.(),this.log(`Processed ${e.length} requests, ${i} remaining`);}catch(t){let i=t instanceof Error?t:new Error(String(t));this.log(`Error processing queue: ${i.message}`,"error"),this.onError?.(i);}finally{this.isProcessing=false;}return e}async processRequest(e){let t={id:e.id,success:false,shouldRetry:false};try{let i=await fetch(e.url,{method:e.method,headers:e.headers,body:e.body});t.statusCode=i.status,t.success=i.ok,i.ok||(t.shouldRetry=this.shouldRetryStatus(i.status,e),t.error=`HTTP ${i.status}`),this.log(`Request ${e.id}: ${t.success?"success":"failed"} (${i.status})`);}catch(i){let n=i instanceof Error?i:new Error(String(i));t.error=n.message,t.shouldRetry=e.retryCount<e.maxRetries,this.log(`Request ${e.id} failed: ${n.message}`,"error");}return t}shouldRetryStatus(e,t){return t.retryCount>=t.maxRetries?false:e>=500||e===408||e===429||e===0}async updateRetryCount(e){e.retryCount++;let i=this.db.transaction([u],"readwrite").objectStore(u);await this.promisifyRequest(i.put(e));}async getStats(){let e={totalQueued:0,byType:{registration:0,heartbeat:0,analytics:0,preferences:0,unregister:0,custom:0}};if(!this.db)return e;try{let t=await this.getAllRequests();e.totalQueued=t.length;for(let i of t)e.byType[i.requestType]++,(!e.oldestRequest||i.queuedAt<e.oldestRequest)&&(e.oldestRequest=i.queuedAt),(!e.newestRequest||i.queuedAt>e.newestRequest)&&(e.newestRequest=i.queuedAt);}catch(t){this.log(`Failed to get stats: ${t}`,"error");}return e}async clearQueue(){if(!this.db)return;let t=this.db.transaction([u],"readwrite").objectStore(u);await this.promisifyRequest(t.clear()),this.log("Queue cleared");}async hasPendingRequests(){return await this.getQueueCount()>0}async getQueueCount(){if(!this.db)return 0;let t=this.db.transaction([u],"readonly").objectStore(u);return this.promisifyRequest(t.count())}async openDatabase(){return new Promise((e,t)=>{let i=indexedDB.open(pe,2);i.onerror=()=>t(i.error),i.onsuccess=()=>{this.db=i.result,e();},i.onupgradeneeded=n=>{let s=n.target.result;if(!s.objectStoreNames.contains(u)){let o=s.createObjectStore(u,{keyPath:"id",autoIncrement:true});o.createIndex("queuedAt","queuedAt",{unique:false}),o.createIndex("requestType","requestType",{unique:false}),o.createIndex("priority","priority",{unique:false});}if(s.objectStoreNames.contains("badge")||s.createObjectStore("badge"),!s.objectStoreNames.contains("notifications")){let o=s.createObjectStore("notifications",{keyPath:"id",autoIncrement:true});o.createIndex("timestamp","timestamp",{unique:false}),o.createIndex("read","read",{unique:false});}s.objectStoreNames.contains("userPreferences")||s.createObjectStore("userPreferences"),s.objectStoreNames.contains("syncQueue")||s.createObjectStore("syncQueue",{keyPath:"id",autoIncrement:true}).createIndex("timestamp","timestamp",{unique:false});};})}async addToStore(e){let i=this.db.transaction([u],"readwrite").objectStore(u);return this.promisifyRequest(i.add(e))}async removeFromStore(e){let i=this.db.transaction([u],"readwrite").objectStore(u);await this.promisifyRequest(i.delete(e));}async getAllRequests(){let t=this.db.transaction([u],"readonly").objectStore(u);return this.promisifyRequest(t.getAll())}async removeOldestRequest(){let t=this.db.transaction([u],"readwrite").objectStore(u),n=t.index("queuedAt").openCursor();return new Promise((s,o)=>{n.onsuccess=l=>{let c=l.target.result;c&&t.delete(c.primaryKey),s();},n.onerror=()=>o(n.error);})}async cleanupOldRequests(){let e=Date.now()-this.config.maxAge,i=this.db.transaction([u],"readwrite").objectStore(u),n=i.index("queuedAt"),s=IDBKeyRange.upperBound(e),o=n.openCursor(s),l=0;return new Promise(c=>{o.onsuccess=d=>{let a=d.target.result;a?(i.delete(a.primaryKey),l++,a.continue()):(l>0&&this.log(`Cleaned up ${l} old requests`),c());},o.onerror=()=>c();})}promisifyRequest(e){return new Promise((t,i)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>i(e.error);})}delay(e){return new Promise(t=>setTimeout(t,e))}log(e,t="log"){if(!this.debug&&t==="log")return;console[t](`[CloudSignal PWA OfflineQueue] ${e}`);}};var A={en:{title:"Install this app",description:"Add this app to your home screen for the best experience.",buttonText:"Show me how",closeText:"Not now",step1:"Tap the Share button at the bottom of Safari",step2:'Scroll down and tap "Add to Home Screen"',step3:'Tap "Add" in the top right corner',shareIconHint:"Look for the Share icon",shareIconDescription:"It's the square with an arrow pointing up"},es:{title:"Instalar esta aplicaci\xF3n",description:"A\xF1ade esta aplicaci\xF3n a tu pantalla de inicio para una mejor experiencia.",buttonText:"Mostrar c\xF3mo",closeText:"Ahora no",step1:"Toca el bot\xF3n Compartir en la parte inferior de Safari",step2:'Despl\xE1zate hacia abajo y toca "A\xF1adir a pantalla de inicio"',step3:'Toca "A\xF1adir" en la esquina superior derecha',shareIconHint:"Busca el icono de Compartir",shareIconDescription:"Es el cuadrado con una flecha apuntando hacia arriba"},fr:{title:"Installer cette application",description:"Ajoutez cette application \xE0 votre \xE9cran d'accueil pour une meilleure exp\xE9rience.",buttonText:"Montrez-moi comment",closeText:"Pas maintenant",step1:"Appuyez sur le bouton Partager en bas de Safari",step2:`Faites d\xE9filer vers le bas et appuyez sur "Sur l'\xE9cran d'accueil"`,step3:'Appuyez sur "Ajouter" dans le coin sup\xE9rieur droit',shareIconHint:"Recherchez l'ic\xF4ne Partager",shareIconDescription:"C'est le carr\xE9 avec une fl\xE8che pointant vers le haut"},de:{title:"Diese App installieren",description:"F\xFCgen Sie diese App zu Ihrem Startbildschirm hinzu f\xFCr das beste Erlebnis.",buttonText:"Zeig mir wie",closeText:"Nicht jetzt",step1:"Tippen Sie auf die Teilen-Schaltfl\xE4che unten in Safari",step2:'Scrollen Sie nach unten und tippen Sie auf "Zum Home-Bildschirm"',step3:'Tippen Sie oben rechts auf "Hinzuf\xFCgen"',shareIconHint:"Suchen Sie das Teilen-Symbol",shareIconDescription:"Es ist das Quadrat mit einem Pfeil nach oben"},it:{title:"Installa questa app",description:"Aggiungi questa app alla schermata Home per un'esperienza migliore.",buttonText:"Mostrami come",closeText:"Non ora",step1:"Tocca il pulsante Condividi nella parte inferiore di Safari",step2:'Scorri verso il basso e tocca "Aggiungi a Home"',step3:`Tocca "Aggiungi" nell'angolo in alto a destra`,shareIconHint:"Cerca l'icona Condividi",shareIconDescription:"\xC8 il quadrato con una freccia che punta verso l'alto"},pt:{title:"Instalar este aplicativo",description:"Adicione este aplicativo \xE0 tela inicial para a melhor experi\xEAncia.",buttonText:"Mostre-me como",closeText:"Agora n\xE3o",step1:"Toque no bot\xE3o Compartilhar na parte inferior do Safari",step2:'Role para baixo e toque em "Adicionar \xE0 Tela de In\xEDcio"',step3:'Toque em "Adicionar" no canto superior direito',shareIconHint:"Procure o \xEDcone Compartilhar",shareIconDescription:"\xC9 o quadrado com uma seta apontando para cima"},nl:{title:"Installeer deze app",description:"Voeg deze app toe aan je beginscherm voor de beste ervaring.",buttonText:"Laat me zien hoe",closeText:"Niet nu",step1:"Tik op de Deel-knop onderaan Safari",step2:'Scroll naar beneden en tik op "Zet op beginscherm"',step3:'Tik rechtsboven op "Voeg toe"',shareIconHint:"Zoek het Deel-icoon",shareIconDescription:"Het is het vierkant met een pijl omhoog"},ru:{title:"\u0423\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435",description:"\u0414\u043E\u0431\u0430\u0432\u044C\u0442\u0435 \u044D\u0442\u043E \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u043D\u0430 \u0433\u043B\u0430\u0432\u043D\u044B\u0439 \u044D\u043A\u0440\u0430\u043D \u0434\u043B\u044F \u043B\u0443\u0447\u0448\u0435\u0433\u043E \u043E\u043F\u044B\u0442\u0430.",buttonText:"\u041F\u043E\u043A\u0430\u0436\u0438\u0442\u0435 \u043A\u0430\u043A",closeText:"\u041D\u0435 \u0441\u0435\u0439\u0447\u0430\u0441",step1:'\u041D\u0430\u0436\u043C\u0438\u0442\u0435 \u043A\u043D\u043E\u043F\u043A\u0443 "\u041F\u043E\u0434\u0435\u043B\u0438\u0442\u044C\u0441\u044F" \u0432\u043D\u0438\u0437\u0443 Safari',step2:'\u041F\u0440\u043E\u043A\u0440\u0443\u0442\u0438\u0442\u0435 \u0432\u043D\u0438\u0437 \u0438 \u043D\u0430\u0436\u043C\u0438\u0442\u0435 "\u041D\u0430 \u044D\u043A\u0440\u0430\u043D \u0414\u043E\u043C\u043E\u0439"',step3:'\u041D\u0430\u0436\u043C\u0438\u0442\u0435 "\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C" \u0432 \u043F\u0440\u0430\u0432\u043E\u043C \u0432\u0435\u0440\u0445\u043D\u0435\u043C \u0443\u0433\u043B\u0443',shareIconHint:'\u041D\u0430\u0439\u0434\u0438\u0442\u0435 \u0437\u043D\u0430\u0447\u043E\u043A "\u041F\u043E\u0434\u0435\u043B\u0438\u0442\u044C\u0441\u044F"',shareIconDescription:"\u042D\u0442\u043E \u043A\u0432\u0430\u0434\u0440\u0430\u0442 \u0441\u043E \u0441\u0442\u0440\u0435\u043B\u043A\u043E\u0439 \u0432\u0432\u0435\u0440\u0445"},zh:{title:"\u5B89\u88C5\u6B64\u5E94\u7528",description:"\u5C06\u6B64\u5E94\u7528\u6DFB\u52A0\u5230\u4E3B\u5C4F\u5E55\u4EE5\u83B7\u5F97\u6700\u4F73\u4F53\u9A8C\u3002",buttonText:"\u663E\u793A\u65B9\u6CD5",closeText:"\u6682\u65F6\u4E0D\u8981",step1:"\u70B9\u51FBSafari\u5E95\u90E8\u7684\u5206\u4EAB\u6309\u94AE",step2:'\u5411\u4E0B\u6EDA\u52A8\u5E76\u70B9\u51FB"\u6DFB\u52A0\u5230\u4E3B\u5C4F\u5E55"',step3:'\u70B9\u51FB\u53F3\u4E0A\u89D2\u7684"\u6DFB\u52A0"',shareIconHint:"\u627E\u5230\u5206\u4EAB\u56FE\u6807",shareIconDescription:"\u662F\u4E00\u4E2A\u5E26\u6709\u5411\u4E0A\u7BAD\u5934\u7684\u65B9\u6846"},ja:{title:"\u3053\u306E\u30A2\u30D7\u30EA\u3092\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB",description:"\u30DB\u30FC\u30E0\u753B\u9762\u306B\u8FFD\u52A0\u3057\u3066\u6700\u9AD8\u306E\u4F53\u9A13\u3092\u3002",buttonText:"\u65B9\u6CD5\u3092\u898B\u308B",closeText:"\u4ECA\u306F\u3057\u306A\u3044",step1:"Safari\u306E\u4E0B\u90E8\u306B\u3042\u308B\u5171\u6709\u30DC\u30BF\u30F3\u3092\u30BF\u30C3\u30D7",step2:"\u4E0B\u306B\u30B9\u30AF\u30ED\u30FC\u30EB\u3057\u3066\u300C\u30DB\u30FC\u30E0\u753B\u9762\u306B\u8FFD\u52A0\u300D\u3092\u30BF\u30C3\u30D7",step3:"\u53F3\u4E0A\u306E\u300C\u8FFD\u52A0\u300D\u3092\u30BF\u30C3\u30D7",shareIconHint:"\u5171\u6709\u30A2\u30A4\u30B3\u30F3\u3092\u63A2\u3057\u3066\u304F\u3060\u3055\u3044",shareIconDescription:"\u4E0A\u5411\u304D\u77E2\u5370\u306E\u3042\u308B\u56DB\u89D2\u5F62\u3067\u3059"},ko:{title:"\uC774 \uC571 \uC124\uCE58",description:"\uCD5C\uACE0\uC758 \uACBD\u9A13\uC744 \uC704\uD574 \uD648 \uD654\uBA74\uC5D0 \uCD94\uAC00\uD558\uC138\uC694.",buttonText:"\uBC29\uBC95 \uBCF4\uAE30",closeText:"\uB098\uC911\uC5D0",step1:"Safari \uD558\uB2E8\uC758 \uACF5\uC720 \uBC84\uD2BC\uC744 \uD0ED\uD558\uC138\uC694",step2:'\uC544\uB798\uB85C \uC2A4\uD06C\uB864\uD558\uC5EC "\uD648 \uD654\uBA74\uC5D0 \uCD94\uAC00"\uB97C \uD0ED\uD558\uC138\uC694',step3:'\uC624\uB978\uCABD \uC0C1\uB2E8\uC758 "\uCD94\uAC00"\uB97C \uD0ED\uD558\uC138\uC694',shareIconHint:"\uACF5\uC720 \uC544\uC774\uCF58\uC744 \uCC3E\uC73C\uC138\uC694",shareIconDescription:"\uC704\uCABD \uD654\uC0B4\uD45C\uAC00 \uC788\uB294 \uC0AC\uAC01\uD615\uC785\uB2C8\uB2E4"},ar:{title:"\u062A\u062B\u0628\u064A\u062A \u0647\u0630\u0627 \u0627\u0644\u062A\u0637\u0628\u064A\u0642",description:"\u0623\u0636\u0641 \u0647\u0630\u0627 \u0627\u0644\u062A\u0637\u0628\u064A\u0642 \u0625\u0644\u0649 \u0627\u0644\u0634\u0627\u0634\u0629 \u0627\u0644\u0631\u0626\u064A\u0633\u064A\u0629 \u0644\u0644\u062D\u0635\u0648\u0644 \u0639\u0644\u0649 \u0623\u0641\u0636\u0644 \u062A\u062C\u0631\u0628\u0629.",buttonText:"\u0623\u0631\u0646\u064A \u0643\u064A\u0641",closeText:"\u0644\u064A\u0633 \u0627\u0644\u0622\u0646",step1:"\u0627\u0636\u063A\u0637 \u0639\u0644\u0649 \u0632\u0631 \u0627\u0644\u0645\u0634\u0627\u0631\u0643\u0629 \u0641\u064A \u0623\u0633\u0641\u0644 Safari",step2:'\u0645\u0631\u0631 \u0644\u0623\u0633\u0641\u0644 \u0648\u0627\u0636\u063A\u0637 \u0639\u0644\u0649 "\u0625\u0636\u0627\u0641\u0629 \u0625\u0644\u0649 \u0627\u0644\u0634\u0627\u0634\u0629 \u0627\u0644\u0631\u0626\u064A\u0633\u064A\u0629"',step3:'\u0627\u0636\u063A\u0637 \u0639\u0644\u0649 "\u0625\u0636\u0627\u0641\u0629" \u0641\u064A \u0627\u0644\u0632\u0627\u0648\u064A\u0629 \u0627\u0644\u0639\u0644\u0648\u064A\u0629 \u0627\u0644\u064A\u0645\u0646\u0649',shareIconHint:"\u0627\u0628\u062D\u062B \u0639\u0646 \u0623\u064A\u0642\u0648\u0646\u0629 \u0627\u0644\u0645\u0634\u0627\u0631\u0643\u0629",shareIconDescription:"\u0647\u0648 \u0645\u0631\u0628\u0639 \u0645\u0639 \u0633\u0647\u0645 \u064A\u0634\u064A\u0631 \u0644\u0623\u0639\u0644\u0649"},he:{title:"\u05D4\u05EA\u05E7\u05DF \u05D0\u05E4\u05DC\u05D9\u05E7\u05E6\u05D9\u05D4 \u05D6\u05D5",description:"\u05D4\u05D5\u05E1\u05E3 \u05D0\u05E4\u05DC\u05D9\u05E7\u05E6\u05D9\u05D4 \u05D6\u05D5 \u05DC\u05DE\u05E1\u05DA \u05D4\u05D1\u05D9\u05EA \u05DC\u05D7\u05D5\u05D5\u05D9\u05D4 \u05D4\u05D8\u05D5\u05D1\u05D4 \u05D1\u05D9\u05D5\u05EA\u05E8.",buttonText:"\u05D4\u05E8\u05D0\u05D4 \u05DC\u05D9 \u05D0\u05D9\u05DA",closeText:"\u05DC\u05D0 \u05E2\u05DB\u05E9\u05D9\u05D5",step1:"\u05D4\u05E7\u05E9 \u05E2\u05DC \u05DB\u05E4\u05EA\u05D5\u05E8 \u05D4\u05E9\u05D9\u05EA\u05D5\u05E3 \u05D1\u05EA\u05D7\u05EA\u05D9\u05EA Safari",step2:'\u05D2\u05DC\u05D5\u05DC \u05DC\u05DE\u05D8\u05D4 \u05D5\u05D4\u05E7\u05E9 \u05E2\u05DC "\u05D4\u05D5\u05E1\u05E3 \u05DC\u05DE\u05E1\u05DA \u05D4\u05D1\u05D9\u05EA"',step3:'\u05D4\u05E7\u05E9 \u05E2\u05DC "\u05D4\u05D5\u05E1\u05E3" \u05D1\u05E4\u05D9\u05E0\u05D4 \u05D4\u05D9\u05DE\u05E0\u05D9\u05EA \u05D4\u05E2\u05DC\u05D9\u05D5\u05E0\u05D4',shareIconHint:"\u05D7\u05E4\u05E9 \u05D0\u05EA \u05E1\u05DE\u05DC \u05D4\u05E9\u05D9\u05EA\u05D5\u05E3",shareIconDescription:"\u05D6\u05D4 \u05E8\u05D9\u05D1\u05D5\u05E2 \u05E2\u05DD \u05D7\u05E5 \u05DE\u05E6\u05D1\u05D9\u05E2 \u05DC\u05DE\u05E2\u05DC\u05D4"},hi:{title:"\u092F\u0939 \u0910\u092A \u0907\u0902\u0938\u094D\u091F\u0949\u0932 \u0915\u0930\u0947\u0902",description:"\u092C\u0947\u0939\u0924\u0930 \u0905\u0928\u0941\u092D\u0935 \u0915\u0947 \u0932\u093F\u090F \u0907\u0938 \u0910\u092A \u0915\u094B \u0939\u094B\u092E \u0938\u094D\u0915\u094D\u0930\u0940\u0928 \u092A\u0930 \u091C\u094B\u0921\u093C\u0947\u0902\u0964",buttonText:"\u092E\u0941\u091D\u0947 \u0926\u093F\u0916\u093E\u0913 \u0915\u0948\u0938\u0947",closeText:"\u0905\u092D\u0940 \u0928\u0939\u0940\u0902",step1:"Safari \u0915\u0947 \u0928\u0940\u091A\u0947 \u0936\u0947\u092F\u0930 \u092C\u091F\u0928 \u092A\u0930 \u091F\u0948\u092A \u0915\u0930\u0947\u0902",step2:'\u0928\u0940\u091A\u0947 \u0938\u094D\u0915\u094D\u0930\u0949\u0932 \u0915\u0930\u0947\u0902 \u0914\u0930 "\u0939\u094B\u092E \u0938\u094D\u0915\u094D\u0930\u0940\u0928 \u092E\u0947\u0902 \u091C\u094B\u0921\u093C\u0947\u0902" \u092A\u0930 \u091F\u0948\u092A \u0915\u0930\u0947\u0902',step3:'\u090A\u092A\u0930 \u0926\u093E\u090F\u0902 \u0915\u094B\u0928\u0947 \u092E\u0947\u0902 "\u091C\u094B\u0921\u093C\u0947\u0902" \u092A\u0930 \u091F\u0948\u092A \u0915\u0930\u0947\u0902',shareIconHint:"\u0936\u0947\u092F\u0930 \u0906\u0907\u0915\u0928 \u0916\u094B\u091C\u0947\u0902",shareIconDescription:"\u092F\u0939 \u090A\u092A\u0930 \u0915\u0940 \u0913\u0930 \u0924\u0940\u0930 \u0935\u093E\u0932\u093E \u0935\u0930\u094D\u0917 \u0939\u0948"},tr:{title:"Bu uygulamay\u0131 y\xFCkle",description:"En iyi deneyim i\xE7in bu uygulamay\u0131 ana ekran\u0131n\u0131za ekleyin.",buttonText:"Nas\u0131l yap\u0131l\u0131r g\xF6ster",closeText:"\u015Eimdi de\u011Fil",step1:"Safari'nin alt\u0131ndaki Payla\u015F d\xFC\u011Fmesine dokunun",step2:'A\u015Fa\u011F\u0131 kayd\u0131r\u0131n ve "Ana Ekrana Ekle"ye dokunun',step3:'Sa\u011F \xFCst k\xF6\u015Fedeki "Ekle"ye dokunun',shareIconHint:"Payla\u015F simgesini aray\u0131n",shareIconDescription:"Yukar\u0131 bakan oklu bir karedir"},pl:{title:"Zainstaluj t\u0119 aplikacj\u0119",description:"Dodaj t\u0119 aplikacj\u0119 do ekranu g\u0142\xF3wnego, aby uzyska\u0107 najlepsze wra\u017Cenia.",buttonText:"Poka\u017C mi jak",closeText:"Nie teraz",step1:"Dotknij przycisku Udost\u0119pnij na dole Safari",step2:'Przewi\u0144 w d\xF3\u0142 i dotknij "Dodaj do ekranu pocz\u0105tkowego"',step3:'Dotknij "Dodaj" w prawym g\xF3rnym rogu',shareIconHint:"Poszukaj ikony Udost\u0119pnij",shareIconDescription:"To kwadrat ze strza\u0142k\u0105 skierowan\u0105 w g\xF3r\u0119"},sv:{title:"Installera denna app",description:"L\xE4gg till denna app p\xE5 hemsk\xE4rmen f\xF6r b\xE4sta upplevelse.",buttonText:"Visa mig hur",closeText:"Inte nu",step1:"Tryck p\xE5 Dela-knappen l\xE4ngst ner i Safari",step2:'Scrolla ner och tryck p\xE5 "L\xE4gg till p\xE5 hemsk\xE4rmen"',step3:'Tryck p\xE5 "L\xE4gg till" i \xF6vre h\xF6gra h\xF6rnet',shareIconHint:"Leta efter Dela-ikonen",shareIconDescription:"Det \xE4r kvadraten med en pil som pekar upp\xE5t"},da:{title:"Installer denne app",description:"F\xF8j denne app til din startsk\xE6rm for den bedste oplevelse.",buttonText:"Vis mig hvordan",closeText:"Ikke nu",step1:"Tryk p\xE5 Del-knappen nederst i Safari",step2:'Rul ned og tryk p\xE5 "F\xF8j til hjemmesk\xE6rm"',step3:'Tryk p\xE5 "Tilf\xF8j" i \xF8verste h\xF8jre hj\xF8rne',shareIconHint:"Kig efter Del-ikonet",shareIconDescription:"Det er firkanten med en pil, der peger opad"},no:{title:"Installer denne appen",description:"Legg til denne appen p\xE5 startskjermen for best opplevelse.",buttonText:"Vis meg hvordan",closeText:"Ikke n\xE5",step1:"Trykk p\xE5 Del-knappen nederst i Safari",step2:'Rull ned og trykk p\xE5 "Legg til p\xE5 Hjem-skjerm"',step3:'Trykk p\xE5 "Legg til" \xF8verst til h\xF8yre',shareIconHint:"Se etter Del-ikonet",shareIconDescription:"Det er firkanten med en pil som peker oppover"},fi:{title:"Asenna t\xE4m\xE4 sovellus",description:"Lis\xE4\xE4 t\xE4m\xE4 sovellus aloitusn\xE4yt\xF6lle parhaan kokemuksen saamiseksi.",buttonText:"N\xE4yt\xE4 miten",closeText:"Ei nyt",step1:"Napauta Jaa-painiketta Safarin alareunassa",step2:'Vierit\xE4 alas ja napauta "Lis\xE4\xE4 Koti-valikkoon"',step3:'Napauta "Lis\xE4\xE4" oikeassa yl\xE4kulmassa',shareIconHint:"Etsi Jaa-kuvake",shareIconDescription:"Se on neli\xF6, jossa on yl\xF6sp\xE4in osoittava nuoli"}};function C(){let r=navigator.language?.toLowerCase()||"en",e=r.split("-")[0];return e in A?e:r.startsWith("zh")?"zh":r.startsWith("pt")?"pt":r.startsWith("no")||r.startsWith("nb")||r.startsWith("nn")?"no":"en"}var N="cloudsignal_pwa_ios_banner_dismissed",m="cloudsignal-ios-install-banner",fe={backgroundColor:"#ffffff",textColor:"#1a1a1a",buttonBackgroundColor:"#007AFF",buttonTextColor:"#ffffff",borderRadius:"16px",boxShadow:"0 4px 24px rgba(0, 0, 0, 0.15)"},B=class{constructor(e={}){this.bannerElement=null;this.overlayElement=null;this.debug=e.debug??false,this.onShow=e.onShow,this.onDismiss=e.onDismiss,this.onInstallClick=e.onInstallClick,this.language=e.language??C();let t=A[this.language];this.strings={...t,...e.customStrings},this.config={enabled:e.config?.enabled??true,title:e.config?.title??this.strings.title,description:e.config?.description??this.strings.description,buttonText:e.config?.buttonText??this.strings.buttonText,closeText:e.config?.closeText??this.strings.closeText,iconUrl:e.config?.iconUrl,customStyles:e.config?.customStyles??{},showDelay:e.config?.showDelay??3e3,dismissalDays:e.config?.dismissalDays??7,position:e.config?.position??"bottom"},this.styles={...fe,...this.config.customStyles},this.log(`Initialized with language: ${this.language}`);}getLanguage(){return this.language}getStrings(){return this.strings}setLanguage(e){this.language=e;let t=A[e];this.strings={...t},this.config.title=this.strings.title,this.config.description=this.strings.description,this.config.buttonText=this.strings.buttonText,this.config.closeText=this.strings.closeText,this.log(`Language changed to: ${e}`);}getState(){return {isVisible:this.bannerElement!==null,wasDismissed:this.wasDismissed(),isEligible:this.isEligible(),isInstalled:this.isInstalled()}}isEligible(){let e=navigator.userAgent;return !(!(/iPad|iPhone|iPod/.test(e)&&!window.MSStream)||!(/Safari/i.test(e)&&!/Chrome|CriOS|FxiOS|EdgiOS/i.test(e))||this.isInstalled())}isInstalled(){return !!(navigator.standalone===true||window.matchMedia("(display-mode: standalone)").matches)}wasDismissed(){try{let e=localStorage.getItem(N);if(!e)return !1;let t=parseInt(e,10);return (Date.now()-t)/(1e3*60*60*24)>this.config.dismissalDays?(localStorage.removeItem(N),!1):!0}catch{return false}}async show(){return this.config.enabled?this.isEligible()?this.wasDismissed()?(this.log("Banner was dismissed recently"),false):this.bannerElement?(this.log("Banner already visible"),true):(this.config.showDelay>0&&await new Promise(e=>setTimeout(e,this.config.showDelay)),!this.isEligible()||this.wasDismissed()?false:(this.createBanner(),this.log("Banner shown"),this.onShow?.(),true)):(this.log("Device not eligible for iOS install banner"),false):(this.log("Banner is disabled"),false)}hide(){this.bannerElement&&(this.bannerElement.remove(),this.bannerElement=null),this.overlayElement&&(this.overlayElement.remove(),this.overlayElement=null);}dismiss(){this.hide();try{localStorage.setItem(N,Date.now().toString());}catch{}this.log("Banner dismissed"),this.onDismiss?.();}resetDismissal(){try{localStorage.removeItem(N);}catch{}}getInstallSteps(){return [this.strings.step1,this.strings.step2,this.strings.step3]}createBanner(){this.overlayElement=document.createElement("div"),this.overlayElement.id=`${m}-overlay`,this.overlayElement.style.cssText=`
6
+ var ce=Object.defineProperty;var ue=(r,e)=>()=>(r&&(e=r(r=0)),e);var de=(r,e)=>{for(var t in e)ce(r,t,{get:e[t],enumerable:true});};var te={};de(te,{generateAuthHeaders:()=>U,generateHMACSignature:()=>Q,isValidUUID:()=>m,makeAuthenticatedRequest:()=>ee});function ve(r){return Array.from(new Uint8Array(r)).map(e=>e.toString(16).padStart(2,"0")).join("")}function ye(r){return btoa(String.fromCharCode(...new Uint8Array(r)))}async function Q(r,e,t,i,n,s=""){let o=new TextEncoder,l,c;try{let g=n.startsWith("http")?new URL(n):new URL(n,"https://pwa.cloudsignal.app");l=g.pathname,c=g.search?g.search.slice(1):"";}catch{let g=n.indexOf("?");g>-1?(l=n.slice(0,g),c=n.slice(g+1)):(l=n,c="");}let d=[i.toUpperCase(),l,c,e,t];if(s&&s.length>0){let g=await crypto.subtle.digest("SHA-256",o.encode(s)),w=ve(g);d.push(w);}let a=d.join(`
7
+ `),b=await crypto.subtle.importKey("raw",o.encode(r),{name:"HMAC",hash:"SHA-256"},false,["sign"]),H=await crypto.subtle.sign("HMAC",b,o.encode(a));return ye(H)}async function U(r,e,t,i,n){let s=Math.floor(Date.now()/1e3).toString(),o=await Q(e,r,s,t,i,n||"");return {"X-CloudSignal-Organization-ID":r,"X-CloudSignal-Timestamp":s,"X-CloudSignal-Signature":o,"Content-Type":"application/json"}}async function ee(r,e,t,i,n){let s=n?JSON.stringify(n):void 0,o=await U(r,e,t,i,s),l={method:t,headers:o};return s&&(l.body=s),fetch(i,l)}function m(r){return !r||typeof r!="string"?false:/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(r)}var P=ue(()=>{});async function M(){try{let r=[];r.push(`${screen.width}x${screen.height}`),r.push(`${screen.colorDepth}`),r.push(Intl.DateTimeFormat().resolvedOptions().timeZone),r.push(navigator.language),r.push(navigator.platform),navigator.hardwareConcurrency&&r.push(navigator.hardwareConcurrency.toString()),navigator.deviceMemory&&r.push(navigator.deviceMemory.toString());let e=ge();e&&r.push(e);let t=he();t&&r.push(t);let i=await pe();i&&r.push(i);let n=r.join("|");return await fe(n)}catch(r){return console.warn("Browser fingerprint generation failed:",r),null}}function ge(){try{let r=document.createElement("canvas"),e=r.getContext("2d");return e?(r.width=200,r.height=50,e.textBaseline="alphabetic",e.fillStyle="#f60",e.fillRect(125,1,62,20),e.fillStyle="#069",e.font="11pt Arial",e.fillText("CloudSignal PWA",2,15),e.fillStyle="rgba(102, 204, 0, 0.7)",e.font="18pt Arial",e.fillText("CloudSignal PWA",4,45),r.toDataURL().slice(-50)):null}catch{return null}}function he(){try{let r=document.createElement("canvas"),e=r.getContext("webgl")||r.getContext("experimental-webgl");if(!e)return null;let t=e,i=t.getExtension("WEBGL_debug_renderer_info");if(i){let o=t.getParameter(i.UNMASKED_VENDOR_WEBGL),l=t.getParameter(i.UNMASKED_RENDERER_WEBGL);return `${o}~${l}`}let n=t.getParameter(t.VENDOR),s=t.getParameter(t.RENDERER);return `${n}~${s}`}catch{return null}}async function pe(){try{let r=window.AudioContext||window.webkitAudioContext;if(!r)return null;let e=new r,t=e.createOscillator(),i=e.createAnalyser(),n=e.createGain(),s=e.createScriptProcessor(4096,1,1);n.gain.value=0,t.type="triangle",t.connect(i),i.connect(s),s.connect(n),n.connect(e.destination),t.start(0),await new Promise(c=>setTimeout(c,100));let o=new Uint8Array(i.frequencyBinCount);i.getByteFrequencyData(o);let l=0;for(let c=0;c<o.length;c++)l+=o[c];return t.stop(),await e.close(),l.toString(36)}catch{return null}}async function fe(r){let t=new TextEncoder().encode(r),i=await crypto.subtle.digest("SHA-256",t);return Array.from(new Uint8Array(i)).map(s=>s.toString(16).padStart(2,"0")).join("")}function _(r,e,t,i,n){return [r,e,t,i,n].filter(Boolean).join("_").replace(/[^a-zA-Z0-9_]/g,"_")}var p=class{constructor(){this.cachedInfo=null;}getDeviceInfo(){if(this.cachedInfo)return this.cachedInfo;let e=this.getPlatformInfo(),t=this.getScreenInfo(),i=this.getNetworkInfo(),n=this.getCapabilities(),s={os:e.os,osVersion:e.osVersion,deviceType:e.deviceType,deviceModel:e.deviceModel,isMobile:this.isMobile(),isTablet:this.isTablet(),isDesktop:this.isDesktop(),isWebView:e.isWebView,browser:e.browser,browserVersion:e.browserVersion,isIOS:e.os==="iOS",isAndroid:e.os==="Android",isMacOS:e.os==="macOS",isWindows:e.os==="Windows",isLinux:e.os==="Linux",screenWidth:t.width,screenHeight:t.height,pixelRatio:t.pixelRatio,supportLevel:n.supportLevel,hasNotificationPermission:n.notifications,hasPushManager:n.push,hasServiceWorker:n.serviceWorker,hasShareAPI:n.share,hasBadgeAPI:n.badge,notificationPermission:this.getNotificationPermission(),isOnline:i.isOnline,connectionType:i.connectionType,platformIcon:this.getPlatformIcon(e.os,e.deviceType),userAgent:e.userAgent,trackingId:_(e.os,e.osVersion,e.browser,e.browserVersion,e.deviceModel)};return this.cachedInfo=s,s}clearCache(){this.cachedInfo=null;}getPlatformInfo(){let e=navigator.userAgent,t=navigator.platform,i="Unknown",n="Unknown",s="Unknown",o="Unknown",l="Unknown",c="Unknown",d=false;if(/iPad|iPhone|iPod/.test(e)&&!window.MSStream){i="iOS";let a=e.match(/OS (\d+)_(\d+)_?(\d+)?/);a&&(n=`${a[1]}.${a[2]}${a[3]?"."+a[3]:""}`),/iPad/.test(e)?(l="iPad",c=this.detectiPadModel(e)):/iPhone/.test(e)?(l="iPhone",c=this.detectiPhoneModel(e)):/iPod/.test(e)&&(l="iPod",c="iPod Touch");}else if(/Android/.test(e)){i="Android";let a=e.match(/Android (\d+\.?\d*\.?\d*)/);a&&(n=a[1]);let b=this.detectAndroidDevice(e);l=b.type,c=b.model;}else if(/Mac/.test(t)){i="macOS";let a=e.match(/Mac OS X (\d+)[_.](\d+)[_.]?(\d+)?/);a&&(n=`${a[1]}.${a[2]}${a[3]?"."+a[3]:""}`),l="Desktop",c="Mac";}else if(/Win/.test(t)){i="Windows";let a=e.match(/Windows NT (\d+\.\d+)/);a&&(n=this.getWindowsVersion(a[1])),l="Desktop",c="PC";}else /Linux/.test(t)&&(i="Linux",l="Desktop",c="Linux PC",/Ubuntu/.test(e)?n="Ubuntu":/Fedora/.test(e)?n="Fedora":/Debian/.test(e)&&(n="Debian"));if(/Chrome/.test(e)&&!/Edg/.test(e)&&!/OPR/.test(e)){s="Chrome";let a=e.match(/Chrome\/(\d+\.?\d*\.?\d*\.?\d*)/);a&&(o=a[1]);}else if(/Safari/.test(e)&&!/Chrome/.test(e)){s="Safari";let a=e.match(/Version\/(\d+\.?\d*\.?\d*)/);a&&(o=a[1]);}else if(/Firefox/.test(e)){s="Firefox";let a=e.match(/Firefox\/(\d+\.?\d*\.?\d*)/);a&&(o=a[1]);}else if(/Edg/.test(e)){s="Edge";let a=e.match(/Edg\/(\d+\.?\d*\.?\d*\.?\d*)/);a&&(o=a[1]);}else if(/OPR/.test(e)||/Opera/.test(e)){s="Opera";let a=e.match(/(?:OPR|Opera)\/(\d+\.?\d*\.?\d*)/);a&&(o=a[1]);}else if(/SamsungBrowser/.test(e)){s="Samsung Internet";let a=e.match(/SamsungBrowser\/(\d+\.?\d*)/);a&&(o=a[1]);}return d=this.detectWebView(e),{os:i,osVersion:n,browser:s,browserVersion:o,deviceType:l,deviceModel:c,isWebView:d,userAgent:e}}getScreenInfo(){return {width:screen.width,height:screen.height,pixelRatio:window.devicePixelRatio||1,orientation:screen.width>screen.height?"landscape":"portrait"}}getNetworkInfo(){let e=navigator.connection;return {isOnline:navigator.onLine,connectionType:e?.effectiveType||"unknown",effectiveType:e?.effectiveType,downlink:e?.downlink,rtt:e?.rtt,saveData:e?.saveData}}getCapabilities(){let e={serviceWorker:"serviceWorker"in navigator,push:"PushManager"in window,notifications:"Notification"in window,backgroundSync:"serviceWorker"in navigator&&"SyncManager"in window,badge:"setAppBadge"in navigator,share:"share"in navigator,shareTarget:"launchQueue"in window,fileSystemAccess:"showOpenFilePicker"in window,contactPicker:"ContactsManager"in window,periodicSync:"serviceWorker"in navigator&&"PeriodicSyncManager"in window,supportLevel:"none"};return e.serviceWorker&&e.push&&e.notifications?e.supportLevel="full":e.serviceWorker&&e.notifications?e.supportLevel="partial":e.serviceWorker&&(e.supportLevel="basic"),e}getNotificationPermission(){return "Notification"in window?Notification.permission:"denied"}isMobile(){let e=navigator.userAgent;return /Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(e)}isTablet(){let e=navigator.userAgent;return /iPad|Android(?!.*Mobile)|Tablet/i.test(e)}isDesktop(){return !this.isMobile()&&!this.isTablet()}getPlatformIcon(e,t){return e==="iOS"||t==="iPhone"||t==="iPad"?"\u{1F4F1}":e==="Android"?"\u{1F916}":e==="macOS"?"\u{1F4BB}":"\u{1F5A5}\uFE0F"}detectiPhoneModel(e){let t=screen.height,i=screen.width;return t===932||i===932?"iPhone 15 Pro Max/14 Pro Max":t===896||i===896?"iPhone 15 Pro/14 Pro/11 Pro Max":t===852||i===852?"iPhone 15/14":t===844||i===844?"iPhone 13/12":t===812||i===812?"iPhone X/XS/11 Pro":t===736||i===736?"iPhone 8 Plus":t===667||i===667?"iPhone 8/SE":t===568||i===568?"iPhone SE (1st)":"iPhone"}detectiPadModel(e){let t=screen.width,i=screen.height,n=window.devicePixelRatio||1;return (i===1366||t===1366)&&n===2?'iPad Pro 12.9"':(i===1194||t===1194)&&n===2?'iPad Pro 11"':(i===1180||t===1180)&&n===2?"iPad Air/10th Gen":(i===1133||t===1133)&&n===2?"iPad mini":"iPad"}detectAndroidDevice(e){let t="Phone",i="Android Device";if(/Tablet|SM-T|Tab|GT-P|MediaPad/i.test(e)&&(t="Tablet"),/SM-S9\d{2}/i.test(e))i="Samsung Galaxy S24";else if(/SM-S91\d/i.test(e))i="Samsung Galaxy S23";else if(/SM-S90\d/i.test(e))i="Samsung Galaxy S22";else if(/SM-G99\d/i.test(e))i="Samsung Galaxy S21";else if(/SM-N9\d{2}/i.test(e))i="Samsung Galaxy Note";else if(/SM-A\d{2}/i.test(e))i="Samsung Galaxy A Series";else if(/SM-/i.test(e)){let n=e.match(/SM-[A-Z]\d+/i);n&&(i=`Samsung ${n[0]}`);}if(/Pixel 8/i.test(e)?i="Google Pixel 8":/Pixel 7/i.test(e)?i="Google Pixel 7":/Pixel 6/i.test(e)?i="Google Pixel 6":/Pixel/i.test(e)&&(i="Google Pixel"),/OnePlus/i.test(e)){let n=e.match(/OnePlus[\s]?(\w+)/i);i=n?`OnePlus ${n[1]}`:"OnePlus";}return /Xiaomi|Redmi|POCO|Mi\s/i.test(e)&&(/Redmi/i.test(e)?i="Xiaomi Redmi":/POCO/i.test(e)?i="Xiaomi POCO":i="Xiaomi"),/HUAWEI|Honor/i.test(e)&&(i=/Honor/i.test(e)?"Honor":"Huawei"),/OPPO/i.test(e)&&(i="OPPO"),/vivo/i.test(e)&&(i="Vivo"),{type:t,model:i}}getWindowsVersion(e){return {"10.0":"Windows 10/11","6.3":"Windows 8.1","6.2":"Windows 8","6.1":"Windows 7","6.0":"Windows Vista","5.1":"Windows XP"}[e]||`Windows NT ${e}`}detectWebView(e){return !!(/FBAN|FBAV/i.test(e)||/Instagram/i.test(e)||/LinkedIn/i.test(e)||/Twitter/i.test(e)||/MicroMessenger/i.test(e)||/Snapchat/i.test(e)||/BytedanceWebview|TikTok/i.test(e)||/wv|WebView/i.test(e))}},me=new p;var S=class{constructor(e={}){this.registration=null;this.config={path:"/service-worker.js",scope:"/",autoRegister:true,updateBehavior:"auto",...e.config},this.debug=e.debug??false,this.onRegistered=e.onRegistered,this.onUpdated=e.onUpdated,this.onError=e.onError;}isSupported(){return "serviceWorker"in navigator}getRegistration(){return this.registration}async register(){if(!this.isSupported())return this.log("Service workers not supported"),null;try{let{path:e,scope:t}=this.getPathAndScope();this.log(`Registering service worker: ${e} with scope ${t}`);let i=await navigator.serviceWorker.register(e,{scope:t});return this.registration=i,i.addEventListener("updatefound",()=>{this.handleUpdateFound(i);}),await navigator.serviceWorker.ready,this.log("Service worker registered successfully"),this.onRegistered?.(i),i}catch(e){let t=e instanceof Error?e:new Error(String(e));return this.log(`Service worker registration failed: ${t.message}`,"error"),this.onError?.(t),null}}async unregister(){if(!this.registration)return false;try{let e=await this.registration.unregister();return e&&(this.registration=null,this.log("Service worker unregistered")),e}catch(e){return this.log(`Failed to unregister service worker: ${e}`,"error"),false}}async checkForUpdate(){if(this.registration)try{await this.registration.update(),this.log("Checked for service worker update");}catch(e){this.log(`Failed to check for update: ${e}`,"error");}}async waitForReady(e=5e3){if(!this.isSupported())return null;let t=new Promise(i=>{setTimeout(()=>i(null),e);});try{let i=await Promise.race([navigator.serviceWorker.ready,t]);return i&&(this.registration=i),i}catch{return null}}postMessage(e){if(!this.registration?.active){this.log("No active service worker to send message to","warn");return}this.registration.active.postMessage(e);}clearBadge(){this.postMessage({type:"CLEAR_BADGE"});}setBadge(e){this.postMessage({type:"SET_BADGE",count:e});}handleUpdateFound(e){let t=e.installing;t&&t.addEventListener("statechange",()=>{t.state==="installed"&&navigator.serviceWorker.controller&&(this.log("New service worker available"),this.config.updateBehavior==="auto"&&t.postMessage({type:"SKIP_WAITING"}),this.onUpdated?.(e));});}getPathAndScope(){if(this.config.path&&this.config.scope)return {path:this.config.path,scope:this.config.scope};try{let e=window.location.pathname||"/";if(e.startsWith("/version-live"))return {path:"/version-live/service-worker.js",scope:"/version-live/"};if(e.startsWith("/version-test"))return {path:"/version-test/service-worker.js",scope:"/version-test/"}}catch{}return {path:this.config.path||"/service-worker.js",scope:this.config.scope||"/"}}log(e,t="log"){if(!this.debug)return;console[t](`[CloudSignal PWA SW] ${e}`);}};var k=class{constructor(e={}){this.deferredPrompt=null;this.isInstalled=false;this.debug=e.debug??false,this.onInstallAvailable=e.onInstallAvailable,this.onInstallAccepted=e.onInstallAccepted,this.onInstallDismissed=e.onInstallDismissed,this.onInstalled=e.onInstalled,this.detectInstallationStatus();}initialize(){window.addEventListener("beforeinstallprompt",e=>{e.preventDefault(),this.deferredPrompt=e,this.log("Install prompt available"),this.onInstallAvailable?.(this.deferredPrompt);}),window.addEventListener("appinstalled",()=>{this.isInstalled=true,this.deferredPrompt=null,this.log("PWA was installed"),this.onInstalled?.();}),window.matchMedia&&window.matchMedia("(display-mode: standalone)").addEventListener("change",t=>{t.matches&&(this.isInstalled=true,this.log("App is now running in standalone mode"));});}getState(){let e=this.getDisplayMode(),t=this.isIOSDevice(),i=this.isSafariBrowser();return {isInstalled:this.isInstalled||e!=="browser",canBeInstalled:this.deferredPrompt!==null,needsManualInstall:t&&i&&!this.isInstalled,showManualInstructions:t&&i&&!this.isInstalled,installSteps:this.getInstallSteps(),displayMode:e}}async showInstallPrompt(){if(!this.deferredPrompt)return this.log("No install prompt available","warn"),{accepted:false,outcome:"dismissed"};try{await this.deferredPrompt.prompt();let{outcome:e,platform:t}=await this.deferredPrompt.userChoice,i={accepted:e==="accepted",outcome:e,platform:t};return e==="accepted"?(this.log("User accepted install prompt"),this.isInstalled=!0,this.onInstallAccepted?.(i)):(this.log("User dismissed install prompt"),this.onInstallDismissed?.(i)),this.deferredPrompt=null,i}catch(e){return this.log(`Error showing install prompt: ${e}`,"error"),{accepted:false,outcome:"dismissed"}}}canInstall(){return this.deferredPrompt!==null}isPWAInstalled(){return this.isInstalled||this.getDisplayMode()!=="browser"}getDisplayMode(){return window.matchMedia("(display-mode: standalone)").matches?"standalone":window.matchMedia("(display-mode: minimal-ui)").matches?"minimal-ui":window.matchMedia("(display-mode: fullscreen)").matches?"fullscreen":navigator.standalone===true?"standalone":"browser"}getInstallSteps(){return this.isIOSDevice()?["Tap the Share button in Safari",'Scroll down and tap "Add to Home Screen"','Tap "Add" to install the app']:this.isAndroidDevice()?["Tap the menu (three dots) in your browser",'Tap "Install App" or "Add to Home Screen"',"Follow the prompts to install"]:["Click the install icon in the address bar","Or use the browser menu to install the app",'Click "Install" when prompted']}detectInstallationStatus(){if(this.getDisplayMode()!=="browser"){this.isInstalled=true;return}if(navigator.standalone===true){this.isInstalled=true;return}"getInstalledRelatedApps"in navigator&&navigator.getInstalledRelatedApps().then(e=>{e.length>0&&(this.isInstalled=true);}).catch(()=>{});}isIOSDevice(){return /iPad|iPhone|iPod/.test(navigator.userAgent)&&!window.MSStream}isAndroidDevice(){return /Android/i.test(navigator.userAgent)}isSafariBrowser(){let e=navigator.userAgent;return /Safari/i.test(e)&&!/Chrome|CriOS|FxiOS/i.test(e)}log(e,t="log"){if(!this.debug)return;console[t](`[CloudSignal PWA Install] ${e}`);}};P();function F(r,e){return {Authorization:`Bearer ${r}`,"X-CloudSignal-Organization-ID":e,"Content-Type":"application/json"}}async function ie(r,e,t,i){let n=i?JSON.stringify(i):void 0,s=F(r.token,r.organizationId),o={method:e,headers:s};n&&(o.body=n);let l=await fetch(t,o);if(l.status===401&&r.onTokenExpired)try{let c=await r.onTokenExpired();c&&(s=F(c,r.organizationId),o.headers=s,l=await fetch(t,o));}catch(c){console.warn("[CloudSignal PWA] Token refresh failed:",c);}return l}function v(r){if(r.userToken)return {mode:"jwt",organizationId:r.organizationId,userToken:r.userToken,onTokenExpired:r.onTokenExpired};if(!r.organizationPublishableKey)throw new Error("Either userToken or organizationPublishableKey is required");return {mode:"hmac",organizationId:r.organizationId,organizationPublishableKey:r.organizationPublishableKey}}async function h(r,e,t,i,n){if(r.mode==="jwt"){if(!r.userToken)throw new Error("userToken required for JWT auth mode");return ie({token:r.userToken,organizationId:r.organizationId,onTokenExpired:n||r.onTokenExpired},e,t,i)}let{makeAuthenticatedRequest:s}=await Promise.resolve().then(()=>(P(),te));if(!r.organizationPublishableKey)throw new Error("organizationPublishableKey required for HMAC auth mode");return s(r.organizationId,r.organizationPublishableKey,e,t,i)}function y(r,e){return r.mode!=="jwt"?{mode:"jwt",organizationId:r.organizationId,userToken:e,onTokenExpired:r.onTokenExpired}:{...r,userToken:e}}var K="cloudsignal_pwa_";function N(r,e){try{let t=`${K}${r}`,i=localStorage.getItem(t);return i===null?e??null:JSON.parse(i)}catch{return e??null}}function x(r,e){try{let t=`${K}${r}`;return localStorage.setItem(t,JSON.stringify(e)),!0}catch{return false}}function ne(r){try{let e=`${K}${r}`;return localStorage.removeItem(e),!0}catch{return false}}function G(r,e){let t=`registration_${r}_${e}`;return N(t)}function X(r,e,t){let i=`registration_${r}_${e}`;return x(i,t)}function L(r,e){let t=`registration_${r}_${e}`;return ne(t)}function re(r,e){let t=`install_registered_${r}_${e}`;return N(t)===true}function se(r,e,t){let i=`install_registered_${r}_${e}`;return x(`install_id_${r}_${e}`,t),x(i,true)}function oe(r,e){let t=`last_identity_${r}_${e}`;return N(t)}function ae(r,e,t){let i=`last_identity_${r}_${e}`;return x(i,t)}var V=class{constructor(e="CloudSignalPWA",t=1){this.db=null;this.dbName=e,this.dbVersion=t;}async init(){return new Promise((e,t)=>{let i=indexedDB.open(this.dbName,this.dbVersion);i.onerror=()=>t(i.error),i.onsuccess=()=>{this.db=i.result,e(this.db);},i.onupgradeneeded=n=>{let s=n.target.result;if(s.objectStoreNames.contains("badge")||s.createObjectStore("badge"),!s.objectStoreNames.contains("notifications")){let o=s.createObjectStore("notifications",{keyPath:"id",autoIncrement:true});o.createIndex("timestamp","timestamp",{unique:false}),o.createIndex("read","read",{unique:false});}s.objectStoreNames.contains("userPreferences")||s.createObjectStore("userPreferences"),s.objectStoreNames.contains("syncQueue")||s.createObjectStore("syncQueue",{keyPath:"id",autoIncrement:true}).createIndex("timestamp","timestamp",{unique:false});};})}async ensureConnection(){this.db||await this.init();}promisifyRequest(e){return new Promise((t,i)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>i(e.error);})}async getBadgeCount(){await this.ensureConnection();let t=this.db.transaction(["badge"],"readonly").objectStore("badge");return await this.promisifyRequest(t.get("count"))||0}async setBadgeCount(e){await this.ensureConnection();let i=this.db.transaction(["badge"],"readwrite").objectStore("badge");await this.promisifyRequest(i.put(e,"count"));}async incrementBadgeCount(e=1){let t=await this.getBadgeCount(),i=Math.max(0,t+e);return await this.setBadgeCount(i),i}async saveNotification(e){await this.ensureConnection();let i=this.db.transaction(["notifications"],"readwrite").objectStore("notifications"),n={...e,timestamp:Date.now(),read:false};return await this.promisifyRequest(i.add(n))}async getRecentNotifications(e=50){await this.ensureConnection();let n=this.db.transaction(["notifications"],"readonly").objectStore("notifications").index("timestamp"),s=[],o=n.openCursor(null,"prev");return new Promise((l,c)=>{o.onsuccess=d=>{let a=d.target.result;a&&s.length<e?(s.push(a.value),a.continue()):l(s);},o.onerror=()=>c(o.error);})}async markNotificationAsRead(e){await this.ensureConnection();let i=this.db.transaction(["notifications"],"readwrite").objectStore("notifications"),n=await this.promisifyRequest(i.get(e));n&&(n.read=true,await this.promisifyRequest(i.put(n)));}async getUserPreference(e){await this.ensureConnection();let i=this.db.transaction(["userPreferences"],"readonly").objectStore("userPreferences");return this.promisifyRequest(i.get(e))}async setUserPreference(e,t){await this.ensureConnection();let n=this.db.transaction(["userPreferences"],"readwrite").objectStore("userPreferences");await this.promisifyRequest(n.put(t,e));}async addToSyncQueue(e){await this.ensureConnection();let i=this.db.transaction(["syncQueue"],"readwrite").objectStore("syncQueue"),n={...e,timestamp:Date.now(),retries:0};return await this.promisifyRequest(i.add(n))}async getSyncQueue(){await this.ensureConnection();let t=this.db.transaction(["syncQueue"],"readonly").objectStore("syncQueue");return this.promisifyRequest(t.getAll())}async removeFromSyncQueue(e){await this.ensureConnection();let i=this.db.transaction(["syncQueue"],"readwrite").objectStore("syncQueue");await this.promisifyRequest(i.delete(e));}};var R=class{constructor(e){this.serviceWorkerRegistration=null;this.pushSubscription=null;this.registrationId=null;this.vapidPublicKey=null;this.serviceUrl=e.serviceUrl,this.organizationId=e.organizationId,this.serviceId=e.serviceId,this.debug=e.debug??false,this.deviceDetector=new p,this.authContext=v({organizationId:e.organizationId,organizationPublishableKey:e.organizationPublishableKey,userToken:e.userToken}),this.onRegistered=e.onRegistered,this.onUnregistered=e.onUnregistered,this.onError=e.onError,this.onPermissionDenied=e.onPermissionDenied,this.onTokenExpired=e.onTokenExpired,this.registrationId=G(this.organizationId,this.serviceId);}setServiceWorkerRegistration(e){this.serviceWorkerRegistration=e;}setVapidPublicKey(e){this.vapidPublicKey=e;}getRegistrationId(){return this.registrationId}isRegistered(){return this.registrationId!==null}getAuthMode(){return this.authContext.mode}updateToken(e){this.authContext=y(this.authContext,e),this.log(`Auth mode: ${this.authContext.mode}`);}async register(e={}){try{if(!this.serviceWorkerRegistration)throw new Error("Service worker not registered");if(!this.vapidPublicKey)throw new Error("VAPID public key not set. Call downloadConfig() first.");if(await this.requestPermission()!=="granted")return this.log("Notification permission denied"),this.onPermissionDenied?.(),null;let i=e.userEmail??null,n=e.userId??null,s=oe(this.organizationId,this.serviceId);if(s){let j=s.email!==null&&i!==null&&s.email!==i,Z=s.userId!==null&&n!==null&&s.userId!==n;if(j||Z){this.log(`Identity changed on this device (was ${s.email||s.userId}, now ${i||n}); dropping stale push subscription`);try{let O=await this.serviceWorkerRegistration.pushManager.getSubscription();O&&(await O.unsubscribe(),this.pushSubscription=null);}catch(O){this.log(`Stale subscription unsubscribe failed: ${O}`,"error");}}}let o=await this.subscribeToPush();if(!o)throw new Error("Failed to subscribe to push notifications");this.pushSubscription=o;let l=await M(),c=this.deviceDetector.getDeviceInfo(),d=this.deviceDetector.getPlatformInfo(),a=o.toJSON(),b={serviceId:this.serviceId,userEmail:e.userEmail,userId:m(e.userId)?e.userId:void 0,endpoint:o.endpoint,keys:{p256dh:a.keys?.p256dh||"",auth:a.keys?.auth||""},browserFingerprint:l||void 0,deviceType:c.deviceType,deviceModel:c.deviceModel,browserName:d.browser,browserVersion:d.browserVersion,osName:d.os,osVersion:d.osVersion,userAgent:d.userAgent,displayMode:"standalone",isInstalled:!0,timezone:e.timezone||Intl.DateTimeFormat().resolvedOptions().timeZone,language:e.language||navigator.language||"en-US"},H=`${this.serviceUrl}/api/v1/registration/register`,g=await h(this.authContext,"POST",H,b,this.onTokenExpired);if(!g.ok){if(g.status===422&&(await g.clone().json().catch(()=>null))?.detail?.code==="user_identity_required")return this.log("Skipping push registration: service requires user identity"),null;let j=await g.text();throw new Error(`Registration failed: ${g.status} - ${j}`)}let w=await g.json();this.registrationId=w.registration_id,X(this.organizationId,this.serviceId,this.registrationId),ae(this.organizationId,this.serviceId,{email:i,userId:n});let z={registrationId:w.registration_id,status:w.status||"active",createdAt:w.created_at||new Date().toISOString(),isActive:!0};return this.log(`Push registration successful: ${z.registrationId}`),this.onRegistered?.(z),z}catch(t){let i=t instanceof Error?t:new Error(String(t));return this.log(`Push registration failed: ${i.message}`,"error"),this.onError?.(i),null}}async registerInstallation(){try{if(re(this.organizationId,this.serviceId))return this.log("Installation already registered, skipping"),null;let e=await M(),t=this.deviceDetector.getDeviceInfo(),i=this.deviceDetector.getPlatformInfo(),n={service_id:this.serviceId,browser_fingerprint:e||void 0,device_type:t.deviceType,device_model:t.deviceModel,browser_name:i.browser,browser_version:i.browserVersion,os_name:i.os,os_version:i.osVersion,user_agent:i.userAgent,display_mode:"standalone",is_installed:!0,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,language:navigator.language||"en-US"},s=`${this.serviceUrl}/api/v1/registration/install-only`,o=await h(this.authContext,"POST",s,n,this.onTokenExpired);if(!o.ok){if(o.status===422&&(await o.clone().json().catch(()=>null))?.detail?.code==="user_identity_required")return this.log("Skipping install-only registration: service requires user identity"),null;let c=await o.text();throw new Error(`Installation registration failed: ${o.status} - ${c}`)}let l=await o.json();return se(this.organizationId,this.serviceId,l.registration_id),this.log(`Installation registered: ${l.registration_id}`),{registrationId:l.registration_id}}catch(e){let t=e instanceof Error?e:new Error(String(e));return this.log(`Installation registration failed: ${t.message}`,"error"),this.onError?.(t),null}}async unregister(){try{if(!this.registrationId)return this.log("No registration to unregister"),!0;this.pushSubscription&&(await this.pushSubscription.unsubscribe(),this.pushSubscription=null);let e=`${this.serviceUrl}/api/v1/registration/unregister`,t=await h(this.authContext,"POST",e,{registration_id:this.registrationId},this.onTokenExpired);return t.ok||this.log(`Backend unregistration failed: ${t.status}`,"warn"),L(this.organizationId,this.serviceId),this.registrationId=null,this.log("Push unregistration successful"),this.onUnregistered?.(),!0}catch(e){let t=e instanceof Error?e:new Error(String(e));return this.log(`Push unregistration failed: ${t.message}`,"error"),this.onError?.(t),false}}async updatePreferences(e){try{if(!this.registrationId)throw new Error("Not registered for push notifications");let t=`${this.serviceUrl}/api/v1/registration/update`,i=await h(this.authContext,"POST",t,{registration_id:this.registrationId,notification_topics:e.topics,timezone:e.timezone,language:e.language,is_active:e.isActive},this.onTokenExpired);if(!i.ok)throw new Error(`Update failed: ${i.status}`);return this.log("Preferences updated successfully"),!0}catch(t){let i=t instanceof Error?t:new Error(String(t));return this.log(`Failed to update preferences: ${i.message}`,"error"),this.onError?.(i),false}}async checkStatus(){try{if(!this.registrationId)return null;let e=`${this.serviceUrl}/api/v1/registration/status/${this.registrationId}`,t=await h(this.authContext,"GET",e,void 0,this.onTokenExpired);if(!t.ok){if(t.status===404)return L(this.organizationId,this.serviceId),this.registrationId=null,null;throw new Error(`Status check failed: ${t.status}`)}let i=await t.json();return {registrationId:i.registration_id,status:i.status,isActive:i.is_active,isOnline:i.is_online,lastActive:i.last_active,lastSeenOnline:i.last_seen_online,lastHeartbeat:i.last_heartbeat,installationDate:i.installation_date,notificationCount:i.notification_count,deviceInfo:i.device_info}}catch(e){let t=e instanceof Error?e:new Error(String(e));return this.log(`Status check failed: ${t.message}`,"error"),null}}async requestPermission(){return "Notification"in window?Notification.permission==="granted"?"granted":Notification.permission==="denied"?"denied":await Notification.requestPermission():"denied"}async subscribeToPush(){if(!this.serviceWorkerRegistration||!this.vapidPublicKey)return null;try{let e=await this.serviceWorkerRegistration.pushManager.getSubscription();if(!e){let t=this.urlBase64ToUint8Array(this.vapidPublicKey);e=await this.serviceWorkerRegistration.pushManager.subscribe({userVisibleOnly:!0,applicationServerKey:t.buffer});}return e}catch(e){return this.log(`Push subscription failed: ${e}`,"error"),null}}urlBase64ToUint8Array(e){let t="=".repeat((4-e.length%4)%4),i=(e+t).replace(/-/g,"+").replace(/_/g,"/"),n=atob(i),s=new Uint8Array(n.length);for(let o=0;o<n.length;++o)s[o]=n.charCodeAt(o);return s}log(e,t="log"){if(!this.debug)return;console[t](`[CloudSignal PWA Push] ${e}`);}};P();var be={"4g":3e4,"3g":6e4,"2g":12e4,"slow-2g":18e4,unknown:3e4},E=class{constructor(e){this.registrationId=null;this.intervalId=null;this.isRunning=false;this.isPausedForBattery=false;this.visibilityHandler=null;this.connectionChangeHandler=null;this.batteryManager=null;this.serviceUrl=e.serviceUrl,this.organizationId=e.organizationId,this.debug=e.debug??false,this.onHeartbeatSent=e.onHeartbeatSent,this.onHeartbeatError=e.onHeartbeatError,this.onIntervalChanged=e.onIntervalChanged,this.onPausedForBattery=e.onPausedForBattery,this.onResumedFromBattery=e.onResumedFromBattery,this.onTokenExpired=e.onTokenExpired,this.authContext=v({organizationId:e.organizationId,organizationPublishableKey:e.organizationPublishableKey,userToken:e.userToken}),this.config={enabled:e.config?.enabled??true,interval:e.config?.interval??3e4,autoStart:e.config?.autoStart??true,stopOnHidden:e.config?.stopOnHidden??true},this.adaptiveConfig={adaptToNetwork:e.adaptiveConfig?.adaptToNetwork??true,adaptToBattery:e.adaptiveConfig?.adaptToBattery??true,batteryPauseThreshold:e.adaptiveConfig?.batteryPauseThreshold??.15,intervals:{...be,...e.adaptiveConfig?.intervals}},this.currentInterval=this.config.interval;}setRegistrationId(e){this.registrationId=e,this.config.autoStart&&!this.isRunning&&this.config.enabled&&this.start();}start(){if(this.isRunning){this.log("Heartbeat already running");return}if(!this.registrationId||!m(this.registrationId)){this.log("Cannot start heartbeat: no valid registration ID","warn");return}if(!this.config.enabled){this.log("Heartbeat is disabled");return}this.isRunning=true,this.updateIntervalForNetwork(),this.log(`Starting heartbeat with interval ${this.currentInterval}ms`),this.sendHeartbeat(),this.startInterval(),this.config.stopOnHidden&&this.setupVisibilityHandler(),this.adaptiveConfig.adaptToNetwork&&this.setupNetworkChangeHandler(),this.adaptiveConfig.adaptToBattery&&this.setupBatteryMonitoring();}stop(){if(this.isRunning){if(this.isRunning=false,this.isPausedForBattery=false,this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null),this.visibilityHandler&&(document.removeEventListener("visibilitychange",this.visibilityHandler),this.visibilityHandler=null),this.connectionChangeHandler){let e=navigator.connection;e&&e.removeEventListener("change",this.connectionChangeHandler),this.connectionChangeHandler=null;}this.log("Heartbeat stopped");}}isHeartbeatRunning(){return this.isRunning}getAuthMode(){return this.authContext.mode}updateToken(e){this.authContext=y(this.authContext,e),this.log(`Auth mode: ${this.authContext.mode}`);}async sendHeartbeat(){if(!this.registrationId||!m(this.registrationId))return this.log("Cannot send heartbeat: no valid registration ID","warn"),false;try{let e=`${this.serviceUrl}/api/v1/registration/heartbeat/${this.registrationId}`,t=await h(this.authContext,"POST",e,void 0,this.onTokenExpired);if(!t.ok)throw new Error(`Heartbeat failed: ${t.status}`);return this.log("Heartbeat sent successfully"),this.onHeartbeatSent?.(),!0}catch(e){let t=e instanceof Error?e:new Error(String(e));return this.log(`Heartbeat failed: ${t.message}`,"error"),this.onHeartbeatError?.(t),false}}updateConfig(e){let t=this.isRunning;t&&this.stop(),this.config={...this.config,...e},t&&this.config.enabled&&this.start();}getNetworkInfo(){let e=navigator.connection;return e?{effectiveType:e.effectiveType,saveData:e.saveData,downlink:e.downlink,rtt:e.rtt}:{}}async getBatteryInfo(){try{if(!("getBattery"in navigator))return null;let e=await navigator.getBattery();return {level:e.level,charging:e.charging}}catch{return null}}getCurrentInterval(){return this.currentInterval}isPausedDueToBattery(){return this.isPausedForBattery}startInterval(){this.intervalId&&clearInterval(this.intervalId),this.intervalId=setInterval(()=>{this.sendHeartbeat();},this.currentInterval);}updateIntervalForNetwork(){if(!this.adaptiveConfig.adaptToNetwork){this.currentInterval=this.config.interval;return}let e=navigator.connection;if(!e){this.currentInterval=this.config.interval;return}if(e.saveData){let n=Math.max(this.currentInterval,12e4);n!==this.currentInterval&&(this.currentInterval=n,this.log(`Interval increased to ${n}ms (saveData enabled)`),this.onIntervalChanged?.(n,"saveData"));return}let t=e.effectiveType||"unknown",i=this.adaptiveConfig.intervals[t]||this.config.interval;if(i!==this.currentInterval){let n=this.currentInterval;this.currentInterval=i,this.log(`Interval changed from ${n}ms to ${i}ms (${t})`),this.onIntervalChanged?.(i,t);}}setupNetworkChangeHandler(){let e=navigator.connection;e&&(this.connectionChangeHandler=()=>{let t=this.currentInterval;this.updateIntervalForNetwork(),t!==this.currentInterval&&this.isRunning&&!this.isPausedForBattery&&this.startInterval();},e.addEventListener("change",this.connectionChangeHandler));}async setupBatteryMonitoring(){try{if(!("getBattery"in navigator))return;this.batteryManager=await navigator.getBattery();let e=()=>{if(!this.batteryManager)return;let t=this.batteryManager.level,i=this.batteryManager.charging;t<=this.adaptiveConfig.batteryPauseThreshold&&!i?!this.isPausedForBattery&&this.isRunning&&(this.isPausedForBattery=!0,this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null),this.log(`Heartbeat paused (battery at ${Math.round(t*100)}%)`),this.onPausedForBattery?.()):this.isPausedForBattery&&this.isRunning&&(this.isPausedForBattery=!1,this.startInterval(),this.log(`Heartbeat resumed (battery at ${Math.round(t*100)}%)`),this.onResumedFromBattery?.());};e(),this.batteryManager.addEventListener("levelchange",e),this.batteryManager.addEventListener("chargingchange",e);}catch{}}setupVisibilityHandler(){this.visibilityHandler=()=>{document.hidden?(this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null),this.log("Heartbeat paused (page hidden)")):this.isRunning&&!this.intervalId&&!this.isPausedForBattery&&(this.sendHeartbeat(),this.startInterval(),this.log("Heartbeat resumed (page visible)"));},document.addEventListener("visibilitychange",this.visibilityHandler);}log(e,t="log"){if(!this.debug)return;console[t](`[CloudSignal PWA Heartbeat] ${e}`);}};var T=class{constructor(e={}){this.wakeLock=null;this.visibilityHandler=null;this.releaseHandler=null;this.shouldReacquire=false;this.debug=e.debug??false,this.onAcquired=e.onAcquired,this.onReleased=e.onReleased,this.onError=e.onError,this.config={enabled:e.config?.enabled??true,autoReacquire:e.config?.autoReacquire??true};}isSupported(){return "wakeLock"in navigator}getState(){return {isActive:this.wakeLock!==null&&!this.wakeLock.released,isSupported:this.isSupported(),type:"screen",acquiredAt:this.wakeLock&&!this.wakeLock.released?Date.now():void 0}}async acquire(){if(!this.config.enabled)return this.log("Wake lock is disabled"),false;if(!this.isSupported())return this.log("Wake Lock API not supported"),this.onError?.({timestamp:Date.now(),error:"Wake Lock API not supported",errorName:"NotSupportedError"}),false;if(this.wakeLock&&!this.wakeLock.released)return this.log("Wake lock already active"),true;try{return this.wakeLock=await navigator.wakeLock.request("screen"),this.shouldReacquire=!0,this.releaseHandler=()=>{this.handleRelease("system");},this.wakeLock.addEventListener("release",this.releaseHandler),this.config.autoReacquire&&this.setupVisibilityHandler(),this.log("Wake lock acquired"),this.onAcquired?.({timestamp:Date.now()}),!0}catch(e){let t=e instanceof Error?e:new Error(String(e));return this.log(`Failed to acquire wake lock: ${t.message}`,"error"),this.onError?.({timestamp:Date.now(),error:t.message,errorName:t.name}),false}}async release(){if(this.shouldReacquire=false,!!this.wakeLock){try{this.releaseHandler&&(this.wakeLock.removeEventListener("release",this.releaseHandler),this.releaseHandler=null),await this.wakeLock.release(),this.wakeLock=null,this.log("Wake lock released manually"),this.onReleased?.({timestamp:Date.now(),reason:"manual"});}catch(e){let t=e instanceof Error?e:new Error(String(e));this.log(`Failed to release wake lock: ${t.message}`,"error");}this.removeVisibilityHandler();}}async toggle(){return this.wakeLock&&!this.wakeLock.released?(await this.release(),false):this.acquire()}isActive(){return this.wakeLock!==null&&!this.wakeLock.released}async destroy(){await this.release(),this.removeVisibilityHandler();}handleRelease(e){this.wakeLock=null,this.log(`Wake lock released by ${e}`),this.onReleased?.({timestamp:Date.now(),reason:e});}setupVisibilityHandler(){this.visibilityHandler||(this.visibilityHandler=async()=>{document.visibilityState==="visible"&&this.shouldReacquire?(this.log("Page visible, attempting to reacquire wake lock"),await this.acquire()):document.visibilityState==="hidden"&&this.wakeLock&&this.log("Page hidden, wake lock will be released");},document.addEventListener("visibilitychange",this.visibilityHandler));}removeVisibilityHandler(){this.visibilityHandler&&(document.removeEventListener("visibilitychange",this.visibilityHandler),this.visibilityHandler=null);}log(e,t="log"){if(!this.debug&&t==="log")return;console[t](`[CloudSignal PWA WakeLock] ${e}`);}};var we="CloudSignalPWA";var u="offlineQueue",A=class{constructor(e={}){this.db=null;this.isProcessing=false;this.onlineHandler=null;this.debug=e.debug??false,this.onRequestQueued=e.onRequestQueued,this.onRequestProcessed=e.onRequestProcessed,this.onQueueEmpty=e.onQueueEmpty,this.onError=e.onError,this.config={enabled:e.config?.enabled??true,maxQueueSize:e.config?.maxQueueSize??100,maxAge:e.config?.maxAge??1440*60*1e3,baseRetryDelay:e.config?.baseRetryDelay??1e3,maxRetryDelay:e.config?.maxRetryDelay??6e4,defaultMaxRetries:e.config?.defaultMaxRetries??5,processOnOnline:e.config?.processOnOnline??true};}async initialize(){if(!this.config.enabled){this.log("Offline queue is disabled");return}try{await this.openDatabase(),this.config.processOnOnline&&(this.onlineHandler=()=>{this.log("Network online, processing queue"),this.processQueue();},window.addEventListener("online",this.onlineHandler)),await this.cleanupOldRequests(),this.log("Offline queue manager initialized");}catch(e){let t=e instanceof Error?e:new Error(String(e));this.log(`Failed to initialize: ${t.message}`,"error"),this.onError?.(t);}}destroy(){this.onlineHandler&&(window.removeEventListener("online",this.onlineHandler),this.onlineHandler=null),this.db&&(this.db.close(),this.db=null);}async queueRequest(e,t,i={}){if(!this.config.enabled||!this.db)return null;try{(await this.getStats()).totalQueued>=this.config.maxQueueSize&&(this.log("Queue is full, removing oldest request"),await this.removeOldestRequest());let s={url:e,method:t,headers:i.headers,body:i.body,queuedAt:Date.now(),retryCount:0,maxRetries:i.maxRetries??this.config.defaultMaxRetries,requestType:i.requestType??"custom",priority:i.priority??0,metadata:i.metadata},o=await this.addToStore(s);return s.id=o,this.log(`Queued request: ${t} ${e} (id: ${o})`),this.onRequestQueued?.(s),o}catch(n){let s=n instanceof Error?n:new Error(String(n));return this.log(`Failed to queue request: ${s.message}`,"error"),this.onError?.(s),null}}async processQueue(){if(!this.config.enabled||!this.db||this.isProcessing)return [];if(!navigator.onLine)return this.log("Offline, skipping queue processing"),[];this.isProcessing=true;let e=[];try{let t=await this.getAllRequests();t.sort((n,s)=>n.priority!==s.priority?s.priority-n.priority:n.queuedAt-s.queuedAt),this.log(`Processing ${t.length} queued requests`);for(let n of t){if(!navigator.onLine){this.log("Went offline during processing, stopping");break}let s=await this.processRequest(n);e.push(s),s.success||!s.shouldRetry?await this.removeFromStore(n.id):await this.updateRetryCount(n),this.onRequestProcessed?.(s),await this.delay(100);}let i=await this.getQueueCount();i===0&&this.onQueueEmpty?.(),this.log(`Processed ${e.length} requests, ${i} remaining`);}catch(t){let i=t instanceof Error?t:new Error(String(t));this.log(`Error processing queue: ${i.message}`,"error"),this.onError?.(i);}finally{this.isProcessing=false;}return e}async processRequest(e){let t={id:e.id,success:false,shouldRetry:false};try{let i=await fetch(e.url,{method:e.method,headers:e.headers,body:e.body});t.statusCode=i.status,t.success=i.ok,i.ok||(t.shouldRetry=this.shouldRetryStatus(i.status,e),t.error=`HTTP ${i.status}`),this.log(`Request ${e.id}: ${t.success?"success":"failed"} (${i.status})`);}catch(i){let n=i instanceof Error?i:new Error(String(i));t.error=n.message,t.shouldRetry=e.retryCount<e.maxRetries,this.log(`Request ${e.id} failed: ${n.message}`,"error");}return t}shouldRetryStatus(e,t){return t.retryCount>=t.maxRetries?false:e>=500||e===408||e===429||e===0}async updateRetryCount(e){e.retryCount++;let i=this.db.transaction([u],"readwrite").objectStore(u);await this.promisifyRequest(i.put(e));}async getStats(){let e={totalQueued:0,byType:{registration:0,heartbeat:0,analytics:0,preferences:0,unregister:0,custom:0}};if(!this.db)return e;try{let t=await this.getAllRequests();e.totalQueued=t.length;for(let i of t)e.byType[i.requestType]++,(!e.oldestRequest||i.queuedAt<e.oldestRequest)&&(e.oldestRequest=i.queuedAt),(!e.newestRequest||i.queuedAt>e.newestRequest)&&(e.newestRequest=i.queuedAt);}catch(t){this.log(`Failed to get stats: ${t}`,"error");}return e}async clearQueue(){if(!this.db)return;let t=this.db.transaction([u],"readwrite").objectStore(u);await this.promisifyRequest(t.clear()),this.log("Queue cleared");}async hasPendingRequests(){return await this.getQueueCount()>0}async getQueueCount(){if(!this.db)return 0;let t=this.db.transaction([u],"readonly").objectStore(u);return this.promisifyRequest(t.count())}async openDatabase(){return new Promise((e,t)=>{let i=indexedDB.open(we,2);i.onerror=()=>t(i.error),i.onsuccess=()=>{this.db=i.result,e();},i.onupgradeneeded=n=>{let s=n.target.result;if(!s.objectStoreNames.contains(u)){let o=s.createObjectStore(u,{keyPath:"id",autoIncrement:true});o.createIndex("queuedAt","queuedAt",{unique:false}),o.createIndex("requestType","requestType",{unique:false}),o.createIndex("priority","priority",{unique:false});}if(s.objectStoreNames.contains("badge")||s.createObjectStore("badge"),!s.objectStoreNames.contains("notifications")){let o=s.createObjectStore("notifications",{keyPath:"id",autoIncrement:true});o.createIndex("timestamp","timestamp",{unique:false}),o.createIndex("read","read",{unique:false});}s.objectStoreNames.contains("userPreferences")||s.createObjectStore("userPreferences"),s.objectStoreNames.contains("syncQueue")||s.createObjectStore("syncQueue",{keyPath:"id",autoIncrement:true}).createIndex("timestamp","timestamp",{unique:false});};})}async addToStore(e){let i=this.db.transaction([u],"readwrite").objectStore(u);return this.promisifyRequest(i.add(e))}async removeFromStore(e){let i=this.db.transaction([u],"readwrite").objectStore(u);await this.promisifyRequest(i.delete(e));}async getAllRequests(){let t=this.db.transaction([u],"readonly").objectStore(u);return this.promisifyRequest(t.getAll())}async removeOldestRequest(){let t=this.db.transaction([u],"readwrite").objectStore(u),n=t.index("queuedAt").openCursor();return new Promise((s,o)=>{n.onsuccess=l=>{let c=l.target.result;c&&t.delete(c.primaryKey),s();},n.onerror=()=>o(n.error);})}async cleanupOldRequests(){let e=Date.now()-this.config.maxAge,i=this.db.transaction([u],"readwrite").objectStore(u),n=i.index("queuedAt"),s=IDBKeyRange.upperBound(e),o=n.openCursor(s),l=0;return new Promise(c=>{o.onsuccess=d=>{let a=d.target.result;a?(i.delete(a.primaryKey),l++,a.continue()):(l>0&&this.log(`Cleaned up ${l} old requests`),c());},o.onerror=()=>c();})}promisifyRequest(e){return new Promise((t,i)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>i(e.error);})}delay(e){return new Promise(t=>setTimeout(t,e))}log(e,t="log"){if(!this.debug&&t==="log")return;console[t](`[CloudSignal PWA OfflineQueue] ${e}`);}};var C={en:{title:"Install this app",description:"Add this app to your home screen for the best experience.",buttonText:"Show me how",closeText:"Not now",step1:"Tap the Share button at the bottom of Safari",step2:'Scroll down and tap "Add to Home Screen"',step3:'Tap "Add" in the top right corner',shareIconHint:"Look for the Share icon",shareIconDescription:"It's the square with an arrow pointing up"},es:{title:"Instalar esta aplicaci\xF3n",description:"A\xF1ade esta aplicaci\xF3n a tu pantalla de inicio para una mejor experiencia.",buttonText:"Mostrar c\xF3mo",closeText:"Ahora no",step1:"Toca el bot\xF3n Compartir en la parte inferior de Safari",step2:'Despl\xE1zate hacia abajo y toca "A\xF1adir a pantalla de inicio"',step3:'Toca "A\xF1adir" en la esquina superior derecha',shareIconHint:"Busca el icono de Compartir",shareIconDescription:"Es el cuadrado con una flecha apuntando hacia arriba"},fr:{title:"Installer cette application",description:"Ajoutez cette application \xE0 votre \xE9cran d'accueil pour une meilleure exp\xE9rience.",buttonText:"Montrez-moi comment",closeText:"Pas maintenant",step1:"Appuyez sur le bouton Partager en bas de Safari",step2:`Faites d\xE9filer vers le bas et appuyez sur "Sur l'\xE9cran d'accueil"`,step3:'Appuyez sur "Ajouter" dans le coin sup\xE9rieur droit',shareIconHint:"Recherchez l'ic\xF4ne Partager",shareIconDescription:"C'est le carr\xE9 avec une fl\xE8che pointant vers le haut"},de:{title:"Diese App installieren",description:"F\xFCgen Sie diese App zu Ihrem Startbildschirm hinzu f\xFCr das beste Erlebnis.",buttonText:"Zeig mir wie",closeText:"Nicht jetzt",step1:"Tippen Sie auf die Teilen-Schaltfl\xE4che unten in Safari",step2:'Scrollen Sie nach unten und tippen Sie auf "Zum Home-Bildschirm"',step3:'Tippen Sie oben rechts auf "Hinzuf\xFCgen"',shareIconHint:"Suchen Sie das Teilen-Symbol",shareIconDescription:"Es ist das Quadrat mit einem Pfeil nach oben"},it:{title:"Installa questa app",description:"Aggiungi questa app alla schermata Home per un'esperienza migliore.",buttonText:"Mostrami come",closeText:"Non ora",step1:"Tocca il pulsante Condividi nella parte inferiore di Safari",step2:'Scorri verso il basso e tocca "Aggiungi a Home"',step3:`Tocca "Aggiungi" nell'angolo in alto a destra`,shareIconHint:"Cerca l'icona Condividi",shareIconDescription:"\xC8 il quadrato con una freccia che punta verso l'alto"},pt:{title:"Instalar este aplicativo",description:"Adicione este aplicativo \xE0 tela inicial para a melhor experi\xEAncia.",buttonText:"Mostre-me como",closeText:"Agora n\xE3o",step1:"Toque no bot\xE3o Compartilhar na parte inferior do Safari",step2:'Role para baixo e toque em "Adicionar \xE0 Tela de In\xEDcio"',step3:'Toque em "Adicionar" no canto superior direito',shareIconHint:"Procure o \xEDcone Compartilhar",shareIconDescription:"\xC9 o quadrado com uma seta apontando para cima"},nl:{title:"Installeer deze app",description:"Voeg deze app toe aan je beginscherm voor de beste ervaring.",buttonText:"Laat me zien hoe",closeText:"Niet nu",step1:"Tik op de Deel-knop onderaan Safari",step2:'Scroll naar beneden en tik op "Zet op beginscherm"',step3:'Tik rechtsboven op "Voeg toe"',shareIconHint:"Zoek het Deel-icoon",shareIconDescription:"Het is het vierkant met een pijl omhoog"},ru:{title:"\u0423\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435",description:"\u0414\u043E\u0431\u0430\u0432\u044C\u0442\u0435 \u044D\u0442\u043E \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u043D\u0430 \u0433\u043B\u0430\u0432\u043D\u044B\u0439 \u044D\u043A\u0440\u0430\u043D \u0434\u043B\u044F \u043B\u0443\u0447\u0448\u0435\u0433\u043E \u043E\u043F\u044B\u0442\u0430.",buttonText:"\u041F\u043E\u043A\u0430\u0436\u0438\u0442\u0435 \u043A\u0430\u043A",closeText:"\u041D\u0435 \u0441\u0435\u0439\u0447\u0430\u0441",step1:'\u041D\u0430\u0436\u043C\u0438\u0442\u0435 \u043A\u043D\u043E\u043F\u043A\u0443 "\u041F\u043E\u0434\u0435\u043B\u0438\u0442\u044C\u0441\u044F" \u0432\u043D\u0438\u0437\u0443 Safari',step2:'\u041F\u0440\u043E\u043A\u0440\u0443\u0442\u0438\u0442\u0435 \u0432\u043D\u0438\u0437 \u0438 \u043D\u0430\u0436\u043C\u0438\u0442\u0435 "\u041D\u0430 \u044D\u043A\u0440\u0430\u043D \u0414\u043E\u043C\u043E\u0439"',step3:'\u041D\u0430\u0436\u043C\u0438\u0442\u0435 "\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C" \u0432 \u043F\u0440\u0430\u0432\u043E\u043C \u0432\u0435\u0440\u0445\u043D\u0435\u043C \u0443\u0433\u043B\u0443',shareIconHint:'\u041D\u0430\u0439\u0434\u0438\u0442\u0435 \u0437\u043D\u0430\u0447\u043E\u043A "\u041F\u043E\u0434\u0435\u043B\u0438\u0442\u044C\u0441\u044F"',shareIconDescription:"\u042D\u0442\u043E \u043A\u0432\u0430\u0434\u0440\u0430\u0442 \u0441\u043E \u0441\u0442\u0440\u0435\u043B\u043A\u043E\u0439 \u0432\u0432\u0435\u0440\u0445"},zh:{title:"\u5B89\u88C5\u6B64\u5E94\u7528",description:"\u5C06\u6B64\u5E94\u7528\u6DFB\u52A0\u5230\u4E3B\u5C4F\u5E55\u4EE5\u83B7\u5F97\u6700\u4F73\u4F53\u9A8C\u3002",buttonText:"\u663E\u793A\u65B9\u6CD5",closeText:"\u6682\u65F6\u4E0D\u8981",step1:"\u70B9\u51FBSafari\u5E95\u90E8\u7684\u5206\u4EAB\u6309\u94AE",step2:'\u5411\u4E0B\u6EDA\u52A8\u5E76\u70B9\u51FB"\u6DFB\u52A0\u5230\u4E3B\u5C4F\u5E55"',step3:'\u70B9\u51FB\u53F3\u4E0A\u89D2\u7684"\u6DFB\u52A0"',shareIconHint:"\u627E\u5230\u5206\u4EAB\u56FE\u6807",shareIconDescription:"\u662F\u4E00\u4E2A\u5E26\u6709\u5411\u4E0A\u7BAD\u5934\u7684\u65B9\u6846"},ja:{title:"\u3053\u306E\u30A2\u30D7\u30EA\u3092\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB",description:"\u30DB\u30FC\u30E0\u753B\u9762\u306B\u8FFD\u52A0\u3057\u3066\u6700\u9AD8\u306E\u4F53\u9A13\u3092\u3002",buttonText:"\u65B9\u6CD5\u3092\u898B\u308B",closeText:"\u4ECA\u306F\u3057\u306A\u3044",step1:"Safari\u306E\u4E0B\u90E8\u306B\u3042\u308B\u5171\u6709\u30DC\u30BF\u30F3\u3092\u30BF\u30C3\u30D7",step2:"\u4E0B\u306B\u30B9\u30AF\u30ED\u30FC\u30EB\u3057\u3066\u300C\u30DB\u30FC\u30E0\u753B\u9762\u306B\u8FFD\u52A0\u300D\u3092\u30BF\u30C3\u30D7",step3:"\u53F3\u4E0A\u306E\u300C\u8FFD\u52A0\u300D\u3092\u30BF\u30C3\u30D7",shareIconHint:"\u5171\u6709\u30A2\u30A4\u30B3\u30F3\u3092\u63A2\u3057\u3066\u304F\u3060\u3055\u3044",shareIconDescription:"\u4E0A\u5411\u304D\u77E2\u5370\u306E\u3042\u308B\u56DB\u89D2\u5F62\u3067\u3059"},ko:{title:"\uC774 \uC571 \uC124\uCE58",description:"\uCD5C\uACE0\uC758 \uACBD\u9A13\uC744 \uC704\uD574 \uD648 \uD654\uBA74\uC5D0 \uCD94\uAC00\uD558\uC138\uC694.",buttonText:"\uBC29\uBC95 \uBCF4\uAE30",closeText:"\uB098\uC911\uC5D0",step1:"Safari \uD558\uB2E8\uC758 \uACF5\uC720 \uBC84\uD2BC\uC744 \uD0ED\uD558\uC138\uC694",step2:'\uC544\uB798\uB85C \uC2A4\uD06C\uB864\uD558\uC5EC "\uD648 \uD654\uBA74\uC5D0 \uCD94\uAC00"\uB97C \uD0ED\uD558\uC138\uC694',step3:'\uC624\uB978\uCABD \uC0C1\uB2E8\uC758 "\uCD94\uAC00"\uB97C \uD0ED\uD558\uC138\uC694',shareIconHint:"\uACF5\uC720 \uC544\uC774\uCF58\uC744 \uCC3E\uC73C\uC138\uC694",shareIconDescription:"\uC704\uCABD \uD654\uC0B4\uD45C\uAC00 \uC788\uB294 \uC0AC\uAC01\uD615\uC785\uB2C8\uB2E4"},ar:{title:"\u062A\u062B\u0628\u064A\u062A \u0647\u0630\u0627 \u0627\u0644\u062A\u0637\u0628\u064A\u0642",description:"\u0623\u0636\u0641 \u0647\u0630\u0627 \u0627\u0644\u062A\u0637\u0628\u064A\u0642 \u0625\u0644\u0649 \u0627\u0644\u0634\u0627\u0634\u0629 \u0627\u0644\u0631\u0626\u064A\u0633\u064A\u0629 \u0644\u0644\u062D\u0635\u0648\u0644 \u0639\u0644\u0649 \u0623\u0641\u0636\u0644 \u062A\u062C\u0631\u0628\u0629.",buttonText:"\u0623\u0631\u0646\u064A \u0643\u064A\u0641",closeText:"\u0644\u064A\u0633 \u0627\u0644\u0622\u0646",step1:"\u0627\u0636\u063A\u0637 \u0639\u0644\u0649 \u0632\u0631 \u0627\u0644\u0645\u0634\u0627\u0631\u0643\u0629 \u0641\u064A \u0623\u0633\u0641\u0644 Safari",step2:'\u0645\u0631\u0631 \u0644\u0623\u0633\u0641\u0644 \u0648\u0627\u0636\u063A\u0637 \u0639\u0644\u0649 "\u0625\u0636\u0627\u0641\u0629 \u0625\u0644\u0649 \u0627\u0644\u0634\u0627\u0634\u0629 \u0627\u0644\u0631\u0626\u064A\u0633\u064A\u0629"',step3:'\u0627\u0636\u063A\u0637 \u0639\u0644\u0649 "\u0625\u0636\u0627\u0641\u0629" \u0641\u064A \u0627\u0644\u0632\u0627\u0648\u064A\u0629 \u0627\u0644\u0639\u0644\u0648\u064A\u0629 \u0627\u0644\u064A\u0645\u0646\u0649',shareIconHint:"\u0627\u0628\u062D\u062B \u0639\u0646 \u0623\u064A\u0642\u0648\u0646\u0629 \u0627\u0644\u0645\u0634\u0627\u0631\u0643\u0629",shareIconDescription:"\u0647\u0648 \u0645\u0631\u0628\u0639 \u0645\u0639 \u0633\u0647\u0645 \u064A\u0634\u064A\u0631 \u0644\u0623\u0639\u0644\u0649"},he:{title:"\u05D4\u05EA\u05E7\u05DF \u05D0\u05E4\u05DC\u05D9\u05E7\u05E6\u05D9\u05D4 \u05D6\u05D5",description:"\u05D4\u05D5\u05E1\u05E3 \u05D0\u05E4\u05DC\u05D9\u05E7\u05E6\u05D9\u05D4 \u05D6\u05D5 \u05DC\u05DE\u05E1\u05DA \u05D4\u05D1\u05D9\u05EA \u05DC\u05D7\u05D5\u05D5\u05D9\u05D4 \u05D4\u05D8\u05D5\u05D1\u05D4 \u05D1\u05D9\u05D5\u05EA\u05E8.",buttonText:"\u05D4\u05E8\u05D0\u05D4 \u05DC\u05D9 \u05D0\u05D9\u05DA",closeText:"\u05DC\u05D0 \u05E2\u05DB\u05E9\u05D9\u05D5",step1:"\u05D4\u05E7\u05E9 \u05E2\u05DC \u05DB\u05E4\u05EA\u05D5\u05E8 \u05D4\u05E9\u05D9\u05EA\u05D5\u05E3 \u05D1\u05EA\u05D7\u05EA\u05D9\u05EA Safari",step2:'\u05D2\u05DC\u05D5\u05DC \u05DC\u05DE\u05D8\u05D4 \u05D5\u05D4\u05E7\u05E9 \u05E2\u05DC "\u05D4\u05D5\u05E1\u05E3 \u05DC\u05DE\u05E1\u05DA \u05D4\u05D1\u05D9\u05EA"',step3:'\u05D4\u05E7\u05E9 \u05E2\u05DC "\u05D4\u05D5\u05E1\u05E3" \u05D1\u05E4\u05D9\u05E0\u05D4 \u05D4\u05D9\u05DE\u05E0\u05D9\u05EA \u05D4\u05E2\u05DC\u05D9\u05D5\u05E0\u05D4',shareIconHint:"\u05D7\u05E4\u05E9 \u05D0\u05EA \u05E1\u05DE\u05DC \u05D4\u05E9\u05D9\u05EA\u05D5\u05E3",shareIconDescription:"\u05D6\u05D4 \u05E8\u05D9\u05D1\u05D5\u05E2 \u05E2\u05DD \u05D7\u05E5 \u05DE\u05E6\u05D1\u05D9\u05E2 \u05DC\u05DE\u05E2\u05DC\u05D4"},hi:{title:"\u092F\u0939 \u0910\u092A \u0907\u0902\u0938\u094D\u091F\u0949\u0932 \u0915\u0930\u0947\u0902",description:"\u092C\u0947\u0939\u0924\u0930 \u0905\u0928\u0941\u092D\u0935 \u0915\u0947 \u0932\u093F\u090F \u0907\u0938 \u0910\u092A \u0915\u094B \u0939\u094B\u092E \u0938\u094D\u0915\u094D\u0930\u0940\u0928 \u092A\u0930 \u091C\u094B\u0921\u093C\u0947\u0902\u0964",buttonText:"\u092E\u0941\u091D\u0947 \u0926\u093F\u0916\u093E\u0913 \u0915\u0948\u0938\u0947",closeText:"\u0905\u092D\u0940 \u0928\u0939\u0940\u0902",step1:"Safari \u0915\u0947 \u0928\u0940\u091A\u0947 \u0936\u0947\u092F\u0930 \u092C\u091F\u0928 \u092A\u0930 \u091F\u0948\u092A \u0915\u0930\u0947\u0902",step2:'\u0928\u0940\u091A\u0947 \u0938\u094D\u0915\u094D\u0930\u0949\u0932 \u0915\u0930\u0947\u0902 \u0914\u0930 "\u0939\u094B\u092E \u0938\u094D\u0915\u094D\u0930\u0940\u0928 \u092E\u0947\u0902 \u091C\u094B\u0921\u093C\u0947\u0902" \u092A\u0930 \u091F\u0948\u092A \u0915\u0930\u0947\u0902',step3:'\u090A\u092A\u0930 \u0926\u093E\u090F\u0902 \u0915\u094B\u0928\u0947 \u092E\u0947\u0902 "\u091C\u094B\u0921\u093C\u0947\u0902" \u092A\u0930 \u091F\u0948\u092A \u0915\u0930\u0947\u0902',shareIconHint:"\u0936\u0947\u092F\u0930 \u0906\u0907\u0915\u0928 \u0916\u094B\u091C\u0947\u0902",shareIconDescription:"\u092F\u0939 \u090A\u092A\u0930 \u0915\u0940 \u0913\u0930 \u0924\u0940\u0930 \u0935\u093E\u0932\u093E \u0935\u0930\u094D\u0917 \u0939\u0948"},tr:{title:"Bu uygulamay\u0131 y\xFCkle",description:"En iyi deneyim i\xE7in bu uygulamay\u0131 ana ekran\u0131n\u0131za ekleyin.",buttonText:"Nas\u0131l yap\u0131l\u0131r g\xF6ster",closeText:"\u015Eimdi de\u011Fil",step1:"Safari'nin alt\u0131ndaki Payla\u015F d\xFC\u011Fmesine dokunun",step2:'A\u015Fa\u011F\u0131 kayd\u0131r\u0131n ve "Ana Ekrana Ekle"ye dokunun',step3:'Sa\u011F \xFCst k\xF6\u015Fedeki "Ekle"ye dokunun',shareIconHint:"Payla\u015F simgesini aray\u0131n",shareIconDescription:"Yukar\u0131 bakan oklu bir karedir"},pl:{title:"Zainstaluj t\u0119 aplikacj\u0119",description:"Dodaj t\u0119 aplikacj\u0119 do ekranu g\u0142\xF3wnego, aby uzyska\u0107 najlepsze wra\u017Cenia.",buttonText:"Poka\u017C mi jak",closeText:"Nie teraz",step1:"Dotknij przycisku Udost\u0119pnij na dole Safari",step2:'Przewi\u0144 w d\xF3\u0142 i dotknij "Dodaj do ekranu pocz\u0105tkowego"',step3:'Dotknij "Dodaj" w prawym g\xF3rnym rogu',shareIconHint:"Poszukaj ikony Udost\u0119pnij",shareIconDescription:"To kwadrat ze strza\u0142k\u0105 skierowan\u0105 w g\xF3r\u0119"},sv:{title:"Installera denna app",description:"L\xE4gg till denna app p\xE5 hemsk\xE4rmen f\xF6r b\xE4sta upplevelse.",buttonText:"Visa mig hur",closeText:"Inte nu",step1:"Tryck p\xE5 Dela-knappen l\xE4ngst ner i Safari",step2:'Scrolla ner och tryck p\xE5 "L\xE4gg till p\xE5 hemsk\xE4rmen"',step3:'Tryck p\xE5 "L\xE4gg till" i \xF6vre h\xF6gra h\xF6rnet',shareIconHint:"Leta efter Dela-ikonen",shareIconDescription:"Det \xE4r kvadraten med en pil som pekar upp\xE5t"},da:{title:"Installer denne app",description:"F\xF8j denne app til din startsk\xE6rm for den bedste oplevelse.",buttonText:"Vis mig hvordan",closeText:"Ikke nu",step1:"Tryk p\xE5 Del-knappen nederst i Safari",step2:'Rul ned og tryk p\xE5 "F\xF8j til hjemmesk\xE6rm"',step3:'Tryk p\xE5 "Tilf\xF8j" i \xF8verste h\xF8jre hj\xF8rne',shareIconHint:"Kig efter Del-ikonet",shareIconDescription:"Det er firkanten med en pil, der peger opad"},no:{title:"Installer denne appen",description:"Legg til denne appen p\xE5 startskjermen for best opplevelse.",buttonText:"Vis meg hvordan",closeText:"Ikke n\xE5",step1:"Trykk p\xE5 Del-knappen nederst i Safari",step2:'Rull ned og trykk p\xE5 "Legg til p\xE5 Hjem-skjerm"',step3:'Trykk p\xE5 "Legg til" \xF8verst til h\xF8yre',shareIconHint:"Se etter Del-ikonet",shareIconDescription:"Det er firkanten med en pil som peker oppover"},fi:{title:"Asenna t\xE4m\xE4 sovellus",description:"Lis\xE4\xE4 t\xE4m\xE4 sovellus aloitusn\xE4yt\xF6lle parhaan kokemuksen saamiseksi.",buttonText:"N\xE4yt\xE4 miten",closeText:"Ei nyt",step1:"Napauta Jaa-painiketta Safarin alareunassa",step2:'Vierit\xE4 alas ja napauta "Lis\xE4\xE4 Koti-valikkoon"',step3:'Napauta "Lis\xE4\xE4" oikeassa yl\xE4kulmassa',shareIconHint:"Etsi Jaa-kuvake",shareIconDescription:"Se on neli\xF6, jossa on yl\xF6sp\xE4in osoittava nuoli"}};function B(){let r=navigator.language?.toLowerCase()||"en",e=r.split("-")[0];return e in C?e:r.startsWith("zh")?"zh":r.startsWith("pt")?"pt":r.startsWith("no")||r.startsWith("nb")||r.startsWith("nn")?"no":"en"}var q="cloudsignal_pwa_ios_banner_dismissed",f="cloudsignal-ios-install-banner",Ie={backgroundColor:"#ffffff",textColor:"#1a1a1a",buttonBackgroundColor:"#007AFF",buttonTextColor:"#ffffff",borderRadius:"16px",boxShadow:"0 4px 24px rgba(0, 0, 0, 0.15)"},D=class{constructor(e={}){this.bannerElement=null;this.overlayElement=null;this.debug=e.debug??false,this.onShow=e.onShow,this.onDismiss=e.onDismiss,this.onInstallClick=e.onInstallClick,this.language=e.language??B();let t=C[this.language];this.strings={...t,...e.customStrings},this.config={enabled:e.config?.enabled??true,title:e.config?.title??this.strings.title,description:e.config?.description??this.strings.description,buttonText:e.config?.buttonText??this.strings.buttonText,closeText:e.config?.closeText??this.strings.closeText,iconUrl:e.config?.iconUrl,customStyles:e.config?.customStyles??{},showDelay:e.config?.showDelay??3e3,dismissalDays:e.config?.dismissalDays??7,position:e.config?.position??"bottom"},this.styles={...Ie,...this.config.customStyles},this.log(`Initialized with language: ${this.language}`);}getLanguage(){return this.language}getStrings(){return this.strings}setLanguage(e){this.language=e;let t=C[e];this.strings={...t},this.config.title=this.strings.title,this.config.description=this.strings.description,this.config.buttonText=this.strings.buttonText,this.config.closeText=this.strings.closeText,this.log(`Language changed to: ${e}`);}getState(){return {isVisible:this.bannerElement!==null,wasDismissed:this.wasDismissed(),isEligible:this.isEligible(),isInstalled:this.isInstalled()}}isEligible(){let e=navigator.userAgent;return !(!(/iPad|iPhone|iPod/.test(e)&&!window.MSStream)||!(/Safari/i.test(e)&&!/Chrome|CriOS|FxiOS|EdgiOS/i.test(e))||this.isInstalled())}isInstalled(){return !!(navigator.standalone===true||window.matchMedia("(display-mode: standalone)").matches)}wasDismissed(){try{let e=localStorage.getItem(q);if(!e)return !1;let t=parseInt(e,10);return (Date.now()-t)/(1e3*60*60*24)>this.config.dismissalDays?(localStorage.removeItem(q),!1):!0}catch{return false}}async show(){return this.config.enabled?this.isEligible()?this.wasDismissed()?(this.log("Banner was dismissed recently"),false):this.bannerElement?(this.log("Banner already visible"),true):(this.config.showDelay>0&&await new Promise(e=>setTimeout(e,this.config.showDelay)),!this.isEligible()||this.wasDismissed()?false:(this.createBanner(),this.log("Banner shown"),this.onShow?.(),true)):(this.log("Device not eligible for iOS install banner"),false):(this.log("Banner is disabled"),false)}hide(){this.bannerElement&&(this.bannerElement.remove(),this.bannerElement=null),this.overlayElement&&(this.overlayElement.remove(),this.overlayElement=null);}dismiss(){this.hide();try{localStorage.setItem(q,Date.now().toString());}catch{}this.log("Banner dismissed"),this.onDismiss?.();}resetDismissal(){try{localStorage.removeItem(q);}catch{}}getInstallSteps(){return [this.strings.step1,this.strings.step2,this.strings.step3]}createBanner(){this.overlayElement=document.createElement("div"),this.overlayElement.id=`${f}-overlay`,this.overlayElement.style.cssText=`
8
8
  position: fixed;
9
9
  top: 0;
10
10
  left: 0;
@@ -14,7 +14,7 @@ var ne=Object.defineProperty;var re=(r,e)=>()=>(r&&(e=r(r=0)),e);var se=(r,e)=>{
14
14
  z-index: 999998;
15
15
  opacity: 0;
16
16
  transition: opacity 0.3s ease;
17
- `,this.overlayElement.addEventListener("click",()=>this.dismiss()),document.body.appendChild(this.overlayElement),this.bannerElement=document.createElement("div"),this.bannerElement.id=m;let e=this.config.position==="top"?"top: 20px;":"bottom: 20px;";this.bannerElement.style.cssText=`
17
+ `,this.overlayElement.addEventListener("click",()=>this.dismiss()),document.body.appendChild(this.overlayElement),this.bannerElement=document.createElement("div"),this.bannerElement.id=f;let e=this.config.position==="top"?"top: 20px;":"bottom: 20px;";this.bannerElement.style.cssText=`
18
18
  position: fixed;
19
19
  ${e}
20
20
  left: 20px;
@@ -40,7 +40,7 @@ var ne=Object.defineProperty;var re=(r,e)=>()=>(r&&(e=r(r=0)),e);var se=(r,e)=>{
40
40
  </div>
41
41
  </div>
42
42
 
43
- <div id="${m}-steps" style="display: none; margin-bottom: 16px;">
43
+ <div id="${f}-steps" style="display: none; margin-bottom: 16px;">
44
44
  <ol style="margin: 0; padding-left: 24px; font-size: 14px; line-height: 1.6;">
45
45
  ${t.map(l=>`<li style="margin-bottom: 8px;">${l}</li>`).join("")}
46
46
  </ol>
@@ -56,7 +56,7 @@ var ne=Object.defineProperty;var re=(r,e)=>()=>(r&&(e=r(r=0)),e);var se=(r,e)=>{
56
56
  </div>
57
57
 
58
58
  <div style="display: flex; gap: 12px;">
59
- <button id="${m}-show" style="
59
+ <button id="${f}-show" style="
60
60
  flex: 1;
61
61
  padding: 12px 16px;
62
62
  background: ${this.styles.buttonBackgroundColor};
@@ -68,7 +68,7 @@ var ne=Object.defineProperty;var re=(r,e)=>()=>(r&&(e=r(r=0)),e);var se=(r,e)=>{
68
68
  cursor: pointer;
69
69
  -webkit-tap-highlight-color: transparent;
70
70
  ">${this.config.buttonText}</button>
71
- <button id="${m}-close" style="
71
+ <button id="${f}-close" style="
72
72
  padding: 12px 16px;
73
73
  background: transparent;
74
74
  color: ${this.styles.textColor};
@@ -80,7 +80,7 @@ var ne=Object.defineProperty;var re=(r,e)=>()=>(r&&(e=r(r=0)),e);var se=(r,e)=>{
80
80
  ">${this.config.closeText}</button>
81
81
  </div>
82
82
  </div>
83
- `,document.body.appendChild(this.bannerElement);let n=document.getElementById(`${m}-show`),s=document.getElementById(`${m}-close`),o=document.getElementById(`${m}-steps`);n?.addEventListener("click",()=>{o&&(o.style.display=o.style.display==="none"?"block":"none",n.textContent=o.style.display==="none"?this.config.buttonText:"Got it!"),this.onInstallClick?.();}),s?.addEventListener("click",()=>this.dismiss()),requestAnimationFrame(()=>{this.overlayElement&&(this.overlayElement.style.opacity="1"),this.bannerElement&&(this.bannerElement.style.opacity="1",this.bannerElement.style.transform="translateY(0)");});}log(e,t="log"){if(!this.debug&&t==="log")return;console[t](`[CloudSignal PWA IOSBanner] ${e}`);}};var me="https://pwa.cloudsignal.app",ie="1.2.0",L=class{constructor(e){this.initialized=false;this.serviceConfig=null;this.wakeLockManager=null;this.offlineQueueManager=null;this.iosInstallBanner=null;this.eventHandlers=new Map;if(this.config=e,this.serviceUrl=e.serviceUrl||me,this.debug=e.debug??false,!e.organizationPublishableKey&&!e.userToken)throw new Error("Either organizationPublishableKey or userToken must be provided");if(this.authContext=y({organizationId:e.organizationId,organizationPublishableKey:e.organizationPublishableKey,userToken:e.userToken}),this.deviceDetector=new f,this.serviceWorkerManager=new S({config:e.serviceWorker,debug:this.debug,onRegistered:t=>this.emit("sw:registered",{registration:t}),onUpdated:t=>this.emit("sw:updated",{registration:t}),onError:t=>this.emit("sw:error",{error:t})}),this.installationManager=new k({debug:this.debug,onInstallAvailable:t=>this.emit("install:available",{platforms:t.platforms}),onInstallAccepted:t=>this.emit("install:accepted",t),onInstallDismissed:t=>this.emit("install:dismissed",t),onInstalled:()=>this.emit("install:completed",{})}),this.pushNotificationManager=new x({serviceUrl:this.serviceUrl,organizationId:e.organizationId,organizationPublishableKey:e.organizationPublishableKey,userToken:e.userToken,onTokenExpired:e.onTokenExpired,serviceId:e.serviceId,debug:this.debug,onRegistered:t=>{this.emit("push:registered",{registrationId:t.registrationId,endpoint:""}),this.heartbeatManager.setRegistrationId(t.registrationId);},onUnregistered:()=>{this.emit("push:unregistered",{}),this.heartbeatManager.stop();},onError:t=>this.emit("push:error",{error:t}),onPermissionDenied:()=>this.emit("permission:denied",{permission:"denied"})}),this.heartbeatManager=new R({serviceUrl:this.serviceUrl,organizationId:e.organizationId,organizationPublishableKey:e.organizationPublishableKey,userToken:e.userToken,onTokenExpired:e.onTokenExpired,config:e.heartbeat,debug:this.debug,onHeartbeatSent:()=>this.emit("heartbeat:sent",{timestamp:Date.now()}),onHeartbeatError:t=>this.emit("heartbeat:error",{timestamp:Date.now(),error:t.message}),onIntervalChanged:(t,i)=>this.emit("heartbeat:intervalChanged",{interval:t,reason:i}),onPausedForBattery:()=>this.emit("heartbeat:pausedForBattery",{timestamp:Date.now()}),onResumedFromBattery:()=>this.emit("heartbeat:resumedFromBattery",{timestamp:Date.now()})}),e.wakeLock?.enabled!==false&&(this.wakeLockManager=new E({config:{enabled:e.wakeLock?.enabled??true,autoReacquire:e.wakeLock?.reacquireOnVisibility??true},debug:this.debug,onAcquired:()=>this.emit("wakeLock:acquired",{timestamp:Date.now()}),onReleased:t=>this.emit("wakeLock:released",{reason:t.reason,timestamp:Date.now()}),onError:t=>this.emit("wakeLock:error",{error:t.error})})),e.offlineQueue?.enabled!==false&&(this.offlineQueueManager=new T({config:{enabled:e.offlineQueue?.enabled??true,maxQueueSize:e.offlineQueue?.maxQueueSize??100,maxAge:(e.offlineQueue?.maxAgeTTLHours??24)*60*60*1e3,processOnOnline:e.offlineQueue?.autoProcessOnOnline??true},debug:this.debug,onRequestQueued:t=>this.emit("offlineQueue:queued",{url:t.url,method:t.method}),onRequestProcessed:t=>this.emit("offlineQueue:processed",{success:t.success,requestId:t.id}),onQueueEmpty:()=>this.emit("offlineQueue:flushed",{})})),e.iosInstallBanner?.enabled!==false){let t=this.deviceDetector.getDeviceInfo();t.isIOS&&t.browser==="Safari"&&(this.iosInstallBanner=new B({config:{enabled:e.iosInstallBanner?.enabled??true,title:e.iosInstallBanner?.title,description:e.iosInstallBanner?.subtitle,iconUrl:e.iosInstallBanner?.iconUrl,showDelay:e.iosInstallBanner?.showDelay,dismissalDays:e.iosInstallBanner?.dismissRememberDays},debug:this.debug,onShow:()=>this.emit("iosBanner:shown",{}),onDismiss:()=>this.emit("iosBanner:dismissed",{}),onInstallClick:()=>this.emit("iosBanner:installClicked",{})}));}this.log(`CloudSignal PWA SDK v${ie} initialized`);}async initialize(){try{this.log("Initializing PWA client..."),this.installationManager.initialize();let e=await this.serviceWorkerManager.register();e&&this.pushNotificationManager.setServiceWorkerRegistration(e);let t=await this.downloadConfig();if(this.setupNetworkListeners(),this.offlineQueueManager&&await this.offlineQueueManager.initialize(),this.config.notificationAnalytics?.enabled!==!1&&e&&this.configureNotificationAnalytics(e),this.iosInstallBanner&&this.config.iosInstallBanner?.showOnFirstVisit!==!1&&(this.installationManager.getState().isInstalled||this.iosInstallBanner.show()),this.installationManager.getState().isInstalled){this.log("PWA installation detected, registering\u2026");let s=await this.pushNotificationManager.registerInstallation();s&&this.emit("install:registered",{registrationId:s.registrationId});}this.initialized=!0;let n={success:!0,config:t||void 0,deviceInfo:this.deviceDetector.getDeviceInfo(),installationState:this.installationManager.getState()};return this.log("PWA client initialized successfully"),n}catch(e){let t=e instanceof Error?e:new Error(String(e));return this.log(`Initialization failed: ${t.message}`,"error"),{success:false,error:t.message}}}async downloadConfig(){try{let e=`${this.serviceUrl}/api/v1/config/download`,t=await h(this.authContext,"POST",e,{organization_id:this.config.organizationId},this.config.onTokenExpired);if(!t.ok)throw new Error(`Config download failed: ${t.status}`);let i=await t.json();return this.serviceConfig=i,i.vapid_public_key&&this.pushNotificationManager.setVapidPublicKey(i.vapid_public_key),i.manifest_url&&this.injectManifest(i.manifest_url),this.emit("config:loaded",{config:i}),this.log("Configuration downloaded successfully"),i}catch(e){let t=e instanceof Error?e:new Error(String(e));return this.log(`Config download failed: ${t.message}`,"error"),this.emit("config:error",{error:t}),null}}async showInstallPrompt(){return this.installationManager.showInstallPrompt()}getInstallationState(){return this.installationManager.getState()}canInstall(){return this.installationManager.canInstall()}isInstalled(){return this.installationManager.isPWAInstalled()}getInstallSteps(){return this.installationManager.getInstallSteps()}async registerForPush(e){return this.pushNotificationManager.register(e)}async unregisterFromPush(){return this.pushNotificationManager.unregister()}async updatePreferences(e){return this.pushNotificationManager.updatePreferences(e)}async checkRegistrationStatus(){return this.pushNotificationManager.checkStatus()}getRegistrationId(){return this.pushNotificationManager.getRegistrationId()}isRegistered(){return this.pushNotificationManager.isRegistered()}async requestPermission(){return this.pushNotificationManager.requestPermission()}getAuthMode(){return this.authContext.mode}setUserToken(e){this.authContext=b(this.authContext,e),this.pushNotificationManager.updateToken(e),this.heartbeatManager.updateToken(e),this.emit("auth:tokenUpdated",{mode:this.authContext.mode}),this.log(`Auth mode updated to: ${this.authContext.mode}`);}getDeviceInfo(){return this.deviceDetector.getDeviceInfo()}getCapabilities(){return this.deviceDetector.getCapabilities()}startHeartbeat(){let e=this.pushNotificationManager.getRegistrationId();e?(this.heartbeatManager.setRegistrationId(e),this.heartbeatManager.start(),this.emit("heartbeat:started",{timestamp:Date.now()})):this.log("Cannot start heartbeat: not registered","warn");}stopHeartbeat(){this.heartbeatManager.stop(),this.emit("heartbeat:stopped",{timestamp:Date.now()});}getHeartbeatInterval(){return this.heartbeatManager.getCurrentInterval()}getNetworkInfo(){return this.heartbeatManager.getNetworkInfo()}async getBatteryInfo(){return this.heartbeatManager.getBatteryInfo()}async requestWakeLock(){return this.wakeLockManager?this.wakeLockManager.acquire():(this.log("Wake lock manager not initialized","warn"),false)}releaseWakeLock(){this.wakeLockManager?.release();}getWakeLockState(){return this.wakeLockManager?this.wakeLockManager.getState():{isSupported:false,isActive:false}}async queueOfflineRequest(e,t,i){return this.offlineQueueManager?this.offlineQueueManager.queueRequest(e,t,{body:i?.body,headers:i?.headers}):(this.log("Offline queue manager not initialized","warn"),null)}async processOfflineQueue(){return this.offlineQueueManager?this.offlineQueueManager.processQueue():[]}async getOfflineQueueStats(){return this.offlineQueueManager?this.offlineQueueManager.getStats():{totalQueued:0,byType:{registration:0,heartbeat:0,analytics:0,preferences:0,unregister:0,custom:0}}}async clearOfflineQueue(){await this.offlineQueueManager?.clearQueue();}showIOSInstallBanner(){if(!this.iosInstallBanner){this.log("iOS install banner not available (not on iOS Safari)","warn");return}this.iosInstallBanner.show();}hideIOSInstallBanner(){this.iosInstallBanner?.hide();}wasIOSBannerDismissed(){return this.iosInstallBanner?.wasDismissed()??false}resetIOSBannerDismissal(){this.iosInstallBanner?.resetDismissal();}clearBadge(){this.serviceWorkerManager.clearBadge();}setBadge(e){this.serviceWorkerManager.setBadge(e);}async checkForUpdates(){return this.serviceWorkerManager.checkForUpdate()}on(e,t){this.eventHandlers.has(e)||this.eventHandlers.set(e,new Set),this.eventHandlers.get(e).add(t);}off(e,t){let i=this.eventHandlers.get(e);i&&i.delete(t);}emit(e,t){let i=this.eventHandlers.get(e);i&&i.forEach(n=>{try{n(t);}catch(s){this.log(`Event handler error for ${e}: ${s}`,"error");}});}getVersion(){return ie}getServiceConfig(){return this.serviceConfig}isInitialized(){return this.initialized}injectManifest(e){let t=document.querySelector('link[rel="manifest"]');t||(t=document.createElement("link"),t.setAttribute("rel","manifest"),document.head.appendChild(t)),t.setAttribute("href",e),this.log(`Manifest injected: ${e}`);}setupNetworkListeners(){window.addEventListener("online",()=>{this.deviceDetector.clearCache(),this.emit("network:online",{isOnline:true}),this.offlineQueueManager&&this.offlineQueueManager.processQueue().catch(e=>{this.log(`Failed to process offline queue: ${e.message}`,"error");});}),window.addEventListener("offline",()=>{this.deviceDetector.clearCache(),this.emit("network:offline",{isOnline:false});});}configureNotificationAnalytics(e){let t=this.config.notificationAnalytics?.endpoint||`${this.serviceUrl}/api/v1/analytics/notifications`;e.active?.postMessage({type:"CONFIGURE_ANALYTICS",config:{enabled:this.config.notificationAnalytics?.enabled!==false,endpoint:t,organizationId:this.config.organizationId}}),this.log("Notification analytics configured");}log(e,t="log"){if(!this.debug&&t==="log")return;console[t](`[CloudSignal PWA] ${e}`);}},ve=L;var q="cloudsignal_pwa_notification_prompt_dismissed",w="cloudsignal-notification-prompt",V={en:{title:"Stay Updated",description:"Get notified about important updates, messages, and alerts.",allowButton:"Enable Notifications",laterButton:"Maybe Later",iosNote:"On iOS, you must first add this app to your home screen."},es:{title:"Mantente Actualizado",description:"Recibe notificaciones sobre actualizaciones importantes, mensajes y alertas.",allowButton:"Activar Notificaciones",laterButton:"Quiz\xE1s Despu\xE9s",iosNote:"En iOS, primero debes a\xF1adir esta aplicaci\xF3n a tu pantalla de inicio."},fr:{title:"Restez Inform\xE9",description:"Recevez des notifications sur les mises \xE0 jour importantes, messages et alertes.",allowButton:"Activer les Notifications",laterButton:"Plus Tard",iosNote:"Sur iOS, vous devez d'abord ajouter cette application \xE0 votre \xE9cran d'accueil."},de:{title:"Bleiben Sie Informiert",description:"Erhalten Sie Benachrichtigungen \xFCber wichtige Updates, Nachrichten und Warnungen.",allowButton:"Benachrichtigungen Aktivieren",laterButton:"Vielleicht Sp\xE4ter",iosNote:"Auf iOS m\xFCssen Sie diese App zuerst zum Startbildschirm hinzuf\xFCgen."},it:{title:"Rimani Aggiornato",description:"Ricevi notifiche su aggiornamenti importanti, messaggi e avvisi.",allowButton:"Attiva Notifiche",laterButton:"Forse Dopo",iosNote:"Su iOS, devi prima aggiungere questa app alla schermata Home."},pt:{title:"Fique Atualizado",description:"Receba notifica\xE7\xF5es sobre atualiza\xE7\xF5es importantes, mensagens e alertas.",allowButton:"Ativar Notifica\xE7\xF5es",laterButton:"Talvez Depois",iosNote:"No iOS, voc\xEA deve primeiro adicionar este aplicativo \xE0 tela inicial."},nl:{title:"Blijf Op De Hoogte",description:"Ontvang meldingen over belangrijke updates, berichten en waarschuwingen.",allowButton:"Meldingen Inschakelen",laterButton:"Misschien Later",iosNote:"Op iOS moet je deze app eerst toevoegen aan je beginscherm."},ru:{title:"\u0411\u0443\u0434\u044C\u0442\u0435 \u0432 \u041A\u0443\u0440\u0441\u0435",description:"\u041F\u043E\u043B\u0443\u0447\u0430\u0439\u0442\u0435 \u0443\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u044F \u043E \u0432\u0430\u0436\u043D\u044B\u0445 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F\u0445, \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F\u0445 \u0438 \u043F\u0440\u0435\u0434\u0443\u043F\u0440\u0435\u0436\u0434\u0435\u043D\u0438\u044F\u0445.",allowButton:"\u0412\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0423\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u044F",laterButton:"\u041F\u043E\u0437\u0436\u0435",iosNote:"\u041D\u0430 iOS \u0441\u043D\u0430\u0447\u0430\u043B\u0430 \u0434\u043E\u0431\u0430\u0432\u044C\u0442\u0435 \u044D\u0442\u043E \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u043D\u0430 \u0433\u043B\u0430\u0432\u043D\u044B\u0439 \u044D\u043A\u0440\u0430\u043D."},zh:{title:"\u4FDD\u6301\u66F4\u65B0",description:"\u83B7\u53D6\u91CD\u8981\u66F4\u65B0\u3001\u6D88\u606F\u548C\u63D0\u9192\u7684\u901A\u77E5\u3002",allowButton:"\u542F\u7528\u901A\u77E5",laterButton:"\u7A0D\u540E\u518D\u8BF4",iosNote:"\u5728iOS\u4E0A\uFF0C\u60A8\u5FC5\u987B\u5148\u5C06\u6B64\u5E94\u7528\u6DFB\u52A0\u5230\u4E3B\u5C4F\u5E55\u3002"},ja:{title:"\u6700\u65B0\u60C5\u5831\u3092\u5165\u624B",description:"\u91CD\u8981\u306A\u30A2\u30C3\u30D7\u30C7\u30FC\u30C8\u3001\u30E1\u30C3\u30BB\u30FC\u30B8\u3001\u30A2\u30E9\u30FC\u30C8\u306E\u901A\u77E5\u3092\u53D7\u3051\u53D6\u308A\u307E\u3059\u3002",allowButton:"\u901A\u77E5\u3092\u6709\u52B9\u306B\u3059\u308B",laterButton:"\u5F8C\u3067",iosNote:"iOS\u3067\u306F\u3001\u307E\u305A\u3053\u306E\u30A2\u30D7\u30EA\u3092\u30DB\u30FC\u30E0\u753B\u9762\u306B\u8FFD\u52A0\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002"},ko:{title:"\uCD5C\uC2E0 \uC815\uBCF4 \uBC1B\uAE30",description:"\uC911\uC694\uD55C \uC5C5\uB370\uC774\uD2B8, \uBA54\uC2DC\uC9C0 \uBC0F \uC54C\uB9BC\uC5D0 \uB300\uD55C \uC54C\uB9BC\uC744 \uBC1B\uC73C\uC138\uC694.",allowButton:"\uC54C\uB9BC \uD65C\uC131\uD654",laterButton:"\uB098\uC911\uC5D0",iosNote:"iOS\uC5D0\uC11C\uB294 \uBA3C\uC800 \uC774 \uC571\uC744 \uD648 \uD654\uBA74\uC5D0 \uCD94\uAC00\uD574\uC57C \uD569\uB2C8\uB2E4."},ar:{title:"\u0627\u0628\u0642 \u0639\u0644\u0649 \u0627\u0637\u0644\u0627\u0639",description:"\u0627\u062D\u0635\u0644 \u0639\u0644\u0649 \u0625\u0634\u0639\u0627\u0631\u0627\u062A \u062D\u0648\u0644 \u0627\u0644\u062A\u062D\u062F\u064A\u062B\u0627\u062A \u0627\u0644\u0645\u0647\u0645\u0629 \u0648\u0627\u0644\u0631\u0633\u0627\u0626\u0644 \u0648\u0627\u0644\u062A\u0646\u0628\u064A\u0647\u0627\u062A.",allowButton:"\u062A\u0641\u0639\u064A\u0644 \u0627\u0644\u0625\u0634\u0639\u0627\u0631\u0627\u062A",laterButton:"\u0631\u0628\u0645\u0627 \u0644\u0627\u062D\u0642\u0627\u064B",iosNote:"\u0639\u0644\u0649 iOS\u060C \u064A\u062C\u0628 \u0623\u0648\u0644\u0627\u064B \u0625\u0636\u0627\u0641\u0629 \u0647\u0630\u0627 \u0627\u0644\u062A\u0637\u0628\u064A\u0642 \u0625\u0644\u0649 \u0627\u0644\u0634\u0627\u0634\u0629 \u0627\u0644\u0631\u0626\u064A\u0633\u064A\u0629."},he:{title:"\u05D4\u05D9\u05E9\u05D0\u05E8 \u05DE\u05E2\u05D5\u05D3\u05DB\u05DF",description:"\u05E7\u05D1\u05DC \u05D4\u05EA\u05E8\u05D0\u05D5\u05EA \u05E2\u05DC \u05E2\u05D3\u05DB\u05D5\u05E0\u05D9\u05DD \u05D7\u05E9\u05D5\u05D1\u05D9\u05DD, \u05D4\u05D5\u05D3\u05E2\u05D5\u05EA \u05D5\u05D4\u05EA\u05E8\u05D0\u05D5\u05EA.",allowButton:"\u05D4\u05E4\u05E2\u05DC \u05D4\u05EA\u05E8\u05D0\u05D5\u05EA",laterButton:"\u05D0\u05D5\u05DC\u05D9 \u05DE\u05D0\u05D5\u05D7\u05E8 \u05D9\u05D5\u05EA\u05E8",iosNote:"\u05D1-iOS, \u05E2\u05DC\u05D9\u05DA \u05DC\u05D4\u05D5\u05E1\u05D9\u05E3 \u05EA\u05D7\u05D9\u05DC\u05D4 \u05D0\u05EA \u05D4\u05D0\u05E4\u05DC\u05D9\u05E7\u05E6\u05D9\u05D4 \u05DC\u05DE\u05E1\u05DA \u05D4\u05D1\u05D9\u05EA."},hi:{title:"\u0905\u092A\u0921\u0947\u091F \u0930\u0939\u0947\u0902",description:"\u092E\u0939\u0924\u094D\u0935\u092A\u0942\u0930\u094D\u0923 \u0905\u092A\u0921\u0947\u091F, \u0938\u0902\u0926\u0947\u0936\u094B\u0902 \u0914\u0930 \u0905\u0932\u0930\u094D\u091F \u0915\u0947 \u092C\u093E\u0930\u0947 \u092E\u0947\u0902 \u0938\u0942\u091A\u0928\u093E\u090F\u0902 \u092A\u094D\u0930\u093E\u092A\u094D\u0924 \u0915\u0930\u0947\u0902\u0964",allowButton:"\u0938\u0942\u091A\u0928\u093E\u090F\u0902 \u0938\u0915\u094D\u0937\u092E \u0915\u0930\u0947\u0902",laterButton:"\u092C\u093E\u0926 \u092E\u0947\u0902",iosNote:"iOS \u092A\u0930, \u0906\u092A\u0915\u094B \u092A\u0939\u0932\u0947 \u0907\u0938 \u0910\u092A \u0915\u094B \u0939\u094B\u092E \u0938\u094D\u0915\u094D\u0930\u0940\u0928 \u092A\u0930 \u091C\u094B\u0921\u093C\u0928\u093E \u0939\u094B\u0917\u093E\u0964"},tr:{title:"G\xFCncel Kal\u0131n",description:"\xD6nemli g\xFCncellemeler, mesajlar ve uyar\u0131lar hakk\u0131nda bildirim al\u0131n.",allowButton:"Bildirimleri Etkinle\u015Ftir",laterButton:"Belki Sonra",iosNote:"iOS'ta \xF6nce bu uygulamay\u0131 ana ekran\u0131n\u0131za eklemelisiniz."},pl:{title:"B\u0105d\u017A Na Bie\u017C\u0105co",description:"Otrzymuj powiadomienia o wa\u017Cnych aktualizacjach, wiadomo\u015Bciach i alertach.",allowButton:"W\u0142\u0105cz Powiadomienia",laterButton:"Mo\u017Ce P\xF3\u017Aniej",iosNote:"Na iOS musisz najpierw doda\u0107 t\u0119 aplikacj\u0119 do ekranu g\u0142\xF3wnego."},sv:{title:"H\xE5ll Dig Uppdaterad",description:"F\xE5 aviseringar om viktiga uppdateringar, meddelanden och varningar.",allowButton:"Aktivera Aviseringar",laterButton:"Kanske Senare",iosNote:"P\xE5 iOS m\xE5ste du f\xF6rst l\xE4gga till denna app p\xE5 hemsk\xE4rmen."},da:{title:"Hold Dig Opdateret",description:"F\xE5 notifikationer om vigtige opdateringer, beskeder og advarsler.",allowButton:"Aktiv\xE9r Notifikationer",laterButton:"M\xE5ske Senere",iosNote:"P\xE5 iOS skal du f\xF8rst tilf\xF8je denne app til startsk\xE6rmen."},no:{title:"Hold Deg Oppdatert",description:"F\xE5 varsler om viktige oppdateringer, meldinger og varsler.",allowButton:"Aktiver Varsler",laterButton:"Kanskje Senere",iosNote:"P\xE5 iOS m\xE5 du f\xF8rst legge til denne appen p\xE5 startskjermen."},fi:{title:"Pysy Ajan Tasalla",description:"Saat ilmoituksia t\xE4rkeist\xE4 p\xE4ivityksist\xE4, viesteist\xE4 ja h\xE4lytyksist\xE4.",allowButton:"Ota Ilmoitukset K\xE4ytt\xF6\xF6n",laterButton:"Ehk\xE4 My\xF6hemmin",iosNote:"iOS:ssa sinun on ensin lis\xE4tt\xE4v\xE4 t\xE4m\xE4 sovellus aloitusn\xE4yt\xF6lle."}},ye={backgroundColor:"#ffffff",textColor:"#1a1a1a",primaryButtonBackground:"#007AFF",primaryButtonText:"#ffffff",secondaryButtonBackground:"transparent",secondaryButtonText:"#666666",borderRadius:"16px",boxShadow:"0 4px 24px rgba(0, 0, 0, 0.15)"},K=class{constructor(e={}){this.promptElement=null;this.overlayElement=null;this.debug=e.debug??false,this.onAllow=e.onAllow,this.onLater=e.onLater,this.onShow=e.onShow,this.language=e.language??C();let t=V[this.language];this.strings={...t,...e.customStrings},this.config={enabled:e.config?.enabled??true,iconUrl:e.config?.iconUrl??"",showDelay:e.config?.showDelay??2e3,dismissalDays:e.config?.dismissalDays??3,position:e.config?.position??"center",customStyles:e.config?.customStyles??{},showIOSNote:e.config?.showIOSNote??true},this.styles={...ye,...this.config.customStyles},this.log(`Initialized with language: ${this.language}`);}getLanguage(){return this.language}setLanguage(e){this.language=e,this.strings={...V[e]},this.log(`Language changed to: ${e}`);}shouldShow(){return !("Notification"in window&&Notification.permission!=="default"||this.wasDismissed())}wasDismissed(){try{let e=localStorage.getItem(q);if(!e)return !1;let t=parseInt(e,10);return (Date.now()-t)/(1e3*60*60*24)>this.config.dismissalDays?(localStorage.removeItem(q),!1):!0}catch{return false}}isIOS(){return /iPad|iPhone|iPod/.test(navigator.userAgent)&&!window.MSStream}isInstalled(){return !!(navigator.standalone===true||window.matchMedia("(display-mode: standalone)").matches)}async show(){return this.config.enabled?this.shouldShow()?this.promptElement?(this.log("Prompt already visible"),true):(this.config.showDelay>0&&await new Promise(e=>setTimeout(e,this.config.showDelay)),this.shouldShow()?(this.createPrompt(),this.log("Prompt shown"),this.onShow?.(),true):false):(this.log("Prompt should not be shown"),false):(this.log("Prompt is disabled"),false)}hide(){this.promptElement&&(this.promptElement.remove(),this.promptElement=null),this.overlayElement&&(this.overlayElement.remove(),this.overlayElement=null);}dismiss(){this.hide();try{localStorage.setItem(q,Date.now().toString());}catch{}this.log("Prompt dismissed"),this.onLater?.();}resetDismissal(){try{localStorage.removeItem(q);}catch{}}async handleAllow(){return this.hide(),this.onAllow?.(),"Notification"in window?await Notification.requestPermission():"denied"}createPrompt(){this.overlayElement=document.createElement("div"),this.overlayElement.id=`${w}-overlay`,this.overlayElement.style.cssText=`
83
+ `,document.body.appendChild(this.bannerElement);let n=document.getElementById(`${f}-show`),s=document.getElementById(`${f}-close`),o=document.getElementById(`${f}-steps`);n?.addEventListener("click",()=>{o&&(o.style.display=o.style.display==="none"?"block":"none",n.textContent=o.style.display==="none"?this.config.buttonText:"Got it!"),this.onInstallClick?.();}),s?.addEventListener("click",()=>this.dismiss()),requestAnimationFrame(()=>{this.overlayElement&&(this.overlayElement.style.opacity="1"),this.bannerElement&&(this.bannerElement.style.opacity="1",this.bannerElement.style.transform="translateY(0)");});}log(e,t="log"){if(!this.debug&&t==="log")return;console[t](`[CloudSignal PWA IOSBanner] ${e}`);}};var Se="https://pwa.cloudsignal.app",le="1.2.0",W=class{constructor(e){this.initialized=false;this.serviceConfig=null;this.wakeLockManager=null;this.offlineQueueManager=null;this.iosInstallBanner=null;this.eventHandlers=new Map;if(this.config=e,this.serviceUrl=e.serviceUrl||Se,this.debug=e.debug??false,!e.organizationPublishableKey&&!e.userToken)throw new Error("Either organizationPublishableKey or userToken must be provided");if(this.authContext=v({organizationId:e.organizationId,organizationPublishableKey:e.organizationPublishableKey,userToken:e.userToken}),this.deviceDetector=new p,this.serviceWorkerManager=new S({config:e.serviceWorker,debug:this.debug,onRegistered:t=>this.emit("sw:registered",{registration:t}),onUpdated:t=>this.emit("sw:updated",{registration:t}),onError:t=>this.emit("sw:error",{error:t})}),this.installationManager=new k({debug:this.debug,onInstallAvailable:t=>this.emit("install:available",{platforms:t.platforms}),onInstallAccepted:t=>this.emit("install:accepted",t),onInstallDismissed:t=>this.emit("install:dismissed",t),onInstalled:()=>this.emit("install:completed",{})}),this.pushNotificationManager=new R({serviceUrl:this.serviceUrl,organizationId:e.organizationId,organizationPublishableKey:e.organizationPublishableKey,userToken:e.userToken,onTokenExpired:e.onTokenExpired,serviceId:e.serviceId,debug:this.debug,onRegistered:t=>{this.emit("push:registered",{registrationId:t.registrationId,endpoint:""}),this.heartbeatManager.setRegistrationId(t.registrationId);},onUnregistered:()=>{this.emit("push:unregistered",{}),this.heartbeatManager.stop();},onError:t=>this.emit("push:error",{error:t}),onPermissionDenied:()=>this.emit("permission:denied",{permission:"denied"})}),this.heartbeatManager=new E({serviceUrl:this.serviceUrl,organizationId:e.organizationId,organizationPublishableKey:e.organizationPublishableKey,userToken:e.userToken,onTokenExpired:e.onTokenExpired,config:e.heartbeat,debug:this.debug,onHeartbeatSent:()=>this.emit("heartbeat:sent",{timestamp:Date.now()}),onHeartbeatError:t=>this.emit("heartbeat:error",{timestamp:Date.now(),error:t.message}),onIntervalChanged:(t,i)=>this.emit("heartbeat:intervalChanged",{interval:t,reason:i}),onPausedForBattery:()=>this.emit("heartbeat:pausedForBattery",{timestamp:Date.now()}),onResumedFromBattery:()=>this.emit("heartbeat:resumedFromBattery",{timestamp:Date.now()})}),e.wakeLock?.enabled!==false&&(this.wakeLockManager=new T({config:{enabled:e.wakeLock?.enabled??true,autoReacquire:e.wakeLock?.reacquireOnVisibility??true},debug:this.debug,onAcquired:()=>this.emit("wakeLock:acquired",{timestamp:Date.now()}),onReleased:t=>this.emit("wakeLock:released",{reason:t.reason,timestamp:Date.now()}),onError:t=>this.emit("wakeLock:error",{error:t.error})})),e.offlineQueue?.enabled!==false&&(this.offlineQueueManager=new A({config:{enabled:e.offlineQueue?.enabled??true,maxQueueSize:e.offlineQueue?.maxQueueSize??100,maxAge:(e.offlineQueue?.maxAgeTTLHours??24)*60*60*1e3,processOnOnline:e.offlineQueue?.autoProcessOnOnline??true},debug:this.debug,onRequestQueued:t=>this.emit("offlineQueue:queued",{url:t.url,method:t.method}),onRequestProcessed:t=>this.emit("offlineQueue:processed",{success:t.success,requestId:t.id}),onQueueEmpty:()=>this.emit("offlineQueue:flushed",{})})),e.iosInstallBanner?.enabled!==false){let t=this.deviceDetector.getDeviceInfo();t.isIOS&&t.browser==="Safari"&&(this.iosInstallBanner=new D({config:{enabled:e.iosInstallBanner?.enabled??true,title:e.iosInstallBanner?.title,description:e.iosInstallBanner?.subtitle,iconUrl:e.iosInstallBanner?.iconUrl,showDelay:e.iosInstallBanner?.showDelay,dismissalDays:e.iosInstallBanner?.dismissRememberDays},debug:this.debug,onShow:()=>this.emit("iosBanner:shown",{}),onDismiss:()=>this.emit("iosBanner:dismissed",{}),onInstallClick:()=>this.emit("iosBanner:installClicked",{})}));}this.log(`CloudSignal PWA SDK v${le} initialized`);}async initialize(){try{this.log("Initializing PWA client..."),this.installationManager.initialize();let e=await this.serviceWorkerManager.register();e&&this.pushNotificationManager.setServiceWorkerRegistration(e);let t=await this.downloadConfig();if(this.setupNetworkListeners(),this.offlineQueueManager&&await this.offlineQueueManager.initialize(),this.config.notificationAnalytics?.enabled!==!1&&e&&this.configureNotificationAnalytics(e),this.iosInstallBanner&&this.config.iosInstallBanner?.showOnFirstVisit!==!1&&(this.installationManager.getState().isInstalled||this.iosInstallBanner.show()),this.installationManager.getState().isInstalled){this.log("PWA installation detected, registering\u2026");let s=await this.pushNotificationManager.registerInstallation();s&&this.emit("install:registered",{registrationId:s.registrationId});}this.initialized=!0;let n={success:!0,config:t||void 0,deviceInfo:this.deviceDetector.getDeviceInfo(),installationState:this.installationManager.getState()};return this.log("PWA client initialized successfully"),n}catch(e){let t=e instanceof Error?e:new Error(String(e));return this.log(`Initialization failed: ${t.message}`,"error"),{success:false,error:t.message}}}async downloadConfig(){try{let e=`${this.serviceUrl}/api/v1/config/download`,t=await h(this.authContext,"POST",e,{organization_id:this.config.organizationId},this.config.onTokenExpired);if(!t.ok)throw new Error(`Config download failed: ${t.status}`);let i=await t.json();return this.serviceConfig=i,i.vapid_public_key&&this.pushNotificationManager.setVapidPublicKey(i.vapid_public_key),i.manifest_url&&this.injectManifest(i.manifest_url),this.emit("config:loaded",{config:i}),this.log("Configuration downloaded successfully"),i}catch(e){let t=e instanceof Error?e:new Error(String(e));return this.log(`Config download failed: ${t.message}`,"error"),this.emit("config:error",{error:t}),null}}async showInstallPrompt(){return this.installationManager.showInstallPrompt()}getInstallationState(){return this.installationManager.getState()}canInstall(){return this.installationManager.canInstall()}isInstalled(){return this.installationManager.isPWAInstalled()}getInstallSteps(){return this.installationManager.getInstallSteps()}async registerForPush(e){return this.pushNotificationManager.register(e)}async unregisterFromPush(){return this.pushNotificationManager.unregister()}async updatePreferences(e){return this.pushNotificationManager.updatePreferences(e)}async checkRegistrationStatus(){return this.pushNotificationManager.checkStatus()}getRegistrationId(){return this.pushNotificationManager.getRegistrationId()}isRegistered(){return this.pushNotificationManager.isRegistered()}async requestPermission(){return this.pushNotificationManager.requestPermission()}getAuthMode(){return this.authContext.mode}setUserToken(e){this.authContext=y(this.authContext,e),this.pushNotificationManager.updateToken(e),this.heartbeatManager.updateToken(e),this.emit("auth:tokenUpdated",{mode:this.authContext.mode}),this.log(`Auth mode updated to: ${this.authContext.mode}`);}getDeviceInfo(){return this.deviceDetector.getDeviceInfo()}getCapabilities(){return this.deviceDetector.getCapabilities()}startHeartbeat(){let e=this.pushNotificationManager.getRegistrationId();e?(this.heartbeatManager.setRegistrationId(e),this.heartbeatManager.start(),this.emit("heartbeat:started",{timestamp:Date.now()})):this.log("Cannot start heartbeat: not registered","warn");}stopHeartbeat(){this.heartbeatManager.stop(),this.emit("heartbeat:stopped",{timestamp:Date.now()});}getHeartbeatInterval(){return this.heartbeatManager.getCurrentInterval()}getNetworkInfo(){return this.heartbeatManager.getNetworkInfo()}async getBatteryInfo(){return this.heartbeatManager.getBatteryInfo()}async requestWakeLock(){return this.wakeLockManager?this.wakeLockManager.acquire():(this.log("Wake lock manager not initialized","warn"),false)}releaseWakeLock(){this.wakeLockManager?.release();}getWakeLockState(){return this.wakeLockManager?this.wakeLockManager.getState():{isSupported:false,isActive:false}}async queueOfflineRequest(e,t,i){return this.offlineQueueManager?this.offlineQueueManager.queueRequest(e,t,{body:i?.body,headers:i?.headers}):(this.log("Offline queue manager not initialized","warn"),null)}async processOfflineQueue(){return this.offlineQueueManager?this.offlineQueueManager.processQueue():[]}async getOfflineQueueStats(){return this.offlineQueueManager?this.offlineQueueManager.getStats():{totalQueued:0,byType:{registration:0,heartbeat:0,analytics:0,preferences:0,unregister:0,custom:0}}}async clearOfflineQueue(){await this.offlineQueueManager?.clearQueue();}showIOSInstallBanner(){if(!this.iosInstallBanner){this.log("iOS install banner not available (not on iOS Safari)","warn");return}this.iosInstallBanner.show();}hideIOSInstallBanner(){this.iosInstallBanner?.hide();}wasIOSBannerDismissed(){return this.iosInstallBanner?.wasDismissed()??false}resetIOSBannerDismissal(){this.iosInstallBanner?.resetDismissal();}clearBadge(){this.serviceWorkerManager.clearBadge();}setBadge(e){this.serviceWorkerManager.setBadge(e);}async checkForUpdates(){return this.serviceWorkerManager.checkForUpdate()}on(e,t){this.eventHandlers.has(e)||this.eventHandlers.set(e,new Set),this.eventHandlers.get(e).add(t);}off(e,t){let i=this.eventHandlers.get(e);i&&i.delete(t);}emit(e,t){let i=this.eventHandlers.get(e);i&&i.forEach(n=>{try{n(t);}catch(s){this.log(`Event handler error for ${e}: ${s}`,"error");}});}getVersion(){return le}getServiceConfig(){return this.serviceConfig}isInitialized(){return this.initialized}injectManifest(e){let t=document.querySelector('link[rel="manifest"]');t||(t=document.createElement("link"),t.setAttribute("rel","manifest"),document.head.appendChild(t)),t.setAttribute("href",e),this.log(`Manifest injected: ${e}`);}setupNetworkListeners(){window.addEventListener("online",()=>{this.deviceDetector.clearCache(),this.emit("network:online",{isOnline:true}),this.offlineQueueManager&&this.offlineQueueManager.processQueue().catch(e=>{this.log(`Failed to process offline queue: ${e.message}`,"error");});}),window.addEventListener("offline",()=>{this.deviceDetector.clearCache(),this.emit("network:offline",{isOnline:false});});}configureNotificationAnalytics(e){let t=this.config.notificationAnalytics?.endpoint||`${this.serviceUrl}/api/v1/analytics/notifications`;e.active?.postMessage({type:"CONFIGURE_ANALYTICS",config:{enabled:this.config.notificationAnalytics?.enabled!==false,endpoint:t,organizationId:this.config.organizationId}}),this.log("Notification analytics configured");}log(e,t="log"){if(!this.debug&&t==="log")return;console[t](`[CloudSignal PWA] ${e}`);}},ke=W;var $="cloudsignal_pwa_notification_prompt_dismissed",I="cloudsignal-notification-prompt",J={en:{title:"Stay Updated",description:"Get notified about important updates, messages, and alerts.",allowButton:"Enable Notifications",laterButton:"Maybe Later",iosNote:"On iOS, you must first add this app to your home screen."},es:{title:"Mantente Actualizado",description:"Recibe notificaciones sobre actualizaciones importantes, mensajes y alertas.",allowButton:"Activar Notificaciones",laterButton:"Quiz\xE1s Despu\xE9s",iosNote:"En iOS, primero debes a\xF1adir esta aplicaci\xF3n a tu pantalla de inicio."},fr:{title:"Restez Inform\xE9",description:"Recevez des notifications sur les mises \xE0 jour importantes, messages et alertes.",allowButton:"Activer les Notifications",laterButton:"Plus Tard",iosNote:"Sur iOS, vous devez d'abord ajouter cette application \xE0 votre \xE9cran d'accueil."},de:{title:"Bleiben Sie Informiert",description:"Erhalten Sie Benachrichtigungen \xFCber wichtige Updates, Nachrichten und Warnungen.",allowButton:"Benachrichtigungen Aktivieren",laterButton:"Vielleicht Sp\xE4ter",iosNote:"Auf iOS m\xFCssen Sie diese App zuerst zum Startbildschirm hinzuf\xFCgen."},it:{title:"Rimani Aggiornato",description:"Ricevi notifiche su aggiornamenti importanti, messaggi e avvisi.",allowButton:"Attiva Notifiche",laterButton:"Forse Dopo",iosNote:"Su iOS, devi prima aggiungere questa app alla schermata Home."},pt:{title:"Fique Atualizado",description:"Receba notifica\xE7\xF5es sobre atualiza\xE7\xF5es importantes, mensagens e alertas.",allowButton:"Ativar Notifica\xE7\xF5es",laterButton:"Talvez Depois",iosNote:"No iOS, voc\xEA deve primeiro adicionar este aplicativo \xE0 tela inicial."},nl:{title:"Blijf Op De Hoogte",description:"Ontvang meldingen over belangrijke updates, berichten en waarschuwingen.",allowButton:"Meldingen Inschakelen",laterButton:"Misschien Later",iosNote:"Op iOS moet je deze app eerst toevoegen aan je beginscherm."},ru:{title:"\u0411\u0443\u0434\u044C\u0442\u0435 \u0432 \u041A\u0443\u0440\u0441\u0435",description:"\u041F\u043E\u043B\u0443\u0447\u0430\u0439\u0442\u0435 \u0443\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u044F \u043E \u0432\u0430\u0436\u043D\u044B\u0445 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F\u0445, \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F\u0445 \u0438 \u043F\u0440\u0435\u0434\u0443\u043F\u0440\u0435\u0436\u0434\u0435\u043D\u0438\u044F\u0445.",allowButton:"\u0412\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0423\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u044F",laterButton:"\u041F\u043E\u0437\u0436\u0435",iosNote:"\u041D\u0430 iOS \u0441\u043D\u0430\u0447\u0430\u043B\u0430 \u0434\u043E\u0431\u0430\u0432\u044C\u0442\u0435 \u044D\u0442\u043E \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u043D\u0430 \u0433\u043B\u0430\u0432\u043D\u044B\u0439 \u044D\u043A\u0440\u0430\u043D."},zh:{title:"\u4FDD\u6301\u66F4\u65B0",description:"\u83B7\u53D6\u91CD\u8981\u66F4\u65B0\u3001\u6D88\u606F\u548C\u63D0\u9192\u7684\u901A\u77E5\u3002",allowButton:"\u542F\u7528\u901A\u77E5",laterButton:"\u7A0D\u540E\u518D\u8BF4",iosNote:"\u5728iOS\u4E0A\uFF0C\u60A8\u5FC5\u987B\u5148\u5C06\u6B64\u5E94\u7528\u6DFB\u52A0\u5230\u4E3B\u5C4F\u5E55\u3002"},ja:{title:"\u6700\u65B0\u60C5\u5831\u3092\u5165\u624B",description:"\u91CD\u8981\u306A\u30A2\u30C3\u30D7\u30C7\u30FC\u30C8\u3001\u30E1\u30C3\u30BB\u30FC\u30B8\u3001\u30A2\u30E9\u30FC\u30C8\u306E\u901A\u77E5\u3092\u53D7\u3051\u53D6\u308A\u307E\u3059\u3002",allowButton:"\u901A\u77E5\u3092\u6709\u52B9\u306B\u3059\u308B",laterButton:"\u5F8C\u3067",iosNote:"iOS\u3067\u306F\u3001\u307E\u305A\u3053\u306E\u30A2\u30D7\u30EA\u3092\u30DB\u30FC\u30E0\u753B\u9762\u306B\u8FFD\u52A0\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002"},ko:{title:"\uCD5C\uC2E0 \uC815\uBCF4 \uBC1B\uAE30",description:"\uC911\uC694\uD55C \uC5C5\uB370\uC774\uD2B8, \uBA54\uC2DC\uC9C0 \uBC0F \uC54C\uB9BC\uC5D0 \uB300\uD55C \uC54C\uB9BC\uC744 \uBC1B\uC73C\uC138\uC694.",allowButton:"\uC54C\uB9BC \uD65C\uC131\uD654",laterButton:"\uB098\uC911\uC5D0",iosNote:"iOS\uC5D0\uC11C\uB294 \uBA3C\uC800 \uC774 \uC571\uC744 \uD648 \uD654\uBA74\uC5D0 \uCD94\uAC00\uD574\uC57C \uD569\uB2C8\uB2E4."},ar:{title:"\u0627\u0628\u0642 \u0639\u0644\u0649 \u0627\u0637\u0644\u0627\u0639",description:"\u0627\u062D\u0635\u0644 \u0639\u0644\u0649 \u0625\u0634\u0639\u0627\u0631\u0627\u062A \u062D\u0648\u0644 \u0627\u0644\u062A\u062D\u062F\u064A\u062B\u0627\u062A \u0627\u0644\u0645\u0647\u0645\u0629 \u0648\u0627\u0644\u0631\u0633\u0627\u0626\u0644 \u0648\u0627\u0644\u062A\u0646\u0628\u064A\u0647\u0627\u062A.",allowButton:"\u062A\u0641\u0639\u064A\u0644 \u0627\u0644\u0625\u0634\u0639\u0627\u0631\u0627\u062A",laterButton:"\u0631\u0628\u0645\u0627 \u0644\u0627\u062D\u0642\u0627\u064B",iosNote:"\u0639\u0644\u0649 iOS\u060C \u064A\u062C\u0628 \u0623\u0648\u0644\u0627\u064B \u0625\u0636\u0627\u0641\u0629 \u0647\u0630\u0627 \u0627\u0644\u062A\u0637\u0628\u064A\u0642 \u0625\u0644\u0649 \u0627\u0644\u0634\u0627\u0634\u0629 \u0627\u0644\u0631\u0626\u064A\u0633\u064A\u0629."},he:{title:"\u05D4\u05D9\u05E9\u05D0\u05E8 \u05DE\u05E2\u05D5\u05D3\u05DB\u05DF",description:"\u05E7\u05D1\u05DC \u05D4\u05EA\u05E8\u05D0\u05D5\u05EA \u05E2\u05DC \u05E2\u05D3\u05DB\u05D5\u05E0\u05D9\u05DD \u05D7\u05E9\u05D5\u05D1\u05D9\u05DD, \u05D4\u05D5\u05D3\u05E2\u05D5\u05EA \u05D5\u05D4\u05EA\u05E8\u05D0\u05D5\u05EA.",allowButton:"\u05D4\u05E4\u05E2\u05DC \u05D4\u05EA\u05E8\u05D0\u05D5\u05EA",laterButton:"\u05D0\u05D5\u05DC\u05D9 \u05DE\u05D0\u05D5\u05D7\u05E8 \u05D9\u05D5\u05EA\u05E8",iosNote:"\u05D1-iOS, \u05E2\u05DC\u05D9\u05DA \u05DC\u05D4\u05D5\u05E1\u05D9\u05E3 \u05EA\u05D7\u05D9\u05DC\u05D4 \u05D0\u05EA \u05D4\u05D0\u05E4\u05DC\u05D9\u05E7\u05E6\u05D9\u05D4 \u05DC\u05DE\u05E1\u05DA \u05D4\u05D1\u05D9\u05EA."},hi:{title:"\u0905\u092A\u0921\u0947\u091F \u0930\u0939\u0947\u0902",description:"\u092E\u0939\u0924\u094D\u0935\u092A\u0942\u0930\u094D\u0923 \u0905\u092A\u0921\u0947\u091F, \u0938\u0902\u0926\u0947\u0936\u094B\u0902 \u0914\u0930 \u0905\u0932\u0930\u094D\u091F \u0915\u0947 \u092C\u093E\u0930\u0947 \u092E\u0947\u0902 \u0938\u0942\u091A\u0928\u093E\u090F\u0902 \u092A\u094D\u0930\u093E\u092A\u094D\u0924 \u0915\u0930\u0947\u0902\u0964",allowButton:"\u0938\u0942\u091A\u0928\u093E\u090F\u0902 \u0938\u0915\u094D\u0937\u092E \u0915\u0930\u0947\u0902",laterButton:"\u092C\u093E\u0926 \u092E\u0947\u0902",iosNote:"iOS \u092A\u0930, \u0906\u092A\u0915\u094B \u092A\u0939\u0932\u0947 \u0907\u0938 \u0910\u092A \u0915\u094B \u0939\u094B\u092E \u0938\u094D\u0915\u094D\u0930\u0940\u0928 \u092A\u0930 \u091C\u094B\u0921\u093C\u0928\u093E \u0939\u094B\u0917\u093E\u0964"},tr:{title:"G\xFCncel Kal\u0131n",description:"\xD6nemli g\xFCncellemeler, mesajlar ve uyar\u0131lar hakk\u0131nda bildirim al\u0131n.",allowButton:"Bildirimleri Etkinle\u015Ftir",laterButton:"Belki Sonra",iosNote:"iOS'ta \xF6nce bu uygulamay\u0131 ana ekran\u0131n\u0131za eklemelisiniz."},pl:{title:"B\u0105d\u017A Na Bie\u017C\u0105co",description:"Otrzymuj powiadomienia o wa\u017Cnych aktualizacjach, wiadomo\u015Bciach i alertach.",allowButton:"W\u0142\u0105cz Powiadomienia",laterButton:"Mo\u017Ce P\xF3\u017Aniej",iosNote:"Na iOS musisz najpierw doda\u0107 t\u0119 aplikacj\u0119 do ekranu g\u0142\xF3wnego."},sv:{title:"H\xE5ll Dig Uppdaterad",description:"F\xE5 aviseringar om viktiga uppdateringar, meddelanden och varningar.",allowButton:"Aktivera Aviseringar",laterButton:"Kanske Senare",iosNote:"P\xE5 iOS m\xE5ste du f\xF6rst l\xE4gga till denna app p\xE5 hemsk\xE4rmen."},da:{title:"Hold Dig Opdateret",description:"F\xE5 notifikationer om vigtige opdateringer, beskeder og advarsler.",allowButton:"Aktiv\xE9r Notifikationer",laterButton:"M\xE5ske Senere",iosNote:"P\xE5 iOS skal du f\xF8rst tilf\xF8je denne app til startsk\xE6rmen."},no:{title:"Hold Deg Oppdatert",description:"F\xE5 varsler om viktige oppdateringer, meldinger og varsler.",allowButton:"Aktiver Varsler",laterButton:"Kanskje Senere",iosNote:"P\xE5 iOS m\xE5 du f\xF8rst legge til denne appen p\xE5 startskjermen."},fi:{title:"Pysy Ajan Tasalla",description:"Saat ilmoituksia t\xE4rkeist\xE4 p\xE4ivityksist\xE4, viesteist\xE4 ja h\xE4lytyksist\xE4.",allowButton:"Ota Ilmoitukset K\xE4ytt\xF6\xF6n",laterButton:"Ehk\xE4 My\xF6hemmin",iosNote:"iOS:ssa sinun on ensin lis\xE4tt\xE4v\xE4 t\xE4m\xE4 sovellus aloitusn\xE4yt\xF6lle."}},Pe={backgroundColor:"#ffffff",textColor:"#1a1a1a",primaryButtonBackground:"#007AFF",primaryButtonText:"#ffffff",secondaryButtonBackground:"transparent",secondaryButtonText:"#666666",borderRadius:"16px",boxShadow:"0 4px 24px rgba(0, 0, 0, 0.15)"},Y=class{constructor(e={}){this.promptElement=null;this.overlayElement=null;this.debug=e.debug??false,this.onAllow=e.onAllow,this.onLater=e.onLater,this.onShow=e.onShow,this.language=e.language??B();let t=J[this.language];this.strings={...t,...e.customStrings},this.config={enabled:e.config?.enabled??true,iconUrl:e.config?.iconUrl??"",showDelay:e.config?.showDelay??2e3,dismissalDays:e.config?.dismissalDays??3,position:e.config?.position??"center",customStyles:e.config?.customStyles??{},showIOSNote:e.config?.showIOSNote??true},this.styles={...Pe,...this.config.customStyles},this.log(`Initialized with language: ${this.language}`);}getLanguage(){return this.language}setLanguage(e){this.language=e,this.strings={...J[e]},this.log(`Language changed to: ${e}`);}shouldShow(){return !("Notification"in window&&Notification.permission!=="default"||this.wasDismissed())}wasDismissed(){try{let e=localStorage.getItem($);if(!e)return !1;let t=parseInt(e,10);return (Date.now()-t)/(1e3*60*60*24)>this.config.dismissalDays?(localStorage.removeItem($),!1):!0}catch{return false}}isIOS(){return /iPad|iPhone|iPod/.test(navigator.userAgent)&&!window.MSStream}isInstalled(){return !!(navigator.standalone===true||window.matchMedia("(display-mode: standalone)").matches)}async show(){return this.config.enabled?this.shouldShow()?this.promptElement?(this.log("Prompt already visible"),true):(this.config.showDelay>0&&await new Promise(e=>setTimeout(e,this.config.showDelay)),this.shouldShow()?(this.createPrompt(),this.log("Prompt shown"),this.onShow?.(),true):false):(this.log("Prompt should not be shown"),false):(this.log("Prompt is disabled"),false)}hide(){this.promptElement&&(this.promptElement.remove(),this.promptElement=null),this.overlayElement&&(this.overlayElement.remove(),this.overlayElement=null);}dismiss(){this.hide();try{localStorage.setItem($,Date.now().toString());}catch{}this.log("Prompt dismissed"),this.onLater?.();}resetDismissal(){try{localStorage.removeItem($);}catch{}}async handleAllow(){return this.hide(),this.onAllow?.(),"Notification"in window?await Notification.requestPermission():"denied"}createPrompt(){this.overlayElement=document.createElement("div"),this.overlayElement.id=`${I}-overlay`,this.overlayElement.style.cssText=`
84
84
  position: fixed;
85
85
  top: 0;
86
86
  left: 0;
@@ -90,7 +90,7 @@ var ne=Object.defineProperty;var re=(r,e)=>()=>(r&&(e=r(r=0)),e);var se=(r,e)=>{
90
90
  z-index: 999998;
91
91
  opacity: 0;
92
92
  transition: opacity 0.3s ease;
93
- `,this.overlayElement.addEventListener("click",()=>this.dismiss()),document.body.appendChild(this.overlayElement),this.promptElement=document.createElement("div"),this.promptElement.id=w;let e;switch(this.config.position){case "top":e="top: 20px; left: 50%; transform: translateX(-50%) translateY(-20px);";break;case "bottom":e="bottom: 20px; left: 50%; transform: translateX(-50%) translateY(20px);";break;default:e="top: 50%; left: 50%; transform: translate(-50%, -50%) scale(0.95);";}this.promptElement.style.cssText=`
93
+ `,this.overlayElement.addEventListener("click",()=>this.dismiss()),document.body.appendChild(this.overlayElement),this.promptElement=document.createElement("div"),this.promptElement.id=I;let e;switch(this.config.position){case "top":e="top: 20px; left: 50%; transform: translateX(-50%) translateY(-20px);";break;case "bottom":e="bottom: 20px; left: 50%; transform: translateX(-50%) translateY(20px);";break;default:e="top: 50%; left: 50%; transform: translate(-50%, -50%) scale(0.95);";}this.promptElement.style.cssText=`
94
94
  position: fixed;
95
95
  ${e}
96
96
  width: 90%;
@@ -117,7 +117,7 @@ var ne=Object.defineProperty;var re=(r,e)=>()=>(r&&(e=r(r=0)),e);var se=(r,e)=>{
117
117
  <p style="margin: 0; font-size: 14px; opacity: 0.7; line-height: 1.5;">${this.strings.description}</p>
118
118
  ${n}
119
119
  <div style="margin-top: 20px; display: flex; flex-direction: column; gap: 10px;">
120
- <button id="${w}-allow" style="
120
+ <button id="${I}-allow" style="
121
121
  width: 100%;
122
122
  padding: 14px 20px;
123
123
  background: ${this.styles.primaryButtonBackground};
@@ -129,7 +129,7 @@ var ne=Object.defineProperty;var re=(r,e)=>()=>(r&&(e=r(r=0)),e);var se=(r,e)=>{
129
129
  cursor: pointer;
130
130
  -webkit-tap-highlight-color: transparent;
131
131
  ">${this.strings.allowButton}</button>
132
- <button id="${w}-later" style="
132
+ <button id="${I}-later" style="
133
133
  width: 100%;
134
134
  padding: 12px 20px;
135
135
  background: ${this.styles.secondaryButtonBackground};
@@ -142,4 +142,4 @@ var ne=Object.defineProperty;var re=(r,e)=>()=>(r&&(e=r(r=0)),e);var se=(r,e)=>{
142
142
  ">${this.strings.laterButton}</button>
143
143
  </div>
144
144
  </div>
145
- `,document.body.appendChild(this.promptElement),document.getElementById(`${w}-allow`)?.addEventListener("click",()=>this.handleAllow()),document.getElementById(`${w}-later`)?.addEventListener("click",()=>this.dismiss()),requestAnimationFrame(()=>{this.overlayElement&&(this.overlayElement.style.opacity="1"),this.promptElement&&(this.promptElement.style.opacity="1",this.config.position==="center"?this.promptElement.style.transform="translate(-50%, -50%) scale(1)":this.config.position==="top"?this.promptElement.style.transform="translateX(-50%) translateY(0)":this.promptElement.style.transform="translateX(-50%) translateY(0)");});}log(e,t="log"){if(!this.debug&&t==="log")return;console[t](`[CloudSignal PWA NotificationPrompt] ${e}`);}};P();var tt="1.2.0";exports.CloudSignalPWA=L;exports.DeviceDetector=f;exports.HeartbeatManager=R;exports.IOSInstallBanner=B;exports.IOS_BANNER_TRANSLATIONS=A;exports.IndexedDBStorage=j;exports.InstallationManager=k;exports.NOTIFICATION_PROMPT_TRANSLATIONS=V;exports.NotificationPermissionPrompt=K;exports.OfflineQueueManager=T;exports.PushNotificationManager=x;exports.ServiceWorkerManager=S;exports.VERSION=tt;exports.WakeLockManager=E;exports.createAuthContext=y;exports.default=ve;exports.detectBrowserLanguage=C;exports.deviceDetector=ue;exports.generateAuthHeaders=H;exports.generateBrowserFingerprint=D;exports.generateHMACSignature=$;exports.generateJWTHeaders=z;exports.generateTrackingId=W;exports.getRegistrationId=U;exports.getStorageItem=_;exports.isValidUUID=v;exports.makeAuthenticatedRequest=X;exports.makeAuthenticatedRequestWithContext=h;exports.makeJWTAuthenticatedRequest=Y;exports.removeRegistrationId=M;exports.removeStorageItem=Z;exports.setRegistrationId=F;exports.setStorageItem=O;exports.updateAuthToken=b;Object.defineProperty(exports,'__esModule',{value:true});return exports;})({});
145
+ `,document.body.appendChild(this.promptElement),document.getElementById(`${I}-allow`)?.addEventListener("click",()=>this.handleAllow()),document.getElementById(`${I}-later`)?.addEventListener("click",()=>this.dismiss()),requestAnimationFrame(()=>{this.overlayElement&&(this.overlayElement.style.opacity="1"),this.promptElement&&(this.promptElement.style.opacity="1",this.config.position==="center"?this.promptElement.style.transform="translate(-50%, -50%) scale(1)":this.config.position==="top"?this.promptElement.style.transform="translateX(-50%) translateY(0)":this.promptElement.style.transform="translateX(-50%) translateY(0)");});}log(e,t="log"){if(!this.debug&&t==="log")return;console[t](`[CloudSignal PWA NotificationPrompt] ${e}`);}};P();var at="1.2.0";exports.CloudSignalPWA=W;exports.DeviceDetector=p;exports.HeartbeatManager=E;exports.IOSInstallBanner=D;exports.IOS_BANNER_TRANSLATIONS=C;exports.IndexedDBStorage=V;exports.InstallationManager=k;exports.NOTIFICATION_PROMPT_TRANSLATIONS=J;exports.NotificationPermissionPrompt=Y;exports.OfflineQueueManager=A;exports.PushNotificationManager=R;exports.ServiceWorkerManager=S;exports.VERSION=at;exports.WakeLockManager=T;exports.createAuthContext=v;exports.default=ke;exports.detectBrowserLanguage=B;exports.deviceDetector=me;exports.generateAuthHeaders=U;exports.generateBrowserFingerprint=M;exports.generateHMACSignature=Q;exports.generateJWTHeaders=F;exports.generateTrackingId=_;exports.getRegistrationId=G;exports.getStorageItem=N;exports.isValidUUID=m;exports.makeAuthenticatedRequest=ee;exports.makeAuthenticatedRequestWithContext=h;exports.makeJWTAuthenticatedRequest=ie;exports.removeRegistrationId=L;exports.removeStorageItem=ne;exports.setRegistrationId=X;exports.setStorageItem=x;exports.updateAuthToken=y;Object.defineProperty(exports,'__esModule',{value:true});return exports;})({});
package/dist/index.js CHANGED
@@ -1038,6 +1038,14 @@ function markInstallationRegistered(organizationId, serviceId, registrationId) {
1038
1038
  setStorageItem(`install_id_${organizationId}_${serviceId}`, registrationId);
1039
1039
  return setStorageItem(key, true);
1040
1040
  }
1041
+ function getLastRegisteredIdentity(organizationId, serviceId) {
1042
+ const key = `last_identity_${organizationId}_${serviceId}`;
1043
+ return getStorageItem(key);
1044
+ }
1045
+ function setLastRegisteredIdentity(organizationId, serviceId, identity) {
1046
+ const key = `last_identity_${organizationId}_${serviceId}`;
1047
+ return setStorageItem(key, identity);
1048
+ }
1041
1049
  var IndexedDBStorage = class {
1042
1050
  constructor(dbName = "CloudSignalPWA", dbVersion = 1) {
1043
1051
  this.db = null;
@@ -1308,6 +1316,27 @@ var PushNotificationManager = class {
1308
1316
  this.onPermissionDenied?.();
1309
1317
  return null;
1310
1318
  }
1319
+ const incomingEmail = options.userEmail ?? null;
1320
+ const incomingUserId = options.userId ?? null;
1321
+ const stored = getLastRegisteredIdentity(this.organizationId, this.serviceId);
1322
+ if (stored) {
1323
+ const emailChanged = stored.email !== null && incomingEmail !== null && stored.email !== incomingEmail;
1324
+ const userIdChanged = stored.userId !== null && incomingUserId !== null && stored.userId !== incomingUserId;
1325
+ if (emailChanged || userIdChanged) {
1326
+ this.log(
1327
+ `Identity changed on this device (was ${stored.email || stored.userId}, now ${incomingEmail || incomingUserId}); dropping stale push subscription`
1328
+ );
1329
+ try {
1330
+ const stale = await this.serviceWorkerRegistration.pushManager.getSubscription();
1331
+ if (stale) {
1332
+ await stale.unsubscribe();
1333
+ this.pushSubscription = null;
1334
+ }
1335
+ } catch (err) {
1336
+ this.log(`Stale subscription unsubscribe failed: ${err}`, "error");
1337
+ }
1338
+ }
1339
+ }
1311
1340
  const subscription = await this.subscribeToPush();
1312
1341
  if (!subscription) {
1313
1342
  throw new Error("Failed to subscribe to push notifications");
@@ -1361,6 +1390,10 @@ var PushNotificationManager = class {
1361
1390
  const result = await response.json();
1362
1391
  this.registrationId = result.registration_id;
1363
1392
  setRegistrationId(this.organizationId, this.serviceId, this.registrationId);
1393
+ setLastRegisteredIdentity(this.organizationId, this.serviceId, {
1394
+ email: incomingEmail,
1395
+ userId: incomingUserId
1396
+ });
1364
1397
  const registration = {
1365
1398
  registrationId: result.registration_id,
1366
1399
  status: result.status || "active",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudsignal/pwa-sdk",
3
- "version": "2.1.2",
3
+ "version": "2.1.3",
4
4
  "description": "CloudSignal PWA SDK - Progressive Web App features with push notifications, JWT/HMAC authentication, installation management, device tracking, offline queue, wake lock, and notification analytics",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",