webhanger-front 1.0.15 → 1.0.17

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/browser.min.js CHANGED
@@ -1 +1 @@
1
- !function(e){const t="components",n="wh2_",r=[],o={};function a(e,t){o[e]||(o[e]=[]),o[e].push(t)}function s(e,t){(o[e]||[]).forEach(e=>e(t))}const c={loads:0,cacheHits:0,errors:0,totalTime:0};function i(e,t){e in c&&(c[e]+=t),s("metric",{name:e,value:t,metrics:{...c}})}const l=new Map;async function d(e,t,n){if(!e)return"";try{const r=e.split(":");if(3!==r.length)return console.warn("[WebHanger] decryptChunk: unexpected format, parts:",r.length),"";const[o,a,s]=r,c=Uint8Array.from(atob(o),e=>e.charCodeAt(0)),i=Uint8Array.from(atob(a),e=>e.charCodeAt(0)),d=Uint8Array.from(atob(s),e=>e.charCodeAt(0)),u=new Uint8Array(d.length+i.length);u.set(d),u.set(i,d.length);const h=await async function(e,t){const n=e+t;if(l.has(n))return l.get(n);const r=new TextEncoder,o=await crypto.subtle.digest("SHA-256",r.encode(e+t)),a=await crypto.subtle.importKey("raw",o,{name:"AES-GCM"},!1,["decrypt"]);return l.set(n,a),a}(t,n),f=await crypto.subtle.decrypt({name:"AES-GCM",iv:c},h,u);return(new TextDecoder).decode(f)}catch(e){return console.warn("[WebHanger] decryptChunk failed:",n,e.message),""}}function u(){return new Promise((e,n)=>{const r=indexedDB.open("webhanger_v2",1);r.onupgradeneeded=e=>e.target.result.createObjectStore(t),r.onsuccess=t=>e(t.target.result),r.onerror=()=>n(r.error)})}async function h(e,r){try{if(r.length<51200)return void localStorage.setItem(n+e,r)}catch(e){}await async function(e,n){const r=await u();return new Promise((o,a)=>{const s=r.transaction(t,"readwrite").objectStore(t).put(n,e);s.onsuccess=()=>o(),s.onerror=()=>a(s.error)})}(e,r)}async function f(e,r){const o=await async function(e){try{const t=localStorage.getItem(n+e);if(t)return t}catch(e){}return await async function(e){const n=await u();return new Promise((r,o)=>{const a=n.transaction(t,"readonly").objectStore(t).get(e);a.onsuccess=()=>r(a.result),a.onerror=()=>o(a.error)})}(e)}(e);if(o)return i("cacheHits",1),r().then(t=>{t&&t!==o&&h(e,t)}).catch(()=>{}),{data:o,source:"cache"};const a=await r();return a&&await h(e,a),{data:a,source:"cdn"}}async function p(e,t,n){const r=Array.isArray(e)?e:[e];let o;for(const e of r)try{const r=n?`${e}?token=${t}&expires=${n}`:`${e}?token=${t}`,o=await fetch(r);if(!o.ok)throw Error("HTTP "+o.status);return await o.text()}catch(t){console.warn(`[WebHanger] CDN failed (${e}): ${t.message} \u2014 trying next...`),o=t}throw Error("All CDN endpoints failed. Last error: "+o?.message)}function m(e){return new Promise(t=>{if("style"===e.type?document.querySelector(`link[href="${e.url}"]`):document.querySelector(`script[src="${e.url}"]`))return t();if("style"===e.type){const n=document.createElement("link");n.rel="stylesheet",n.href=e.url,n.onload=t,n.onerror=t,document.head.appendChild(n)}else{const n=document.createElement("script");n.src=e.url,e.defer&&(n.defer=!0),e.async&&(n.async=!0),n.onload=t,n.onerror=t,document.head.appendChild(n)}})}async function g(e=[]){for(const t of e)await m(t)}function w(e,t){return e&&t?e.replace(/\{\{wh\.([^}]+)\}\}/g,function(e,n){return void 0!==t[n]?t[n]+"":""}):e}async function y(e,t){const n=`${e.cdnUrl}@${e.expires}`,{data:r}=await f(n,()=>p(e.cdnUrl,e.token,e.expires));if(!r)return;let o;try{o=JSON.parse(r)}catch(e){return}o.assets&&o.assets.length&&await g(o.assets);const a=await d(o.c,t,"::css");if(a&&!document.querySelector(`style[data-wh-dep="${e.name}@${e.version}"]`)){const t=document.createElement("style");t.setAttribute("data-wh-dep",`${e.name}@${e.version}`),t.textContent=a,document.head.appendChild(t)}const s=await d(o.h,t,"::html");if(s){const t=document.querySelector(`[data-wh-${e.name}]`);t&&(t.innerHTML=s)}const c=await d(o.j,t,"::js");if(c){const e=document.createElement("script");e.textContent=c,document.head.appendChild(e),document.head.removeChild(e)}}async function b(t,n,r,o,a="[data-wh]",c=null,l=[],u={}){if(!t||!n||!r||null==o)return void console.error("[WebHanger] Missing required params");if(0!==o&&Math.floor(Date.now()/1e3)>o)return void console.warn("[WebHanger] Token expired.");const h=function(e){return(t,n={})=>{"function"==typeof e&&e({stage:t,...n}),s(t,n)}}(c),m=function(e){const t=document.querySelector(e||"[data-wh]");if(!t)return{show:()=>{},hide:()=>{}};const n=document.createElement("div");return n.setAttribute("data-wh-loader",""),n.style.cssText="display:flex;align-items:center;justify-content:center;padding:24px;width:100%;box-sizing:border-box;",n.innerHTML='<div style="width:28px;height:28px;border:3px solid #e5e7eb;border-top-color:#6366f1;border-radius:50%;animation:wh-spin 0.7s linear infinite;"></div><style>@keyframes wh-spin{to{transform:rotate(360deg);}}</style>',{show(){t.appendChild(n)},hide(){n.parentNode&&n.parentNode.removeChild(n)}}}(a),b=performance.now();h("start",{cdnUrl:t,selector:a}),m.show(),i("loads",1);const v=`${t}@${o}`;try{h("fetching");const{data:c,source:S}=await f(v,()=>p(t,r,o));let W;try{W=JSON.parse(c)}catch(e){W={}}W.assets&&W.assets.length&&(h("assets",{count:W.assets.length}),await g(W.assets));const C=[...l,...W.dependencies||[]];if(C.length){h("deps",{count:C.length});for(const e of C)"object"==typeof e&&e.cdnUrl&&await y(e,n)}h("injecting"),m.hide(),await async function(t,n,r,o={}){const{sandbox:a=!1,allowedDomains:c,beforeMount:i,afterMount:l,props:u={}}=o,h=document.querySelector(r||"[data-wh]");if(!h)return void console.warn("[WebHanger] No mount target found.");let f;try{f=JSON.parse(t)}catch(e){return void console.error("[WebHanger] Invalid payload.")}if(!function(t){if(!t||!t.length)return!0;const n=e.location?.hostname||"";return t.some(e=>n===e||n.endsWith("."+e))}(c))return console.error("[WebHanger] Domain not allowed: "+e.location?.hostname),void s("error",{reason:"domain_restricted"});const p=await d(f.c,n,"::css");let m=await d(f.h,n,"::html"),g=await d(f.j,n,"::js");if(f.integrity&&(m||g)){const e=await async function(e,t,n,r){if(!r)return!0;const o=(new TextEncoder).encode(e+t+n),a=await crypto.subtle.digest("SHA-256",o);return Array.from(new Uint8Array(a)).map(e=>e.toString(16).padStart(2,"0")).join("")===r}(m,p,g,f.integrity);if(!e)return console.error("[WebHanger] Integrity check failed \u2014 bundle may be tampered or re-deploy needed."),void s("error",{reason:"integrity_failed"})}const y=(v=u,S={},Object.keys((b=f.props)||{}).forEach(function(e){var t=b[e];S[e]=void 0!==t.default?t.default:""}),Object.keys(v||{}).forEach(function(e){var t=e.replace(/^wh-/,"").replace(/-([a-z])/g,function(e,t){return t.toUpperCase()}),n=v[e],r=b&&b[t];if(r&&"json"===r.type)try{n=JSON.parse(n)}catch(e){}S[t]=n}),S);var b,v,S;if(console.log("[WebHanger] props schema:",JSON.stringify(f.props),"| userProps:",JSON.stringify(u),"| resolved:",JSON.stringify(y)),m=w(m,y),g=w(g,y),"function"==typeof i&&i({target:h,html:m,css:p}),s("beforeMount",{target:h,selector:r}),a)!function(e,t,n){const r=e.attachShadow({mode:"closed"});if(n){const e=document.createElement("style");e.textContent=n,r.appendChild(e)}if(t){const e=document.createElement("div");e.innerHTML=t,r.appendChild(e)}}(h,m,p);else{if(p){const e=document.createElement("style");e.textContent=p,document.head.appendChild(e)}m&&(h.innerHTML=m)}if(g){const e=document.createElement("script");e.textContent=g,document.head.appendChild(e),document.head.removeChild(e)}"function"==typeof l&&l({target:h}),s("afterMount",{target:h,selector:r})}(c,n,a,u);const E=Math.round(performance.now()-b);i("totalTime",E),h("done",{time:E,source:S}),s("load",{cdnUrl:t,time:E,source:S,selector:a})}catch(e){m.hide(),i("errors",1),h("error",{message:e.message}),"function"==typeof u.onError&&u.onError(e),console.error("[WebHanger] Load failed:",e.message)}}let v=null,S="./wh-manifest.json";async function W(e="./wh-manifest.json"){S=e;const t=await fetch(e);v=await t.json(),document.querySelectorAll("wh-component[name]").forEach(e=>e._load())}"undefined"!=typeof customElements&&customElements.define("wh-component",class extends HTMLElement{async connectedCallback(){v&&await this._load()}async _load(){const e=this.getAttribute("name"),t=this.hasAttribute("sandbox");if(!e)return void console.error("[WebHanger] <wh-component> missing 'name' attribute");const n={};for(const e of this.attributes)e.name.startsWith("wh-")&&"wh-component"!==e.name&&(n[e.name]=e.value);const r=v||await fetch(this.getAttribute("src")||S).then(e=>e.json()),o=r.components[e];if(!o)return void console.error(`[WebHanger] <wh-component>: "${e}" not found in manifest`);this.setAttribute("data-wh-el",e);const a=`wh-component[data-wh-el="${e}"]`;await b(o.urls||o.url,r.pid,o.token,o.expires,a,null,[],{sandbox:t,props:n})}});const C={supported:!1,adapter:null,device:null},E="wh_last_updated";!async function(){if(!navigator.gpu)return!1;try{const e=await navigator.gpu.requestAdapter();if(!e)return!1;const t=await e.requestDevice();return C.supported=!0,C.adapter=e,C.device=t,s("gpu",{supported:!0,adapter:e}),!0}catch(e){return!1}}(),e.WebHangerFront={load:b,clearCache:async function(){Object.keys(localStorage).filter(e=>e.startsWith(n)).forEach(e=>localStorage.removeItem(e));try{(await u()).transaction(t,"readwrite").objectStore(t).clear()}catch(e){}if("caches"in window){const e=await caches.keys();await Promise.all(e.map(e=>caches.delete(e)))}if("serviceWorker"in navigator){const e=await navigator.serviceWorker.getRegistrations();await Promise.all(e.map(e=>e.unregister()))}Object.keys(sessionStorage).filter(e=>e.startsWith(n)).forEach(e=>sessionStorage.removeItem(e))},use:function(e){"function"==typeof e.install&&e.install({on:a,emit:s}),r.push(e)},on:a,metrics:c,initialize:W,smartInitialize:async function(e,r){e=e||"./wh-manifest.json",r=r||"http://localhost:5000";var o=!1;try{var a=await fetch(r+"/api/last-updated",{signal:AbortSignal.timeout(2e3)}),c=(await a.json()).lastUpdatedAt||0,i=parseInt(localStorage.getItem(E)||"0");c>i?(o=!0,localStorage.setItem(E,c+""),s("cache-invalidated",{reason:"components_updated",serverTs:c,cachedTs:i}),console.log("[WebHanger] Components updated \u2014 refreshing cache")):(s("cache-hit",{reason:"up_to_date",serverTs:c,cachedTs:i}),console.log("[WebHanger] Up to date \u2014 loading from cache"))}catch(e){console.log("[WebHanger] Admin server unreachable \u2014 using cache")}if(o)try{Object.keys(localStorage).filter(function(e){return e.startsWith(n)}).forEach(function(e){localStorage.removeItem(e)}),(await u()).transaction(t,"readwrite").objectStore(t).clear()}catch(e){}await W(e)},registerSW:async function(e="/webhanger.sw.js"){if("serviceWorker"in navigator)try{s("sw",{registered:!0,scope:(await navigator.serviceWorker.register(e)).scope})}catch(e){console.warn("[WebHanger] SW registration failed:",e.message)}},setOfflinePage:async function(e="",t=""){if(!("serviceWorker"in navigator))return;const n=await navigator.serviceWorker.ready;n.active?.postMessage({type:"SET_OFFLINE_PAGE",html:e,css:t})},gpu:C,version:"2.0.0"}}(window);
1
+ !function(e){const t="components",n="wh2_",r=[],o={};function a(e,t){o[e]||(o[e]=[]),o[e].push(t)}function s(e,t){(o[e]||[]).forEach(e=>e(t))}const i={loads:0,cacheHits:0,errors:0,totalTime:0};function c(e,t){e in i&&(i[e]+=t),s("metric",{name:e,value:t,metrics:{...i}})}const l=new Map;async function d(e,t,n){if(!e)return"";try{const r=e.split(":");if(3!==r.length)return console.warn("[WebHanger] decryptChunk: unexpected format, parts:",r.length),"";const[o,a,s]=r,i=Uint8Array.from(atob(o),e=>e.charCodeAt(0)),c=Uint8Array.from(atob(a),e=>e.charCodeAt(0)),d=Uint8Array.from(atob(s),e=>e.charCodeAt(0)),u=new Uint8Array(d.length+c.length);u.set(d),u.set(c,d.length);const f=await async function(e,t){const n=e+t;if(l.has(n))return l.get(n);const r=new TextEncoder,o=await crypto.subtle.digest("SHA-256",r.encode(e+t)),a=await crypto.subtle.importKey("raw",o,{name:"AES-GCM"},!1,["decrypt"]);return l.set(n,a),a}(t,n),h=await crypto.subtle.decrypt({name:"AES-GCM",iv:i},f,u);return(new TextDecoder).decode(h)}catch(e){return console.warn("[WebHanger] decryptChunk failed:",n,e.message),""}}function u(){return new Promise((e,n)=>{const r=indexedDB.open("webhanger_v2",1);r.onupgradeneeded=e=>e.target.result.createObjectStore(t),r.onsuccess=t=>e(t.target.result),r.onerror=()=>n(r.error)})}async function f(e,r){try{if(r.length<51200)return void localStorage.setItem(n+e,r)}catch(e){}await async function(e,n){const r=await u();return new Promise((o,a)=>{const s=r.transaction(t,"readwrite").objectStore(t).put(n,e);s.onsuccess=()=>o(),s.onerror=()=>a(s.error)})}(e,r)}async function h(e,r){const o=await async function(e){try{const t=localStorage.getItem(n+e);if(t)return t}catch(e){}return await async function(e){const n=await u();return new Promise((r,o)=>{const a=n.transaction(t,"readonly").objectStore(t).get(e);a.onsuccess=()=>r(a.result),a.onerror=()=>o(a.error)})}(e)}(e);if(o)return c("cacheHits",1),r().then(t=>{t&&t!==o&&f(e,t)}).catch(()=>{}),{data:o,source:"cache"};const a=await r();return a&&await f(e,a),{data:a,source:"cdn"}}async function p(e,t,n){const r=Array.isArray(e)?e:[e];let o;for(const e of r)try{const r=n?`${e}?token=${t}&expires=${n}`:`${e}?token=${t}`,o=await fetch(r);if(!o.ok)throw Error("HTTP "+o.status);return await o.text()}catch(t){console.warn(`[WebHanger] CDN failed (${e}): ${t.message} \u2014 trying next...`),o=t}throw Error("All CDN endpoints failed. Last error: "+o?.message)}function m(e){return new Promise(t=>{if("style"===e.type?document.querySelector(`link[href="${e.url}"]`):document.querySelector(`script[src="${e.url}"]`))return t();if("style"===e.type){const n=document.createElement("link");n.rel="stylesheet",n.href=e.url,n.onload=t,n.onerror=t,document.head.appendChild(n)}else{const n=document.createElement("script");n.src=e.url,e.defer&&(n.defer=!0),e.async&&(n.async=!0),n.onload=t,n.onerror=t,document.head.appendChild(n)}})}async function g(e=[]){for(const t of e)await m(t)}function w(e,t){return e&&t?e.replace(/\{\{wh\.([^}]+)\}\}/g,function(e,n){return void 0!==t[n]?t[n]+"":""}):e}async function y(e,t){const n=`${e.cdnUrl}@${e.expires}`,{data:r}=await h(n,()=>p(e.cdnUrl,e.token,e.expires));if(!r)return;let o;try{o=JSON.parse(r)}catch(e){return}o.assets&&o.assets.length&&await g(o.assets);const a=await d(o.c,t,"::css");if(a&&!document.querySelector(`style[data-wh-dep="${e.name}@${e.version}"]`)){const t=document.createElement("style");t.setAttribute("data-wh-dep",`${e.name}@${e.version}`),t.textContent=a,document.head.appendChild(t)}const s=await d(o.h,t,"::html");if(s){const t=document.querySelector(`[data-wh-${e.name}]`);t&&(t.innerHTML=s)}const i=await d(o.j,t,"::js");if(i){const e=document.createElement("script");e.textContent=i,document.head.appendChild(e),document.head.removeChild(e)}}async function b(t,n,r,o,a="[data-wh]",i=null,l=[],u={}){if(!t||!n||!r||null==o)return void console.error("[WebHanger] Missing required params");if(0!==o&&Math.floor(Date.now()/1e3)>o)return void console.warn("[WebHanger] Token expired.");const f=function(e){return(t,n={})=>{"function"==typeof e&&e({stage:t,...n}),s(t,n)}}(i),m=function(e){const t=document.querySelector(e||"[data-wh]");if(!t)return{show:()=>{},hide:()=>{}};const n=document.createElement("div");return n.setAttribute("data-wh-loader",""),n.style.cssText="display:flex;align-items:center;justify-content:center;padding:24px;width:100%;box-sizing:border-box;",n.innerHTML='<div style="width:28px;height:28px;border:3px solid #e5e7eb;border-top-color:#6366f1;border-radius:50%;animation:wh-spin 0.7s linear infinite;"></div><style>@keyframes wh-spin{to{transform:rotate(360deg);}}</style>',{show(){t.appendChild(n)},hide(){n.parentNode&&n.parentNode.removeChild(n)}}}(a),b=performance.now();f("start",{cdnUrl:t,selector:a}),m.show(),c("loads",1);const v=`${t}@${o}`;try{f("fetching");const{data:i,source:S}=await h(v,()=>p(t,r,o));let x;try{x=JSON.parse(i)}catch(e){x={}}x.assets&&x.assets.length&&(f("assets",{count:x.assets.length}),await g(x.assets));const C=[...l,...x.dependencies||[]];if(C.length){f("deps",{count:C.length});for(const e of C)"object"==typeof e&&e.cdnUrl&&await y(e,n)}f("injecting"),m.hide(),await async function(t,n,r,o={}){const{sandbox:a=!1,allowedDomains:i,beforeMount:c,afterMount:l,props:u={}}=o,f=document.querySelector(r||"[data-wh]");if(!f)return void console.warn("[WebHanger] No mount target found.");let h;try{h=JSON.parse(t)}catch(e){return void console.error("[WebHanger] Invalid payload.")}if(!function(t){if(!t||!t.length)return!0;const n=e.location?.hostname||"";return t.some(e=>n===e||n.endsWith("."+e))}(i))return console.error("[WebHanger] Domain not allowed: "+e.location?.hostname),void s("error",{reason:"domain_restricted"});const p=await d(h.c,n,"::css");let m=await d(h.h,n,"::html"),g=await d(h.j,n,"::js");if(h.integrity&&(m||g)){const e=await async function(e,t,n,r){if(!r)return!0;const o=(new TextEncoder).encode(e+t+n),a=await crypto.subtle.digest("SHA-256",o);return Array.from(new Uint8Array(a)).map(e=>e.toString(16).padStart(2,"0")).join("")===r}(m,p,g,h.integrity);if(!e)return console.error("[WebHanger] Integrity check failed \u2014 bundle may be tampered or re-deploy needed."),void s("error",{reason:"integrity_failed"})}const y=(v=u,S={},Object.keys((b=h.props)||{}).forEach(function(e){var t=b[e];S[e]=void 0!==t.default?t.default:""}),Object.keys(v||{}).forEach(function(e){var t=e.replace(/^wh-/,"").replace(/-([a-z])/g,function(e,t){return t.toUpperCase()}),n=v[e],r=b&&b[t];if(r&&"json"===r.type)try{n=JSON.parse(n)}catch(e){}S[t]=n}),S);var b,v,S;if(console.log("[WebHanger] props schema:",JSON.stringify(h.props),"| userProps:",JSON.stringify(u),"| resolved:",JSON.stringify(y)),m=w(m,y),g=w(g,y),"function"==typeof c&&c({target:f,html:m,css:p}),s("beforeMount",{target:f,selector:r}),a)!function(e,t,n){const r=e.attachShadow({mode:"closed"});if(n){const e=document.createElement("style");e.textContent=n,r.appendChild(e)}if(t){const e=document.createElement("div");e.innerHTML=t,r.appendChild(e)}}(f,m,p);else{if(p){const e=document.createElement("style");e.textContent=p,document.head.appendChild(e)}m&&(f.innerHTML=m)}if(g){const e=document.createElement("script");e.textContent=g,document.head.appendChild(e),document.head.removeChild(e)}"function"==typeof l&&l({target:f}),s("afterMount",{target:f,selector:r})}(i,n,a,u);const W=Math.round(performance.now()-b);c("totalTime",W),f("done",{time:W,source:S}),s("load",{cdnUrl:t,time:W,source:S,selector:a})}catch(e){m.hide(),c("errors",1),f("error",{message:e.message}),"function"==typeof u.onError&&u.onError(e),console.error("[WebHanger] Load failed:",e.message)}}let v=null,S="./wh-manifest.json";async function x(e="./wh-manifest.json"){S=e;const t=await fetch(e);v=await t.json(),document.querySelectorAll("wh-component[name]").forEach(e=>e._load())}"undefined"!=typeof customElements&&customElements.define("wh-component",class extends HTMLElement{async connectedCallback(){v&&await this._load()}async _load(){let e=this.getAttribute("name");const t=this.hasAttribute("sandbox");if(this.hasAttribute("personalize")&&W){const t=H(e);t!==e&&(console.log("[WebHanger] Personalized: "+e+" \u2192 "+t),e=t)}if(!e)return void console.error("[WebHanger] <wh-component> missing 'name' attribute");const n={};for(const e of this.attributes)e.name.startsWith("wh-")&&"wh-component"!==e.name&&(n[e.name]=e.value);const r=v||await fetch(this.getAttribute("src")||S).then(e=>e.json()),o=r.components[e];if(!o)return void console.error(`[WebHanger] <wh-component>: "${e}" not found in manifest`);this.setAttribute("data-wh-el",e);const a=`wh-component[data-wh-el="${e}"]`;await b(o.urls||o.url,r.pid,o.token,o.expires,a,null,[],{sandbox:t,props:n})}});const C={supported:!1,adapter:null,device:null};var W=null;function A(e,t){for(var n in e){var r=e[n],o=t[n];if(Array.isArray(r)){if(-1===r.indexOf(o))return!1}else if("object"==typeof r&&null!==r){if(void 0!==r.min&&o<r.min)return!1;if(void 0!==r.max&&o>r.max)return!1}else if(("string"==typeof o?o.toLowerCase():o)!==("string"==typeof r?r.toLowerCase():r))return!1}return!0}function H(e,t){if(!W||!W[e])return e;for(var n=W[e],r="undefined"!=typeof window&&window._wh_ctx_override||{},o=function(e){e=e||{};var t=navigator.userAgent||"",n=/tablet|ipad/i.test(t)?"tablet":/mobile|android|iphone/i.test(t)?"mobile":"desktop",r=null;try{var o=localStorage.getItem("wh_auth_token");o&&(r=JSON.parse(atob(o.split(".")[1])).role||null)}catch(e){}var a=localStorage.getItem("wh_ab_bucket");return a||(a=Math.random()<.5?"variant-a":"variant-b",localStorage.setItem("wh_ab_bucket",a)),{country:e.country||null,device:e.device||n,lang:e.lang||(navigator.language||"en").split("-")[0],role:e.role||r,abTest:e.abTest||a,hour:void 0!==e.hour?e.hour:(new Date).getHours(),plan:e.plan||localStorage.getItem("wh_plan")||"free"}}(Object.assign({},r,t||{})),a=0;a<(n.rules||[]).length;a++)if(A(n.rules[a].if||{},o)){var i=n.rules[a].component;return s("personalized",{slot:e,resolved:i,ctx:o}),i}return n.default||e}const j="wh_last_updated";!async function(){if(!navigator.gpu)return!1;try{const e=await navigator.gpu.requestAdapter();if(!e)return!1;const t=await e.requestDevice();return C.supported=!0,C.adapter=e,C.device=t,s("gpu",{supported:!0,adapter:e}),!0}catch(e){return!1}}(),e.WebHangerFront={load:b,clearCache:async function(){Object.keys(localStorage).filter(e=>e.startsWith(n)).forEach(e=>localStorage.removeItem(e));try{(await u()).transaction(t,"readwrite").objectStore(t).clear()}catch(e){}if("caches"in window){const e=await caches.keys();await Promise.all(e.map(e=>caches.delete(e)))}if("serviceWorker"in navigator){const e=await navigator.serviceWorker.getRegistrations();await Promise.all(e.map(e=>e.unregister()))}Object.keys(sessionStorage).filter(e=>e.startsWith(n)).forEach(e=>sessionStorage.removeItem(e))},use:function(e){"function"==typeof e.install&&e.install({on:a,emit:s}),r.push(e)},on:a,metrics:i,initialize:x,smartInitialize:async function(e,r){e=e||"./wh-manifest.json",r=r||"http://localhost:5000";var o=!1;try{var a=await fetch(r+"/api/last-updated",{signal:AbortSignal.timeout(2e3)}),i=(await a.json()).lastUpdatedAt||0,c=parseInt(localStorage.getItem(j)||"0");i>c?(o=!0,localStorage.setItem(j,i+""),s("cache-invalidated",{reason:"components_updated",serverTs:i,cachedTs:c}),console.log("[WebHanger] Components updated \u2014 refreshing cache")):(s("cache-hit",{reason:"up_to_date",serverTs:i,cachedTs:c}),console.log("[WebHanger] Up to date \u2014 loading from cache"))}catch(e){console.log("[WebHanger] Admin server unreachable \u2014 using cache")}if(o)try{Object.keys(localStorage).filter(function(e){return e.startsWith(n)}).forEach(function(e){localStorage.removeItem(e)}),(await u()).transaction(t,"readwrite").objectStore(t).clear()}catch(e){}await x(e)},loadPersonalization:async function(e){e=e||"./wh-personalization.json";try{var t=await fetch(e);s("personalization-loaded",{rules:W=await t.json()})}catch(t){console.warn("[WebHanger] Could not load personalization rules from "+e)}},resolvePersonalizedComponent:H,registerSW:async function(e="/webhanger.sw.js"){if("serviceWorker"in navigator)try{s("sw",{registered:!0,scope:(await navigator.serviceWorker.register(e)).scope})}catch(e){console.warn("[WebHanger] SW registration failed:",e.message)}},setOfflinePage:async function(e="",t=""){if(!("serviceWorker"in navigator))return;const n=await navigator.serviceWorker.ready;n.active?.postMessage({type:"SET_OFFLINE_PAGE",html:e,css:t})},gpu:C,version:"2.0.0"}}(window);
package/index.js CHANGED
@@ -403,4 +403,6 @@ export async function smartInitialize(manifestUrl = "./wh-manifest.json", adminS
403
403
  await initialize(manifestUrl);
404
404
  }
405
405
 
406
+ export { resolveAll as resolvePersonalization, buildContext as buildPersonalizationContext } from "../helper/personalization.js";
407
+
406
408
  export { VERSION as version };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webhanger-front",
3
- "version": "1.0.15",
3
+ "version": "1.0.17",
4
4
  "description": "WebHanger browser SDK — load encrypted UI components from CDN",
5
5
  "main": "index.js",
6
6
  "module": "index.js",