@givefreely/adunit 1.1.2-rc.3 → 1.1.2-rc.4
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";class e{log({level:t,message:r,timestamp:i,data:s,title:a}){const n="debug"===t?"log":t,o=a?`[${a}]`:"";void 0!==s?console[n](`[${i}] - ${e.LOGGER_PREFIX} ${o} ${r}`,s):console[n](`[${i}] - ${e.LOGGER_PREFIX} ${o} ${r}`)}}var t,r;e.LOGGER_PREFIX="[GFAdUnit] -",function(e){e.checkoutPopupShown="CHECKOUT-POPUP-SHOWN",e.checkoutPopupNotShown="CHECKOUT-POPUP-NOT-SHOWN",e.checkoutPopupSupressed="CHECKOUT-POPUP-SUPRESSED",e.checkoutPopupDonation="CHECKOUT-POPUP-DONATION",e.checkoutPopupFailedActivation="CHECKOUT-POPUP-FAILED-ACTIVATION",e.checkoutPopupPurchaseConfirmed="CHECKOUT-POPUP-PURCHASE-CONFIRMED",e.checkoutPopupHealthCheck="CHECKOUT-POPUP-HEALTH-CHECK",e.checkouPopupActivationFailed="CHECKOUT-POPUP-ACTIVATION-FAILED",e.checkoutPopupOfferDetailsClicked="CHECKOUT-POPUP-OFFER-DETAILS-CLICKED"}(t||(t={})),function(e){e.adUnitLog="ADUNIT-LOG"}(r||(r={}));class i{constructor(e){this.gfService=e}log({level:e,message:t,data:i,title:s}){this.gfService.trackEvent(r.adUnitLog,{log:{level:e,message:t,data:i,title:s}})}}const s=["debug","info","warn","error"];class a{constructor(t){this.config={minLevel:t?.minLevel??"debug",enabled:t?.enabled??!1,transports:t?.transports??[new e],title:t?.title??""}}static getInstance(e){return a.instance||(a.instance=new a(e)),a.instance}configure(e){this.config={...this.config,...e}}shouldLog(e){return!!this.config.enabled&&s.indexOf(e)>=s.indexOf(this.config.minLevel)}createLogMessage(e,t,r){return{level:e,version:"1.1.2",message:t,timestamp:(new Date).toISOString(),data:r,title:this.config.title}}log(e,t,r){if(!this.shouldLog(e))return;const i=this.createLogMessage(e,t,r);this.config.transports.forEach((e=>e.log(i)))}debug(e,t){this.log("debug",e,t)}info(e,t){this.log("info",e,t)}warn(e,t){this.log("warn",e,t)}error(e,t){this.log("error",e,t)}addTransport(e){this.config.transports.push(e)}clearTransports(){this.config.transports=[]}disable(){this.config.enabled=!1}enable(){this.config.enabled=!0}}const n=3,o=e=>1e3*e;async function g(e,t={}){const{maxAttempts:r=n,delayFn:i=o}=t;let s=null;for(let t=1;t<=r;t+=1)try{return await e()}catch(e){s=e,t<r&&await new Promise((e=>{setTimeout(e,i(t))}))}throw s||new Error("Operation failed after multiple attempts")}const c=chrome;function l(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self&&self;var h={exports:{}};"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,function(e){if(!(globalThis.chrome&&globalThis.chrome.runtime&&globalThis.chrome.runtime.id))throw new Error("This script should only be loaded in a browser extension.");if(globalThis.browser&&globalThis.browser.runtime&&globalThis.browser.runtime.id)e.exports=globalThis.browser;else{const t="The message port closed before a response was received.",r=e=>{const r={alarms:{clear:{minArgs:0,maxArgs:1},clearAll:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getAll:{minArgs:0,maxArgs:0}},bookmarks:{create:{minArgs:1,maxArgs:1},get:{minArgs:1,maxArgs:1},getChildren:{minArgs:1,maxArgs:1},getRecent:{minArgs:1,maxArgs:1},getSubTree:{minArgs:1,maxArgs:1},getTree:{minArgs:0,maxArgs:0},move:{minArgs:2,maxArgs:2},remove:{minArgs:1,maxArgs:1},removeTree:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1},update:{minArgs:2,maxArgs:2}},browserAction:{disable:{minArgs:0,maxArgs:1,fallbackToNoCallback:!0},enable:{minArgs:0,maxArgs:1,fallbackToNoCallback:!0},getBadgeBackgroundColor:{minArgs:1,maxArgs:1},getBadgeText:{minArgs:1,maxArgs:1},getPopup:{minArgs:1,maxArgs:1},getTitle:{minArgs:1,maxArgs:1},openPopup:{minArgs:0,maxArgs:0},setBadgeBackgroundColor:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setBadgeText:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setIcon:{minArgs:1,maxArgs:1},setPopup:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setTitle:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},browsingData:{remove:{minArgs:2,maxArgs:2},removeCache:{minArgs:1,maxArgs:1},removeCookies:{minArgs:1,maxArgs:1},removeDownloads:{minArgs:1,maxArgs:1},removeFormData:{minArgs:1,maxArgs:1},removeHistory:{minArgs:1,maxArgs:1},removeLocalStorage:{minArgs:1,maxArgs:1},removePasswords:{minArgs:1,maxArgs:1},removePluginData:{minArgs:1,maxArgs:1},settings:{minArgs:0,maxArgs:0}},commands:{getAll:{minArgs:0,maxArgs:0}},contextMenus:{remove:{minArgs:1,maxArgs:1},removeAll:{minArgs:0,maxArgs:0},update:{minArgs:2,maxArgs:2}},cookies:{get:{minArgs:1,maxArgs:1},getAll:{minArgs:1,maxArgs:1},getAllCookieStores:{minArgs:0,maxArgs:0},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}},devtools:{inspectedWindow:{eval:{minArgs:1,maxArgs:2,singleCallbackArg:!1}},panels:{create:{minArgs:3,maxArgs:3,singleCallbackArg:!0},elements:{createSidebarPane:{minArgs:1,maxArgs:1}}}},downloads:{cancel:{minArgs:1,maxArgs:1},download:{minArgs:1,maxArgs:1},erase:{minArgs:1,maxArgs:1},getFileIcon:{minArgs:1,maxArgs:2},open:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},pause:{minArgs:1,maxArgs:1},removeFile:{minArgs:1,maxArgs:1},resume:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1},show:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},extension:{isAllowedFileSchemeAccess:{minArgs:0,maxArgs:0},isAllowedIncognitoAccess:{minArgs:0,maxArgs:0}},history:{addUrl:{minArgs:1,maxArgs:1},deleteAll:{minArgs:0,maxArgs:0},deleteRange:{minArgs:1,maxArgs:1},deleteUrl:{minArgs:1,maxArgs:1},getVisits:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1}},i18n:{detectLanguage:{minArgs:1,maxArgs:1},getAcceptLanguages:{minArgs:0,maxArgs:0}},identity:{launchWebAuthFlow:{minArgs:1,maxArgs:1}},idle:{queryState:{minArgs:1,maxArgs:1}},management:{get:{minArgs:1,maxArgs:1},getAll:{minArgs:0,maxArgs:0},getSelf:{minArgs:0,maxArgs:0},setEnabled:{minArgs:2,maxArgs:2},uninstallSelf:{minArgs:0,maxArgs:1}},notifications:{clear:{minArgs:1,maxArgs:1},create:{minArgs:1,maxArgs:2},getAll:{minArgs:0,maxArgs:0},getPermissionLevel:{minArgs:0,maxArgs:0},update:{minArgs:2,maxArgs:2}},pageAction:{getPopup:{minArgs:1,maxArgs:1},getTitle:{minArgs:1,maxArgs:1},hide:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setIcon:{minArgs:1,maxArgs:1},setPopup:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setTitle:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},show:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},permissions:{contains:{minArgs:1,maxArgs:1},getAll:{minArgs:0,maxArgs:0},remove:{minArgs:1,maxArgs:1},request:{minArgs:1,maxArgs:1}},runtime:{getBackgroundPage:{minArgs:0,maxArgs:0},getPlatformInfo:{minArgs:0,maxArgs:0},openOptionsPage:{minArgs:0,maxArgs:0},requestUpdateCheck:{minArgs:0,maxArgs:0},sendMessage:{minArgs:1,maxArgs:3},sendNativeMessage:{minArgs:2,maxArgs:2},setUninstallURL:{minArgs:1,maxArgs:1}},sessions:{getDevices:{minArgs:0,maxArgs:1},getRecentlyClosed:{minArgs:0,maxArgs:1},restore:{minArgs:0,maxArgs:1}},storage:{local:{clear:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}},managed:{get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1}},sync:{clear:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}}},tabs:{captureVisibleTab:{minArgs:0,maxArgs:2},create:{minArgs:1,maxArgs:1},detectLanguage:{minArgs:0,maxArgs:1},discard:{minArgs:0,maxArgs:1},duplicate:{minArgs:1,maxArgs:1},executeScript:{minArgs:1,maxArgs:2},get:{minArgs:1,maxArgs:1},getCurrent:{minArgs:0,maxArgs:0},getZoom:{minArgs:0,maxArgs:1},getZoomSettings:{minArgs:0,maxArgs:1},goBack:{minArgs:0,maxArgs:1},goForward:{minArgs:0,maxArgs:1},highlight:{minArgs:1,maxArgs:1},insertCSS:{minArgs:1,maxArgs:2},move:{minArgs:2,maxArgs:2},query:{minArgs:1,maxArgs:1},reload:{minArgs:0,maxArgs:2},remove:{minArgs:1,maxArgs:1},removeCSS:{minArgs:1,maxArgs:2},sendMessage:{minArgs:2,maxArgs:3},setZoom:{minArgs:1,maxArgs:2},setZoomSettings:{minArgs:1,maxArgs:2},update:{minArgs:1,maxArgs:2}},topSites:{get:{minArgs:0,maxArgs:0}},webNavigation:{getAllFrames:{minArgs:1,maxArgs:1},getFrame:{minArgs:1,maxArgs:1}},webRequest:{handlerBehaviorChanged:{minArgs:0,maxArgs:0}},windows:{create:{minArgs:0,maxArgs:1},get:{minArgs:1,maxArgs:2},getAll:{minArgs:0,maxArgs:1},getCurrent:{minArgs:0,maxArgs:1},getLastFocused:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},update:{minArgs:2,maxArgs:2}}};if(0===Object.keys(r).length)throw new Error("api-metadata.json has not been included in browser-polyfill");class i extends WeakMap{constructor(e,t=void 0){super(t),this.createItem=e}get(e){return this.has(e)||this.set(e,this.createItem(e)),super.get(e)}}const s=(t,r)=>(...i)=>{e.runtime.lastError?t.reject(new Error(e.runtime.lastError.message)):r.singleCallbackArg||i.length<=1&&!1!==r.singleCallbackArg?t.resolve(i[0]):t.resolve(i)},a=e=>1==e?"argument":"arguments",n=(e,t,r)=>new Proxy(t,{apply:(t,i,s)=>r.call(i,e,...s)});let o=Function.call.bind(Object.prototype.hasOwnProperty);const g=(e,t={},r={})=>{let i=Object.create(null),c={has:(t,r)=>r in e||r in i,get(c,l,h){if(l in i)return i[l];if(!(l in e))return;let d=e[l];if("function"==typeof d)if("function"==typeof t[l])d=n(e,e[l],t[l]);else if(o(r,l)){let t=((e,t)=>function(r,...i){if(i.length<t.minArgs)throw new Error(`Expected at least ${t.minArgs} ${a(t.minArgs)} for ${e}(), got ${i.length}`);if(i.length>t.maxArgs)throw new Error(`Expected at most ${t.maxArgs} ${a(t.maxArgs)} for ${e}(), got ${i.length}`);return new Promise(((a,n)=>{if(t.fallbackToNoCallback)try{r[e](...i,s({resolve:a,reject:n},t))}catch(s){console.warn(`${e} API method doesn't seem to support the callback parameter, falling back to call it without a callback: `,s),r[e](...i),t.fallbackToNoCallback=!1,t.noCallback=!0,a()}else t.noCallback?(r[e](...i),a()):r[e](...i,s({resolve:a,reject:n},t))}))})(l,r[l]);d=n(e,e[l],t)}else d=d.bind(e);else if("object"==typeof d&&null!==d&&(o(t,l)||o(r,l)))d=g(d,t[l],r[l]);else{if(!o(r,"*"))return Object.defineProperty(i,l,{configurable:!0,enumerable:!0,get:()=>e[l],set(t){e[l]=t}}),d;d=g(d,t[l],r["*"])}return i[l]=d,d},set:(t,r,s,a)=>(r in i?i[r]=s:e[r]=s,!0),defineProperty:(e,t,r)=>Reflect.defineProperty(i,t,r),deleteProperty:(e,t)=>Reflect.deleteProperty(i,t)},l=Object.create(e);return new Proxy(l,c)},c=e=>({addListener(t,r,...i){t.addListener(e.get(r),...i)},hasListener:(t,r)=>t.hasListener(e.get(r)),removeListener(t,r){t.removeListener(e.get(r))}}),l=new i((e=>"function"!=typeof e?e:function(t){const r=g(t,{},{getContent:{minArgs:0,maxArgs:0}});e(r)})),h=new i((e=>"function"!=typeof e?e:function(t,r,i){let s,a,n=!1,o=new Promise((e=>{s=function(t){n=!0,e(t)}}));try{a=e(t,r,s)}catch(e){a=Promise.reject(e)}const g=!0!==a&&((c=a)&&"object"==typeof c&&"function"==typeof c.then);var c;if(!0!==a&&!g&&!n)return!1;return(g?a:o).then((e=>{i(e)}),(e=>{let t;t=e&&(e instanceof Error||"string"==typeof e.message)?e.message:"An unexpected error occurred",i({__mozWebExtensionPolyfillReject__:!0,message:t})})).catch((e=>{console.error("Failed to send onMessage rejected reply",e)})),!0})),d=({reject:r,resolve:i},s)=>{e.runtime.lastError?e.runtime.lastError.message===t?i():r(new Error(e.runtime.lastError.message)):s&&s.__mozWebExtensionPolyfillReject__?r(new Error(s.message)):i(s)},m=(e,t,r,...i)=>{if(i.length<t.minArgs)throw new Error(`Expected at least ${t.minArgs} ${a(t.minArgs)} for ${e}(), got ${i.length}`);if(i.length>t.maxArgs)throw new Error(`Expected at most ${t.maxArgs} ${a(t.maxArgs)} for ${e}(), got ${i.length}`);return new Promise(((e,t)=>{const s=d.bind(null,{resolve:e,reject:t});i.push(s),r.sendMessage(...i)}))},u={devtools:{network:{onRequestFinished:c(l)}},runtime:{onMessage:c(h),onMessageExternal:c(h),sendMessage:m.bind(null,"sendMessage",{minArgs:1,maxArgs:3})},tabs:{sendMessage:m.bind(null,"sendMessage",{minArgs:2,maxArgs:3})}},f={clear:{minArgs:1,maxArgs:1},get:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}};return r.privacy={network:{"*":f},services:{"*":f},websites:{"*":f}},g(e,u,r)};e.exports=r(chrome)}}(h);var d=l(h.exports);class m{constructor(e,t,r,i,s){this.wfSecret=e,this.wfAppId=t,this.deviceUrl=r,this.dataUrl=i,this._logger=s}async getWildfireDevice(){return(await fetch(this.deviceUrl,{method:"POST",body:JSON.stringify({}),headers:{"Content-Type":"application/json","WF-Secret":this.wfSecret,"WF-App-ID":this.wfAppId}})).json()}async getActiveDomains(){return(await fetch(`${this.dataUrl}/${this.wfAppId}/active-domain/1`)).json()}async getStanddownPolicy(){const e=`${this.dataUrl}/${this.wfAppId}/stand-down-policy/1`;try{const t=await fetch(e);if(t.ok)return t.json();throw new Error(`Failed to fetch standdown policy. Status ${t.status}`)}catch(t){return this._logger.error("Failed to fetch the standdown policy",{err:t,url:e}),{Domains:[],LostAttribution:[],Params:[],Serp:[]}}}async getMerchantRates(){const e=`${this.dataUrl}/${this.wfAppId}/merchant-rate/1`;try{const t=await fetch(e);if(this._logger.debug("Merchant rates response",{response:t}),!t.ok)throw new Error(`Failed to fetch merchant rates. Status ${t.status}`);const r=t.json();return this._logger.debug("Merchant rates",{rates:r}),r}catch(t){return this._logger.error("Failed to fetch the merchant rates",{err:t,url:e}),[]}}}const u="GIVE_FREELY_",f={async get(e){const t=`${u}${e}`;return(await d.storage.local.get(t))[t]||null},async set(e,t){const r=`${u}${e}`;await d.storage.local.set({[r]:t})},async remove(e){const t=Array.isArray(e)?e.map((e=>`${u}${e}`)):`${u}${e}`;await d.storage.local.remove(t)}},A="wfDevice",y="wfDomains",p="wfStanddownPolicy",w="wfStanddownHistory",v="wfLastCacheRefresh",x="wfMerchantRates";async function E(e,t,r){try{const i=(await b())?.[t];if(i&&i>Date.now())return e.info("Standing down because current affiliate stand down session has not expired"),S(t),!0;await this.refreshCache();const s=await this.getStanddownPolicy();if(_(s,new URL(r).search))return e.info("Standing down because url has affiliate params"),S(t),!0;e.info("No need to stand down.")}catch(t){e.error("Exception produced when trying to determine if it should stand down",t)}return!1}function C(e,t){if(!this.standDownPolicy)return this.logger.warn("Couldnt check affilliation. No stand down policy found"),this.getStanddownPolicy().then((e=>{this.standDownPolicy=e})),!1;const r=e.some((e=>I(this.standDownPolicy,e)))||_(this.standDownPolicy,t);return this.logger.info("Affilliation result:",r),r}const b=async()=>f.get(w),S=async(e,t=1)=>{const r=await b()??{};r[e]=Date.now()+60*t*60*1e3,await f.set(w,r)},I=(e,t)=>{if(!t)return!1;const{Domains:r}=e;return r.some((e=>t.includes(e)))},_=(e,t)=>{if(!t)return!1;const{Params:r}=e;return r.some((e=>t.toLowerCase().includes(`?${e.toLowerCase()}`)))||r.some((e=>t.toLowerCase().includes(`&${e.toLowerCase()}`)))};class T{constructor(e,t,r,i){this.userId=e,this.charityEin=t,this.charityThirdPartyIdentifier=r,this.partnerId=i}toString(){return`userId=${this.encodeUserId(this.userId)},charityEin=${this.charityEin},charityThirdPartyIdentifier=${this.charityThirdPartyIdentifier},partnerId=${this.partnerId}`}encodeUserId(e){return btoa(e).replace(/_/g,"/").replace(/-/g,"+")}}class U{constructor(e,t,r,i=21600){if(this.standDownPolicy=null,this.shouldStandDown=E.bind(this),this.hasAffilliation=C.bind(this),!e||!r)throw new Error("WildfireService requires wildfireClient and vanityBaseUrl");this.wildfireClient=e,this.vanityBaseUrl=r.replace(/\/$/,""),this.refreshInterval=i,this.logger=t}async getActiveDomains(){try{await this.refreshCache();const e=await f.get(y);if(!e)throw Error("Failed to retrieve active domains");return e}catch(e){throw this.logger.error("Failed to get active domains:",e),new Error("Failed to retrieve active domains")}}async getMerchantRates(e){try{await this.refreshCache();const t=await f.get(x);if(!t)throw new Error("Failed to retrieve merchant rates");return t[e]}catch(e){throw this.logger.error("Failed to retrieve merchant rates:",e),new Error("Failed to retrieve merchant rates")}}async generateAffiliateUrl(e,t,r,i){if(!e||!t||!i)throw new Error("Missing required parameters for affiliate URL generation");try{const s=await this.getDevice(),a=await this.generateTrackingCode(r,i,s),n=encodeURIComponent(t),o=encodeURIComponent(a);return`${this.vanityBaseUrl}/e?d=${s.DeviceID}&c=${e.ID}&tc=${o}&url=${n}`}catch(e){throw this.logger.error("Failed to generate affiliate URL:",e),new Error("Failed to generate affiliate URL")}}async clearCache(){try{await Promise.all([f.remove(A),f.remove(y),f.remove(v),f.remove(p),f.remove(x)])}catch(e){throw this.logger.error("Failed to clear cache:",e),new Error("Failed to clear cache")}}async refreshCache(e=!1){try{const t=await f.get(v),r=Date.now();(e||!t||r-t>1e3*this.refreshInterval)&&(await Promise.all([this.syncActiveDomains(),this.syncStanddownPolicy(),this.syncMerchantRates()]),await f.set(v,r)),this.standDownPolicy=await this.getStanddownPolicy()}catch(e){throw this.logger.error("Failed to refresh cache:",e),new Error("Failed to refresh cache")}}async getStanddownPolicy(){return await f.get(p)??(()=>{throw new Error("Failed to retrieve standdown policy. Storage value was null")})()}getRetryOptions(){return{delayFn:e=>1e3*e,maxAttempts:3}}async getDevice(){try{const e=await f.get(A);return e?.DeviceID?e:g((()=>this.fetchAndStoreDevice()),this.getRetryOptions())}catch(e){throw this.logger.error("Failed to get device:",e),new Error("Failed to retrieve device information")}}async fetchAndStoreDevice(){const e=await this.wildfireClient.getWildfireDevice();if(!e?.DeviceID)throw new Error("Failed to retrieve device information");return await f.set(A,e),e}async generateTrackingCode(e,t,r){if(!(e?.id&&e?.charity&&e?.charity.ein&&e?.charity.thirdPartyId)){const e=`notregistered-${r.DeviceID}`;return this.logger.info("User not provided. Using GF's default bucket instead.",e),e}return new T(e.id,e.charity.ein,e.charity.thirdPartyId,t).toString()}async syncActiveDomains(){const e=await g((()=>this.wildfireClient.getActiveDomains()),this.getRetryOptions());return await f.set(y,e),e}async syncStanddownPolicy(){const e=await g((()=>this.wildfireClient.getStanddownPolicy()),this.getRetryOptions());return this.logger.info("Updated standdown policy",e),await f.set(p,e),e}async syncMerchantRates(){const e=await g((()=>this.wildfireClient.getMerchantRates()),this.getRetryOptions());return this.logger.debug("Merchant rates",e),await f.set(x,e),e}}class P{constructor(e){this.partnerApiKey=e,this.partnerConfig=null,this.globalConfig=null,this.lastConfigRefresh=0,this.CONFIG_REFRESH_INTERVAL=3e5,this.PARTNER_CONFIG_STORAGE_KEY="gf_partner_config",this.GLOBAL_CONFIG_STORAGE_KEY="gf_global_config"}getRetryOptions(){return{delayFn:e=>1e3*e,maxAttempts:3}}async fetchAndUpdatePartnerConfig(){const e=await g((async()=>{const e=await fetch(`https://cdn.givefreely.com/adunit/config/${this.partnerApiKey}.json`,{cache:"no-store"});if(!e.ok)throw new Error(`Failed to fetch partner config: ${e.statusText}`);return e.json()}),this.getRetryOptions()),t={config:e,lastRefresh:Date.now()};return await f.set(this.PARTNER_CONFIG_STORAGE_KEY,t),e}async fetchAndUpdateGlobalConfig(){const e=await g((async()=>{const e=await fetch("https://cdn.givefreely.com/adunit/config/global.json",{cache:"no-store"});if(!e.ok)throw new Error(`Failed to fetch global config: ${e.statusText}`);return e.json()}),this.getRetryOptions()),t={config:e,lastRefresh:Date.now()};return await f.set(this.GLOBAL_CONFIG_STORAGE_KEY,t),e}async getConfig(){const e=Date.now(),t=await f.get(this.PARTNER_CONFIG_STORAGE_KEY),r=await f.get(this.GLOBAL_CONFIG_STORAGE_KEY);if(this.partnerConfig=t?.config||null,this.globalConfig=r?.config||null,this.lastConfigRefresh=Math.max(t?.lastRefresh||0,r?.lastRefresh||0),!this.partnerConfig||!this.globalConfig||e-this.lastConfigRefresh>this.CONFIG_REFRESH_INTERVAL)try{const[t,r]=await Promise.all([this.fetchAndUpdatePartnerConfig(),this.fetchAndUpdateGlobalConfig()]);this.partnerConfig=t,this.globalConfig=r,this.lastConfigRefresh=e}catch(e){if(!this.partnerConfig||!this.globalConfig)throw e}const i=this.mergeConfigs(this.globalConfig,this.partnerConfig);return((e,t,r)=>{const i=[...t.merchantExclusions??[],...r.merchantExclusions??[]];e.merchantExclusions=i})(i,this.globalConfig,this.partnerConfig),i}async clearCache(){await Promise.all([f.remove(this.PARTNER_CONFIG_STORAGE_KEY),f.remove(this.GLOBAL_CONFIG_STORAGE_KEY)]),this.partnerConfig=null,this.globalConfig=null,this.lastConfigRefresh=0}mergeConfigs(e,t){if(!t)return e;const r={...e};return Object.keys(t).forEach((i=>{if(!Object.prototype.hasOwnProperty.call(t,i))return;const s=e[i],a=t[i];Array.isArray(a)&&Array.isArray(s)?r[i]=a:r[i]=null===a||"object"!=typeof a||"object"!=typeof s?void 0!==a?a:s:this.mergeConfigs(s,a)})),r}getStoredState(e){return e?{config:e.config,lastRefresh:e.lastRefresh}:{config:null,lastRefresh:0}}}const D="TRACK_EVENT",O="GF_HIDE_POPUP",R="GF_GET_POPUP_CONFIG",k="GF_IS_ACTIVE_DOMAIN",F="GF_SHOULD_STAND_DOWN",L="GF_ACTIVATE_OFFER",N="GF_STORE_SHOPIFY_SHOP_ID",G="GF_GET_DOMAIN_BY_SHOP_ID",$="shopify_shop_ids",M="gf_last_health_check";class K{static register(e,t){this.strategies.set(e,t)}static get(e){return this.strategies.get(e)}static exists(e){return!!function(e){return"object"==typeof e&&null!==e&&"type"in e&&"payload"in e&&"string"==typeof e.type}(e)&&this.strategies.has(e.type)}}K.strategies=new Map;class H{async handle(e,t){const{hostname:r}=e.payload,{giveFreelyService:i}=t,s=i.getLogger(),a=await i.getConfig().merchantExclusions,n=await i.getActiveDomains();s.debug("Checking hostname",{hostname:r});try{if(i.healthCheck(),a.some((e=>e.disableDomain&&(r===e.domain||r.endsWith(`.${e.domain}`)))))return s.debug("Hostname is in merchant exclusions",{hostname:r}),{type:k,payload:{isActive:!1,domain:void 0,rates:[]}};if(!n||0===n.length)throw new Error("No active domains");s.debug("Retrieved active domains",{count:n.length});const e=n.find((e=>r===e.Domain||r.endsWith(`.${e.Domain}`)));s.debug("Domain check result",{hostname:r,isActive:!!e});let t=[];if(e?.ID){const r=await i.getMerchantRates(e.Merchant.ID);r&&(s.debug("Retrieved merchant rates",{merchantRates:r}),t=r)}return{type:k,payload:{isActive:!!e,domain:e,rates:t}}}catch(e){return s.error("Error checking domain",{hostname:r,error:e}),{type:k,payload:{isActive:!1,domain:void 0,rates:[]}}}}}class j{async handle(e,t){const{giveFreelyService:r}=t,i=r.getLogger(),s=await r.getCachedConfig();try{return i.debug("Fetching popup config"),{type:R,payload:{config:s}}}catch(e){return i.error("Error fetching popup config",{error:e}),{type:R,payload:{config:null}}}}}class z{async handle(e,t){const{days:r}=e.payload,{giveFreelyService:i}=t,s=i.getLogger();try{const e=new Date;return e.setDate(e.getDate()+r),await f.set("popup_hide",{popupHide:e.getTime()}),s.debug("Popup hidden",{days:r,expiry:e}),{type:O,payload:{ack:!0}}}catch(e){return s.error("Error hiding popup",{days:r,error:e}),{type:O,payload:{ack:!1}}}}}class Y{async handle(e,t){const{activeDomain:r,url:i}=e.payload,{giveFreelyService:s}=t,a=s.getLogger();a.debug("Checking if we should stand down",{activeDomain:r});try{const e=await s.shouldStandDown(r,i);return{type:F,payload:{result:e}}}catch(e){return a.error("Error checking if we should stand down. Returning false",{activeDomain:r,error:e}),{type:F,payload:{result:!1}}}}}const B="version",W=()=>String("1.1.2");class q{constructor(e,t,r=60){this._config=null,this.track=async(e,t,r)=>{null===this._config&&(this._config=await new P(this._partnerApiKey).getConfig());const i={partner:`adUnit_${this._partnerApiKey}`,eventType:e,eventData:{userId:this._userService?.user?.id,libVersion:W(),wfDeviceId:t,...r}};try{const e=JSON.stringify(i);return await this.shouldBroadcastEvent(e)?(await fetch(this._config.eventsUrl,{headers:{"Content-Type":"application/json"},method:"POST",body:e,cache:"no-store"}),await this.pushEventToHistory(e),!0):(this._logger.info("This event has already been broadcasted during the past hour",i),!1)}catch(e){return this._logger.info("Something unexpected happened when dispatching a gf event",{error:e,event:i}),!1}},this.getEventsHistory=async()=>{const{GF_AD_EVENT_HISTORY:e}=await c.storage.local.get({GF_AD_EVENT_HISTORY:""}),t=(e=>{const t=new Date;return t.setTime(t.getTime()+60*e*1e3),t.getTime()})(-this._debounceWindowInMinutes),r=(e||[]).filter((e=>e.createdAt>t));return await c.storage.local.set({GF_AD_EVENT_HISTORY:r}),r},this.shouldBroadcastEvent=async e=>!(await this.getEventsHistory()).some((t=>t.payload===e)),this.pushEventToHistory=async e=>{const t=await this.getEventsHistory();t.push({createdAt:Date.now(),payload:e}),await c.storage.local.set({GF_AD_EVENT_HISTORY:t})},this._partnerApiKey=e,this._userService=t,this._debounceWindowInMinutes=r,this._logger=a.getInstance({title:"Analytics"})}}q.trackEvent=async(e,t)=>{await async function(e){return new Promise(((t,r)=>{try{const i=i=>chrome.runtime.lastError?r({error:chrome.runtime.lastError,message:e}):t(i);c.runtime.sendMessage(e,i)}catch(e){r(e)}}))}({type:D,payload:{eventType:e,eventData:t}})};class V{async handle(e,t){const{eventType:r,eventData:i}=e.payload,{giveFreelyService:s}=t,a=s.getLogger();a.debug("Broadcasting event",{eventType:r,eventData:i});try{const e=await s.trackEvent(r,i);return{type:D,payload:{result:e}}}catch(e){return a.error("Error broadcasting event. Returning false",{eventType:r,eventData:i,error:e}),{type:D,payload:{result:!1}}}}}class X{async handle(e,r){const{activeDomain:i,originalUrl:s,selectedCharity:a}=e.payload,{giveFreelyService:n}=r,o=n.getLogger();o.debug("Activating offer",{activeDomain:i,selectedCharity:a});try{if(!a?.ein||!a?.thirdPartyId)throw new Error("Missing charity information");const e=await n.initializeWfDeviceId();if(!e)throw new Error("Failed to initialize Wildfire device ID");const t=await n.upsertUser(a,e);o.debug("Upserted user",{currentUser:t}),t||o.info("Failed to create user. Using default bucket's user");const r=await n.generateAffiliateUrl(i,s,t);return o.debug("Generated affiliate URL",{affiliateUrl:r}),(async e=>{const t=c.tabs.getCurrent(),r=await t,i=await c.tabs.create({url:e,openerTabId:r?.id,active:!1,pinned:!0});await(async e=>new Promise((t=>{c.tabs.onUpdated.addListener((async function r(i,s){if(i===e&&"complete"===s.status){if(c.tabs.onUpdated.removeListener(r),!(await c.tabs.get(e)).url)return void t(!1);t(!0)}}))})))(i.id)&&setTimeout((()=>{i?.id&&c.tabs.remove(i.id)}),3e4)})(r),await n.updateStanddownHistory(i.Domain),o.debug("Activated offer",{activeDomain:i,selectedCharity:a}),{type:L,payload:{response:!0}}}catch(e){return o.error("Error activating offer",{activeDomain:i,error:e}),n.trackEvent(t.checkoutPopupFailedActivation,{activeDomain:i,error:e}),{type:L,payload:{response:!1}}}}}var Z;!function(e){e[e.Pending=0]="Pending",e[e.Ready=1]="Ready",e[e.Received=2]="Received",e[e.Donated=3]="Donated",e[e.Disqualified=4]="Disqualified"}(Z||(Z={}));class J{constructor(e,t,r){this.adUnitId=e,this.config=t,this.logger=r}async getAnonymousUserCommissions(e,t){try{const r=new URLSearchParams(t).toString(),i=await fetch(`${this.config.apiConfig.baseUri}/${this.config.apiConfig.getAnonymousUserComissionsPath}?${r}`,{method:"GET",headers:{"Content-Type":"application/json","X-AnonymousUserToken":e}});if(!i.ok)throw new Error("Failed to fetch commissions");return await i.json()}catch(e){throw this.logger.error("[GiveFreelyApiClient] Error fetching anonymous user's commissions:",{error:e}),e}}async createOrUpdateAnonymousUser(e,t){try{const r=await fetch(`${this.config.apiConfig.baseUri}/${this.config.apiConfig.createAnonymousUserPath}?adUnitId=${this.adUnitId}`,{method:"PUT",headers:{"Content-Type":"application/json","X-GF-Platform":"adUnitLibrary","X-AnonymousUserToken":t??""},body:JSON.stringify(e)});if(!r.ok)throw new Error("Failed to create/update anonymous user");this.logger.info("[GiveFreelyApiClient] anonymous user created/updated succesfuly.");const i=await r.json(),s=r.headers.get("X-AnonymousUserToken");return{resultUser:{...i,charity:{ein:i.selectedCharity,thirdPartyId:i.selectedCharityThirdPartyIdentifier,name:void 0}},resultToken:s}}catch(e){throw this.logger.error("[GiveFreelyApiClient] Error creating anonymous user:",{error:e}),e}}}class Q{constructor(e,t){this.ANONYMOUS_USER_STORAGE_KEY="gf_anonymous_user_info",this.ANONYMOUS_ENCRIPTED_TOKEN_STORAGE_KEY="gf_anonymous_encrypted_token",this._giveFreelyApiClient=e,this._logger=t,this.user=null}async resetUser(){await f.remove(this.ANONYMOUS_ENCRIPTED_TOKEN_STORAGE_KEY),await f.remove(this.ANONYMOUS_USER_STORAGE_KEY)}async upsertUser(e,t){let r=await this.fetchUser();try{const i={selectedCharity:e?.ein,selectedCharityThirdPartyIdentifier:e?.thirdPartyId,deviceId:t};if(!await this.shouldCreateOrUpdateUser(r,i))return r;const s=await f.get(this.ANONYMOUS_ENCRIPTED_TOKEN_STORAGE_KEY),{resultUser:a,resultToken:n}=await this._giveFreelyApiClient.createOrUpdateAnonymousUser(i,s);n&&await f.set(this.ANONYMOUS_ENCRIPTED_TOKEN_STORAGE_KEY,n),a&&(await f.set(this.ANONYMOUS_USER_STORAGE_KEY,a),r=a)}catch(e){this._logger.error("Error creating/updating anonymous user:",e)}return this.user=r,r}async fetchUser(){const e=await f.get(this.ANONYMOUS_USER_STORAGE_KEY);return this.user=e,e}async shouldCreateOrUpdateUser(e,t){return!e||e.charity?.ein!==t.selectedCharity||e.charity?.thirdPartyId!==t.selectedCharityThirdPartyIdentifier||e.deviceId!==t.deviceId}}const ee=e=>{try{return new URL(e),!0}catch{return!1}},te=new Set,re=new Set,ie={urls:["<all_urls>"],types:["main_frame"]};class se{async handle(e,t){const r=e.payload,{giveFreelyService:i}=t,s=i.getLogger();try{const e=$,t=await f.get(e)||[],i=t.findIndex((e=>e.shopId===r.shopId));return i>=0?t[i]=r:t.push(r),await f.set(e,t),s.debug("Stored Shopify shop ID",{shopInfo:r}),{type:N,payload:{success:!0}}}catch(e){return s.error("Error storing Shopify shop ID",{shopInfo:r,error:e}),{type:N,payload:{success:!1}}}}}class ae{async handle(e,t){const{shopId:r}=e.payload,{giveFreelyService:i}=t,s=i.getLogger();try{const e=$,t=(await f.get(e)||[]).find((e=>e.shopId===r));return t?(s.debug("Found domain for shop ID",{shopId:r,domain:t.domain}),{type:G,payload:{found:!0,domain:t.domain,shopInfo:t}}):(s.debug("No domain found for shop ID",{shopId:r}),{type:G,payload:{found:!1}})}catch(e){return s.error("Error getting domain for shop ID",{shopId:r,error:e}),{type:G,payload:{found:!1}}}}}class ne{constructor(e,t){this.lastCheck=null,this.trackEvent=e,this.logger=t}async initialize(){const e=await f.get(M);e&&(this.lastCheck=e)}getToday(){return(new Date).toISOString().split("T")[0]}async check(){try{const e=this.lastCheck??0,r=this.getToday();(e?new Date(e).toISOString().split("T")[0]:null)!==r&&(await this.trackEvent(t.checkoutPopupHealthCheck,{language:chrome.i18n.getUILanguage()}),this.lastCheck=Date.now(),await f.set(M,this.lastCheck))}catch(e){this.logger.error("Health check failed",{error:e instanceof Error?e.message:"Unknown error"})}}}class oe extends Error{constructor(e="Service not initialized"){super(e),this.name="UninitializedServiceError"}}const ge=K.exists;exports.GiveFreelyService=class{constructor(e){this.partnerApiKey=e,this.dependencies={configService:new P(e)},this.logger=a.getInstance(),this.logger.configure({enabled:!0,title:"Background - GiveFreelyService"}),this.state={initialized:!1,wildfireService:null,partnerFilter:null,config:null,wfDeviceId:null,analytics:null,giveFreelyUserService:null,partnerApiKey:e,healthCheck:null}}assertInitialized(){if(!this.state.initialized)throw new oe}async initialize(e=async()=>(this.logger.debug("Using default partnerFilter."),!0)){if(!this.state.initialized)try{const t=await this.dependencies.configService.getConfig();this.logger.configure({minLevel:t.backgroundMinLogLevel??"error"}),this.logger.info("Initializing GiveFreelyService",{partnerApiKey:this.partnerApiKey});const r=this.createWildfireService(t),s=await(async(e=!0)=>{const t=await f.get(B),r=await W(),i=t!==r;var s;return e&&i&&await(s=r,f.set(B,s)),i})();s&&this.logger.info("New version detected."),this.state.healthCheck=new ne(this.trackEvent.bind(this),this.logger),await this.state.healthCheck.initialize(),await r.refreshCache(s);const n=new J(this.partnerApiKey,t,this.logger);this.state.giveFreelyUserService=new Q(n,this.logger),await this.state.giveFreelyUserService.fetchUser(),this.state.giveFreelyUserService.user?.id||await this.state.giveFreelyUserService.upsertUser(),this.state.analytics=new q(this.partnerApiKey,this.state.giveFreelyUserService),this.logger.addTransport(new i(this)),K.register(k,new H),K.register(R,new j),K.register(O,new z),K.register(F,new Y),K.register(D,new V),K.register(L,new X),K.register(N,new se),K.register(G,new ae),this.state.wildfireService=r,this.state.partnerFilter=e,this.state.config=t,this.state.initialized=!0,d.webRequest&&(d.webRequest.onBeforeRequest.addListener((e=>{const t=e.getLogger();return({requestId:r,url:i,initiator:s})=>{try{if(te.has(r)||re.has(r)||!ee(i))return;const{hostname:a,search:n}=new URL(i);if(a.includes("wild.link"))return t.info("Cashback activation request identified, ignoring affiliate check for request"),void re.add(r);let o;s&&ee(s)&&(o=new URL(s).hostname),e.hasAffiliation([a,o],n)&&(t.info("Affiliation found on url, adding request id to track"),te.add(r))}catch(e){t.error("Error on handleAffiliateOnBeforeRequest",e)}}})(this),ie),d.webRequest.onBeforeRedirect.addListener((e=>{const t=e.getLogger();return async({requestId:r,redirectUrl:i})=>{try{if(!te.has(r))return;t.info("Attempting to find active domain");const{hostname:s}=new URL(i),a=(await e.getActiveDomains()).find((e=>s===e.Domain||s.endsWith(`.${e.Domain}`)));if(!a)return;t.info("Found active domain. Updating standdown state"),await S(a.Domain)}catch(e){t.error("Error on handleAffiliateOnBeforeRedirect",e)}}})(this),ie),d.webRequest.onCompleted.addListener((()=>{const e=this.getLogger();return({requestId:t})=>{try{te.has(t)&&(e.info("Tracking request completed. Request Id:",t),te.delete(t))}catch(t){e.error("Error on handleAffiliateOnCompleted",t)}}})(),ie)),function(e){const t=a.getInstance();c.runtime.onMessage.addListener(((r,i,s)=>{function a(e){s(e)}const n=e(r,0);return n instanceof Promise?(n.then((e=>{e&&a(e)})).catch((e=>{t.warn("Async message callback error:",e)})),!0):n}))}((async(e,t)=>(this.logger.debug("Received message",{type:e.type,payload:e.payload}),this.handleMessage(e)))),this.logger.info("GiveFreelyService initialized successfully")}catch(e){const t=e instanceof Error?e.message:"Unknown error";throw this.logger.error("Failed to initialize GiveFreelyService",{error:t}),new Error(`Failed to initialize GiveFreelyService: ${t}`)}}getConfig(){return this.assertInitialized(),this.state.config}async getCachedConfig(){return this.state.config=await this.dependencies.configService.getConfig(),this.logger.configure({minLevel:this.state.config.backgroundMinLogLevel??"error"}),this.state.config}async getActiveDomains(){return this.assertInitialized(),this.state.wildfireService.getActiveDomains()}async shouldStandDown(e,t){this.assertInitialized();const r=new URL(t);if(!await this.state.partnerFilter(r.hostname))return this.logger.info("Should standdown due to partner filter."),!0;if(this.getConfig().merchantExclusions.some((t=>t.mutePopups&&e===t.domain)))return this.logger.info("Should standdown due to merchant exclusions"),!0;const i=await this.state.wildfireService.shouldStandDown(this.logger,e,t);return i&&this.logger.info("Should standdown due to vendor policy"),i}hasAffiliation(e,t){if(!this.state.wildfireService)return this.logger.warn("Wildfire service not initialized yet"),!1;const r=this.state.wildfireService.hasAffilliation(e,t);return r&&this.logger.info("Affiliation identified"),r}trackEvent(e,t){return this.assertInitialized(),this.state.analytics.track(e,this.state.wfDeviceId?.DeviceID,t)}getLogger(){return this.assertInitialized(),this.logger}async initializeWfDeviceId(){if(this.assertInitialized(),this.state.wfDeviceId=await this.state.wildfireService.getDevice(),!this.state.wfDeviceId)throw new Error("Failed to initialize Wildfire device ID");return this.state.wfDeviceId.DeviceID}getWfDeviceId(){return this.assertInitialized(),this.state.wfDeviceId.DeviceID}upsertUser(e,t){return this.assertInitialized(),this.state.giveFreelyUserService.upsertUser(e,t)}getCurrentUser(){return this.assertInitialized(),this.state.giveFreelyUserService.user}generateAffiliateUrl(e,t,r){return this.assertInitialized(),this.state.wildfireService.generateAffiliateUrl(e,t,r,this.state.partnerApiKey)}updateStanddownHistory(e){return this.assertInitialized(),S(e)}getMerchantRates(e){if(this.assertInitialized(),!this.state.wildfireService)throw new Error("Wildfire service not initialized");return this.state.wildfireService.getMerchantRates(e)}async healthCheck(){this.assertInitialized(),await this.state.healthCheck.check()}async handleMessage(e){this.assertInitialized();const t=K.get(e.type);if(!t)return;const r={giveFreelyService:this};return t.handle(e,r)}createWildfireService(e){this.logger.debug("config",this.state.config);const t=new m(e.wfSecret,e.wfAppId,e.deviceUrl,e.dataUrl,this.logger);return new U(t,this.logger,e.vanityBaseUrl,e.refreshInterval)}async destroy(){this.logger.info("Destroying GiveFreelyService"),await Promise.all([this.dependencies.configService.clearCache(),this.state.wildfireService?.clearCache(),this.state.giveFreelyUserService?.resetUser()]),this.state.initialized=!1,this.state.wildfireService=null,this.state.partnerFilter=null,this.state.analytics=null,this.state.config=null}},exports.browser=c,exports.isAdUnitMessage=ge;
|
|
1
|
+
"use strict";class e{log({level:t,message:r,timestamp:i,data:s,title:a}){const n="debug"===t?"log":t,o=a?`[${a}]`:"";void 0!==s?console[n](`[${i}] - ${e.LOGGER_PREFIX} ${o} ${r}`,s):console[n](`[${i}] - ${e.LOGGER_PREFIX} ${o} ${r}`)}}var t,r;e.LOGGER_PREFIX="[GFAdUnit] -",function(e){e.checkoutPopupShown="CHECKOUT-POPUP-SHOWN",e.checkoutPopupNotShown="CHECKOUT-POPUP-NOT-SHOWN",e.checkoutPopupSupressed="CHECKOUT-POPUP-SUPRESSED",e.checkoutPopupDonation="CHECKOUT-POPUP-DONATION",e.checkoutPopupFailedActivation="CHECKOUT-POPUP-FAILED-ACTIVATION",e.checkoutPopupPurchaseConfirmed="CHECKOUT-POPUP-PURCHASE-CONFIRMED",e.checkoutPopupHealthCheck="CHECKOUT-POPUP-HEALTH-CHECK",e.checkouPopupActivationFailed="CHECKOUT-POPUP-ACTIVATION-FAILED",e.checkoutPopupOfferDetailsClicked="CHECKOUT-POPUP-OFFER-DETAILS-CLICKED"}(t||(t={})),function(e){e.adUnitLog="ADUNIT-LOG"}(r||(r={}));class i{constructor(e){this.gfService=e}log({level:e,message:t,data:i,title:s}){this.gfService.trackEvent(r.adUnitLog,{log:{level:e,message:t,data:i,title:s}})}}const s=["debug","info","warn","error"];class a{constructor(t){this.config={minLevel:t?.minLevel??"debug",enabled:t?.enabled??!1,transports:t?.transports??[new e],title:t?.title??""}}static getInstance(e){return a.instance||(a.instance=new a(e)),a.instance}configure(e){this.config={...this.config,...e}}shouldLog(e){return!!this.config.enabled&&s.indexOf(e)>=s.indexOf(this.config.minLevel)}createLogMessage(e,t,r){return{level:e,version:"1.1.2",message:t,timestamp:(new Date).toISOString(),data:r,title:this.config.title}}log(e,t,r){if(!this.shouldLog(e))return;const i=this.createLogMessage(e,t,r);this.config.transports.forEach((e=>e.log(i)))}debug(e,t){this.log("debug",e,t)}info(e,t){this.log("info",e,t)}warn(e,t){this.log("warn",e,t)}error(e,t){this.log("error",e,t)}addTransport(e){this.config.transports.push(e)}clearTransports(){this.config.transports=[]}disable(){this.config.enabled=!1}enable(){this.config.enabled=!0}}const n=3,o=e=>1e3*e;async function g(e,t={}){const{maxAttempts:r=n,delayFn:i=o}=t;let s=null;for(let t=1;t<=r;t+=1)try{return await e()}catch(e){s=e,t<r&&await new Promise((e=>{setTimeout(e,i(t))}))}throw s||new Error("Operation failed after multiple attempts")}const c=chrome;function l(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self&&self;var h={exports:{}};"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,function(e){if(!(globalThis.chrome&&globalThis.chrome.runtime&&globalThis.chrome.runtime.id))throw new Error("This script should only be loaded in a browser extension.");if(globalThis.browser&&globalThis.browser.runtime&&globalThis.browser.runtime.id)e.exports=globalThis.browser;else{const t="The message port closed before a response was received.",r=e=>{const r={alarms:{clear:{minArgs:0,maxArgs:1},clearAll:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getAll:{minArgs:0,maxArgs:0}},bookmarks:{create:{minArgs:1,maxArgs:1},get:{minArgs:1,maxArgs:1},getChildren:{minArgs:1,maxArgs:1},getRecent:{minArgs:1,maxArgs:1},getSubTree:{minArgs:1,maxArgs:1},getTree:{minArgs:0,maxArgs:0},move:{minArgs:2,maxArgs:2},remove:{minArgs:1,maxArgs:1},removeTree:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1},update:{minArgs:2,maxArgs:2}},browserAction:{disable:{minArgs:0,maxArgs:1,fallbackToNoCallback:!0},enable:{minArgs:0,maxArgs:1,fallbackToNoCallback:!0},getBadgeBackgroundColor:{minArgs:1,maxArgs:1},getBadgeText:{minArgs:1,maxArgs:1},getPopup:{minArgs:1,maxArgs:1},getTitle:{minArgs:1,maxArgs:1},openPopup:{minArgs:0,maxArgs:0},setBadgeBackgroundColor:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setBadgeText:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setIcon:{minArgs:1,maxArgs:1},setPopup:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setTitle:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},browsingData:{remove:{minArgs:2,maxArgs:2},removeCache:{minArgs:1,maxArgs:1},removeCookies:{minArgs:1,maxArgs:1},removeDownloads:{minArgs:1,maxArgs:1},removeFormData:{minArgs:1,maxArgs:1},removeHistory:{minArgs:1,maxArgs:1},removeLocalStorage:{minArgs:1,maxArgs:1},removePasswords:{minArgs:1,maxArgs:1},removePluginData:{minArgs:1,maxArgs:1},settings:{minArgs:0,maxArgs:0}},commands:{getAll:{minArgs:0,maxArgs:0}},contextMenus:{remove:{minArgs:1,maxArgs:1},removeAll:{minArgs:0,maxArgs:0},update:{minArgs:2,maxArgs:2}},cookies:{get:{minArgs:1,maxArgs:1},getAll:{minArgs:1,maxArgs:1},getAllCookieStores:{minArgs:0,maxArgs:0},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}},devtools:{inspectedWindow:{eval:{minArgs:1,maxArgs:2,singleCallbackArg:!1}},panels:{create:{minArgs:3,maxArgs:3,singleCallbackArg:!0},elements:{createSidebarPane:{minArgs:1,maxArgs:1}}}},downloads:{cancel:{minArgs:1,maxArgs:1},download:{minArgs:1,maxArgs:1},erase:{minArgs:1,maxArgs:1},getFileIcon:{minArgs:1,maxArgs:2},open:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},pause:{minArgs:1,maxArgs:1},removeFile:{minArgs:1,maxArgs:1},resume:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1},show:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},extension:{isAllowedFileSchemeAccess:{minArgs:0,maxArgs:0},isAllowedIncognitoAccess:{minArgs:0,maxArgs:0}},history:{addUrl:{minArgs:1,maxArgs:1},deleteAll:{minArgs:0,maxArgs:0},deleteRange:{minArgs:1,maxArgs:1},deleteUrl:{minArgs:1,maxArgs:1},getVisits:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1}},i18n:{detectLanguage:{minArgs:1,maxArgs:1},getAcceptLanguages:{minArgs:0,maxArgs:0}},identity:{launchWebAuthFlow:{minArgs:1,maxArgs:1}},idle:{queryState:{minArgs:1,maxArgs:1}},management:{get:{minArgs:1,maxArgs:1},getAll:{minArgs:0,maxArgs:0},getSelf:{minArgs:0,maxArgs:0},setEnabled:{minArgs:2,maxArgs:2},uninstallSelf:{minArgs:0,maxArgs:1}},notifications:{clear:{minArgs:1,maxArgs:1},create:{minArgs:1,maxArgs:2},getAll:{minArgs:0,maxArgs:0},getPermissionLevel:{minArgs:0,maxArgs:0},update:{minArgs:2,maxArgs:2}},pageAction:{getPopup:{minArgs:1,maxArgs:1},getTitle:{minArgs:1,maxArgs:1},hide:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setIcon:{minArgs:1,maxArgs:1},setPopup:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setTitle:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},show:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},permissions:{contains:{minArgs:1,maxArgs:1},getAll:{minArgs:0,maxArgs:0},remove:{minArgs:1,maxArgs:1},request:{minArgs:1,maxArgs:1}},runtime:{getBackgroundPage:{minArgs:0,maxArgs:0},getPlatformInfo:{minArgs:0,maxArgs:0},openOptionsPage:{minArgs:0,maxArgs:0},requestUpdateCheck:{minArgs:0,maxArgs:0},sendMessage:{minArgs:1,maxArgs:3},sendNativeMessage:{minArgs:2,maxArgs:2},setUninstallURL:{minArgs:1,maxArgs:1}},sessions:{getDevices:{minArgs:0,maxArgs:1},getRecentlyClosed:{minArgs:0,maxArgs:1},restore:{minArgs:0,maxArgs:1}},storage:{local:{clear:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}},managed:{get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1}},sync:{clear:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}}},tabs:{captureVisibleTab:{minArgs:0,maxArgs:2},create:{minArgs:1,maxArgs:1},detectLanguage:{minArgs:0,maxArgs:1},discard:{minArgs:0,maxArgs:1},duplicate:{minArgs:1,maxArgs:1},executeScript:{minArgs:1,maxArgs:2},get:{minArgs:1,maxArgs:1},getCurrent:{minArgs:0,maxArgs:0},getZoom:{minArgs:0,maxArgs:1},getZoomSettings:{minArgs:0,maxArgs:1},goBack:{minArgs:0,maxArgs:1},goForward:{minArgs:0,maxArgs:1},highlight:{minArgs:1,maxArgs:1},insertCSS:{minArgs:1,maxArgs:2},move:{minArgs:2,maxArgs:2},query:{minArgs:1,maxArgs:1},reload:{minArgs:0,maxArgs:2},remove:{minArgs:1,maxArgs:1},removeCSS:{minArgs:1,maxArgs:2},sendMessage:{minArgs:2,maxArgs:3},setZoom:{minArgs:1,maxArgs:2},setZoomSettings:{minArgs:1,maxArgs:2},update:{minArgs:1,maxArgs:2}},topSites:{get:{minArgs:0,maxArgs:0}},webNavigation:{getAllFrames:{minArgs:1,maxArgs:1},getFrame:{minArgs:1,maxArgs:1}},webRequest:{handlerBehaviorChanged:{minArgs:0,maxArgs:0}},windows:{create:{minArgs:0,maxArgs:1},get:{minArgs:1,maxArgs:2},getAll:{minArgs:0,maxArgs:1},getCurrent:{minArgs:0,maxArgs:1},getLastFocused:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},update:{minArgs:2,maxArgs:2}}};if(0===Object.keys(r).length)throw new Error("api-metadata.json has not been included in browser-polyfill");class i extends WeakMap{constructor(e,t=void 0){super(t),this.createItem=e}get(e){return this.has(e)||this.set(e,this.createItem(e)),super.get(e)}}const s=(t,r)=>(...i)=>{e.runtime.lastError?t.reject(new Error(e.runtime.lastError.message)):r.singleCallbackArg||i.length<=1&&!1!==r.singleCallbackArg?t.resolve(i[0]):t.resolve(i)},a=e=>1==e?"argument":"arguments",n=(e,t,r)=>new Proxy(t,{apply:(t,i,s)=>r.call(i,e,...s)});let o=Function.call.bind(Object.prototype.hasOwnProperty);const g=(e,t={},r={})=>{let i=Object.create(null),c={has:(t,r)=>r in e||r in i,get(c,l,h){if(l in i)return i[l];if(!(l in e))return;let d=e[l];if("function"==typeof d)if("function"==typeof t[l])d=n(e,e[l],t[l]);else if(o(r,l)){let t=((e,t)=>function(r,...i){if(i.length<t.minArgs)throw new Error(`Expected at least ${t.minArgs} ${a(t.minArgs)} for ${e}(), got ${i.length}`);if(i.length>t.maxArgs)throw new Error(`Expected at most ${t.maxArgs} ${a(t.maxArgs)} for ${e}(), got ${i.length}`);return new Promise(((a,n)=>{if(t.fallbackToNoCallback)try{r[e](...i,s({resolve:a,reject:n},t))}catch(s){console.warn(`${e} API method doesn't seem to support the callback parameter, falling back to call it without a callback: `,s),r[e](...i),t.fallbackToNoCallback=!1,t.noCallback=!0,a()}else t.noCallback?(r[e](...i),a()):r[e](...i,s({resolve:a,reject:n},t))}))})(l,r[l]);d=n(e,e[l],t)}else d=d.bind(e);else if("object"==typeof d&&null!==d&&(o(t,l)||o(r,l)))d=g(d,t[l],r[l]);else{if(!o(r,"*"))return Object.defineProperty(i,l,{configurable:!0,enumerable:!0,get:()=>e[l],set(t){e[l]=t}}),d;d=g(d,t[l],r["*"])}return i[l]=d,d},set:(t,r,s,a)=>(r in i?i[r]=s:e[r]=s,!0),defineProperty:(e,t,r)=>Reflect.defineProperty(i,t,r),deleteProperty:(e,t)=>Reflect.deleteProperty(i,t)},l=Object.create(e);return new Proxy(l,c)},c=e=>({addListener(t,r,...i){t.addListener(e.get(r),...i)},hasListener:(t,r)=>t.hasListener(e.get(r)),removeListener(t,r){t.removeListener(e.get(r))}}),l=new i((e=>"function"!=typeof e?e:function(t){const r=g(t,{},{getContent:{minArgs:0,maxArgs:0}});e(r)})),h=new i((e=>"function"!=typeof e?e:function(t,r,i){let s,a,n=!1,o=new Promise((e=>{s=function(t){n=!0,e(t)}}));try{a=e(t,r,s)}catch(e){a=Promise.reject(e)}const g=!0!==a&&((c=a)&&"object"==typeof c&&"function"==typeof c.then);var c;if(!0!==a&&!g&&!n)return!1;return(g?a:o).then((e=>{i(e)}),(e=>{let t;t=e&&(e instanceof Error||"string"==typeof e.message)?e.message:"An unexpected error occurred",i({__mozWebExtensionPolyfillReject__:!0,message:t})})).catch((e=>{console.error("Failed to send onMessage rejected reply",e)})),!0})),d=({reject:r,resolve:i},s)=>{e.runtime.lastError?e.runtime.lastError.message===t?i():r(new Error(e.runtime.lastError.message)):s&&s.__mozWebExtensionPolyfillReject__?r(new Error(s.message)):i(s)},m=(e,t,r,...i)=>{if(i.length<t.minArgs)throw new Error(`Expected at least ${t.minArgs} ${a(t.minArgs)} for ${e}(), got ${i.length}`);if(i.length>t.maxArgs)throw new Error(`Expected at most ${t.maxArgs} ${a(t.maxArgs)} for ${e}(), got ${i.length}`);return new Promise(((e,t)=>{const s=d.bind(null,{resolve:e,reject:t});i.push(s),r.sendMessage(...i)}))},u={devtools:{network:{onRequestFinished:c(l)}},runtime:{onMessage:c(h),onMessageExternal:c(h),sendMessage:m.bind(null,"sendMessage",{minArgs:1,maxArgs:3})},tabs:{sendMessage:m.bind(null,"sendMessage",{minArgs:2,maxArgs:3})}},f={clear:{minArgs:1,maxArgs:1},get:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}};return r.privacy={network:{"*":f},services:{"*":f},websites:{"*":f}},g(e,u,r)};e.exports=r(chrome)}}(h);var d=l(h.exports);class m{constructor(e,t,r,i,s){this.wfSecret=e,this.wfAppId=t,this.deviceUrl=r,this.dataUrl=i,this._logger=s}async getWildfireDevice(){return(await fetch(this.deviceUrl,{method:"POST",body:JSON.stringify({}),headers:{"Content-Type":"application/json","WF-Secret":this.wfSecret,"WF-App-ID":this.wfAppId}})).json()}async getActiveDomains(){return(await fetch(`${this.dataUrl}/${this.wfAppId}/active-domain/1`)).json()}async getStanddownPolicy(){const e=`${this.dataUrl}/${this.wfAppId}/stand-down-policy/1`;try{const t=await fetch(e);if(t.ok)return t.json();throw new Error(`Failed to fetch standdown policy. Status ${t.status}`)}catch(t){return this._logger.error("Failed to fetch the standdown policy",{err:t,url:e}),{Domains:[],LostAttribution:[],Params:[],Serp:[]}}}async getMerchantRates(){const e=`${this.dataUrl}/${this.wfAppId}/merchant-rate/1`;try{const t=await fetch(e);if(this._logger.debug("Merchant rates response",{response:t}),!t.ok)throw new Error(`Failed to fetch merchant rates. Status ${t.status}`);const r=t.json();return this._logger.debug("Merchant rates",{rates:r}),r}catch(t){return this._logger.error("Failed to fetch the merchant rates",{err:t,url:e}),[]}}}const u="GIVE_FREELY_",f={async get(e){const t=`${u}${e}`;return(await d.storage.local.get(t))[t]||null},async set(e,t){const r=`${u}${e}`;await d.storage.local.set({[r]:t})},async remove(e){const t=Array.isArray(e)?e.map((e=>`${u}${e}`)):`${u}${e}`;await d.storage.local.remove(t)}},A="wfDevice",y="wfDomains",p="wfStanddownPolicy",w="wfStanddownHistory",v="wfLastCacheRefresh",x="wfMerchantRates";async function E(e,t,r){try{const i=(await b())?.[t];if(i&&i>Date.now())return e.info("Standing down because current affiliate stand down session has not expired"),S(t),!0;await this.refreshCache();const s=await this.getStanddownPolicy();if(_(s,new URL(r).search))return e.info("Standing down because url has affiliate params"),S(t),!0;e.info("No need to stand down.")}catch(t){e.error("Exception produced when trying to determine if it should stand down",t)}return!1}function C(e,t){if(!this.standDownPolicy)return this.logger.warn("Couldnt check affilliation. No stand down policy found"),this.getStanddownPolicy().then((e=>{this.standDownPolicy=e})),!1;const r=e.some((e=>I(this.standDownPolicy,e)))||_(this.standDownPolicy,t);return this.logger.info("Affilliation result:",r),r}const b=async()=>f.get(w),S=async(e,t=1)=>{const r=await b()??{};r[e]=Date.now()+60*t*60*1e3,await f.set(w,r)},I=(e,t)=>{if(!t)return!1;const{Domains:r}=e;return r.some((e=>t.includes(e)))},_=(e,t)=>{if(!t)return!1;const{Params:r}=e;return r.some((e=>t.toLowerCase().includes(`?${e.toLowerCase()}`)))||r.some((e=>t.toLowerCase().includes(`&${e.toLowerCase()}`)))};class T{constructor(e,t,r,i){this.userId=e,this.charityEin=t,this.charityThirdPartyIdentifier=r,this.partnerId=i}toString(){return`userId=${this.encodeUserId(this.userId)},charityEin=${this.charityEin},charityThirdPartyIdentifier=${this.charityThirdPartyIdentifier},partnerId=${this.partnerId}`}encodeUserId(e){return btoa(e).replace(/_/g,"/").replace(/-/g,"+")}}class U{constructor(e,t,r,i=21600){if(this.standDownPolicy=null,this.shouldStandDown=E.bind(this),this.hasAffilliation=C.bind(this),!e||!r)throw new Error("WildfireService requires wildfireClient and vanityBaseUrl");this.wildfireClient=e,this.vanityBaseUrl=r.replace(/\/$/,""),this.refreshInterval=i,this.logger=t}async getActiveDomains(){try{await this.refreshCache();const e=await f.get(y);if(!e)throw Error("Failed to retrieve active domains");return e}catch(e){throw this.logger.error("Failed to get active domains:",e),new Error("Failed to retrieve active domains")}}async getMerchantRates(e){try{await this.refreshCache();const t=await f.get(x);if(!t)throw new Error("Failed to retrieve merchant rates");return t[e]}catch(e){throw this.logger.error("Failed to retrieve merchant rates:",e),new Error("Failed to retrieve merchant rates")}}async generateAffiliateUrl(e,t,r,i){if(!e||!t||!i)throw new Error("Missing required parameters for affiliate URL generation");try{const s=await this.getDevice(),a=await this.generateTrackingCode(r,i,s),n=encodeURIComponent(t),o=encodeURIComponent(a);return`${this.vanityBaseUrl}/e?d=${s.DeviceID}&c=${e.ID}&tc=${o}&url=${n}`}catch(e){throw this.logger.error("Failed to generate affiliate URL:",e),new Error("Failed to generate affiliate URL")}}async clearCache(){try{await Promise.all([f.remove(A),f.remove(y),f.remove(v),f.remove(p),f.remove(x)])}catch(e){throw this.logger.error("Failed to clear cache:",e),new Error("Failed to clear cache")}}async refreshCache(e=!1){try{const t=await f.get(v),r=Date.now();(e||!t||r-t>1e3*this.refreshInterval)&&(await Promise.all([this.syncActiveDomains(),this.syncStanddownPolicy(),this.syncMerchantRates()]),await f.set(v,r)),this.standDownPolicy=await this.getStanddownPolicy()}catch(e){throw this.logger.error("Failed to refresh cache:",e),new Error("Failed to refresh cache")}}async getStanddownPolicy(){return await f.get(p)??(()=>{throw new Error("Failed to retrieve standdown policy. Storage value was null")})()}getRetryOptions(){return{delayFn:e=>1e3*e,maxAttempts:3}}async getDevice(){try{const e=await f.get(A);return e?.DeviceID?e:g((()=>this.fetchAndStoreDevice()),this.getRetryOptions())}catch(e){throw this.logger.error("Failed to get device:",e),new Error("Failed to retrieve device information")}}async fetchAndStoreDevice(){const e=await this.wildfireClient.getWildfireDevice();if(!e?.DeviceID)throw new Error("Failed to retrieve device information");return await f.set(A,e),e}async generateTrackingCode(e,t,r){if(!(e?.id&&e?.charity&&e?.charity.ein&&e?.charity.thirdPartyId)){const e=`notregistered-${r.DeviceID}`;return this.logger.info("User not provided. Using GF's default bucket instead.",e),e}return new T(e.id,e.charity.ein,e.charity.thirdPartyId,t).toString()}async syncActiveDomains(){const e=await g((()=>this.wildfireClient.getActiveDomains()),this.getRetryOptions());return await f.set(y,e),e}async syncStanddownPolicy(){const e=await g((()=>this.wildfireClient.getStanddownPolicy()),this.getRetryOptions());return this.logger.info("Updated standdown policy",e),await f.set(p,e),e}async syncMerchantRates(){const e=await g((()=>this.wildfireClient.getMerchantRates()),this.getRetryOptions());return this.logger.debug("Merchant rates",e),await f.set(x,e),e}}class P{constructor(e){this.partnerApiKey=e,this.partnerConfig=null,this.globalConfig=null,this.lastConfigRefresh=0,this.CONFIG_REFRESH_INTERVAL=3e5,this.PARTNER_CONFIG_STORAGE_KEY="gf_partner_config",this.GLOBAL_CONFIG_STORAGE_KEY="gf_global_config"}getRetryOptions(){return{delayFn:e=>1e3*e,maxAttempts:3}}async fetchAndUpdatePartnerConfig(){const e=await g((async()=>{const e=await fetch(`https://cdn.givefreely.com/adunit/config/${this.partnerApiKey}.json`,{cache:"no-store"});if(!e.ok)throw new Error(`Failed to fetch partner config: ${e.statusText}`);return e.json()}),this.getRetryOptions()),t={config:e,lastRefresh:Date.now()};return await f.set(this.PARTNER_CONFIG_STORAGE_KEY,t),e}async fetchAndUpdateGlobalConfig(){const e=await g((async()=>{const e=await fetch("https://cdn.givefreely.com/adunit/config/global.json",{cache:"no-store"});if(!e.ok)throw new Error(`Failed to fetch global config: ${e.statusText}`);return e.json()}),this.getRetryOptions()),t={config:e,lastRefresh:Date.now()};return await f.set(this.GLOBAL_CONFIG_STORAGE_KEY,t),e}async getConfig(){const e=Date.now(),t=await f.get(this.PARTNER_CONFIG_STORAGE_KEY),r=await f.get(this.GLOBAL_CONFIG_STORAGE_KEY);if(this.partnerConfig=t?.config||null,this.globalConfig=r?.config||null,this.lastConfigRefresh=Math.max(t?.lastRefresh||0,r?.lastRefresh||0),!this.partnerConfig||!this.globalConfig||e-this.lastConfigRefresh>this.CONFIG_REFRESH_INTERVAL)try{const[t,r]=await Promise.all([this.fetchAndUpdatePartnerConfig(),this.fetchAndUpdateGlobalConfig()]);this.partnerConfig=t,this.globalConfig=r,this.lastConfigRefresh=e}catch(e){if(!this.partnerConfig||!this.globalConfig)throw e}const i=this.mergeConfigs(this.globalConfig,this.partnerConfig);return((e,t,r)=>{const i=[...t.merchantExclusions??[],...r.merchantExclusions??[]];e.merchantExclusions=i})(i,this.globalConfig,this.partnerConfig),i}async clearCache(){await Promise.all([f.remove(this.PARTNER_CONFIG_STORAGE_KEY),f.remove(this.GLOBAL_CONFIG_STORAGE_KEY)]),this.partnerConfig=null,this.globalConfig=null,this.lastConfigRefresh=0}mergeConfigs(e,t){if(!t)return e;const r={...e};return Object.keys(t).forEach((i=>{if(!Object.prototype.hasOwnProperty.call(t,i))return;const s=e[i],a=t[i];Array.isArray(a)&&Array.isArray(s)?r[i]=a:r[i]=null===a||"object"!=typeof a||"object"!=typeof s?void 0!==a?a:s:this.mergeConfigs(s,a)})),r}getStoredState(e){return e?{config:e.config,lastRefresh:e.lastRefresh}:{config:null,lastRefresh:0}}}const D="TRACK_EVENT",O="GF_HIDE_POPUP",R="GF_GET_POPUP_CONFIG",k="GF_IS_ACTIVE_DOMAIN",F="GF_SHOULD_STAND_DOWN",L="GF_ACTIVATE_OFFER",N="GF_STORE_SHOPIFY_SHOP_ID",G="GF_GET_DOMAIN_BY_SHOP_ID",$="shopify_shop_ids",M="gf_last_health_check";class K{static register(e,t){this.strategies.set(e,t)}static get(e){return this.strategies.get(e)}static exists(e){return!!function(e){return"object"==typeof e&&null!==e&&"type"in e&&"payload"in e&&"string"==typeof e.type}(e)&&this.strategies.has(e.type)}}K.strategies=new Map;class H{async handle(e,t){const{hostname:r}=e.payload,{giveFreelyService:i}=t,s=i.getLogger(),a=await i.getConfig().merchantExclusions,n=await i.getActiveDomains();s.debug("Checking hostname",{hostname:r});try{if(i.healthCheck(),a.some((e=>e.disableDomain&&(r===e.domain||r.endsWith(`.${e.domain}`)))))return s.debug("Hostname is in merchant exclusions",{hostname:r}),{type:k,payload:{isActive:!1,domain:void 0,rates:[]}};if(!n||0===n.length)throw new Error("No active domains");s.debug("Retrieved active domains",{count:n.length});const e=n.find((e=>r===e.Domain||r.endsWith(`.${e.Domain}`)));s.debug("Domain check result",{hostname:r,isActive:!!e});let t=[];if(e?.ID){const r=await i.getMerchantRates(e.Merchant.ID);r&&(s.debug("Retrieved merchant rates",{merchantRates:r}),t=r)}return{type:k,payload:{isActive:!!e,domain:e,rates:t}}}catch(e){return s.error("Error checking domain",{hostname:r,error:e}),{type:k,payload:{isActive:!1,domain:void 0,rates:[]}}}}}class j{async handle(e,t){const{giveFreelyService:r}=t,i=r.getLogger(),s=await r.getCachedConfig();try{return i.debug("Fetching popup config"),{type:R,payload:{config:s}}}catch(e){return i.error("Error fetching popup config",{error:e}),{type:R,payload:{config:null}}}}}class z{async handle(e,t){const{days:r}=e.payload,{giveFreelyService:i}=t,s=i.getLogger();try{const e=new Date;return e.setDate(e.getDate()+r),await f.set("popup_hide",{popupHide:e.getTime()}),s.debug("Popup hidden",{days:r,expiry:e}),{type:O,payload:{ack:!0}}}catch(e){return s.error("Error hiding popup",{days:r,error:e}),{type:O,payload:{ack:!1}}}}}class Y{async handle(e,t){const{activeDomain:r,url:i}=e.payload,{giveFreelyService:s}=t,a=s.getLogger();a.debug("Checking if we should stand down",{activeDomain:r});try{const e=await s.shouldStandDown(r,i);return{type:F,payload:{result:e}}}catch(e){return a.error("Error checking if we should stand down. Returning false",{activeDomain:r,error:e}),{type:F,payload:{result:!1}}}}}const B="version",W=()=>String("1.1.2");class q{constructor(e,t,r=60){this._config=null,this.track=async(e,t,r)=>{null===this._config&&(this._config=await new P(this._partnerApiKey).getConfig());const i={partner:`adUnit_${this._partnerApiKey}`,eventType:e,eventData:{userId:this._userService?.user?.id,libVersion:W(),wfDeviceId:t,...r}};try{const e=JSON.stringify(i);return await this.shouldBroadcastEvent(e)?(await fetch(this._config.eventsUrl,{headers:{"Content-Type":"application/json"},method:"POST",body:e,cache:"no-store"}),await this.pushEventToHistory(e),!0):(this._logger.info("This event has already been broadcasted during the past hour",i),!1)}catch(e){return this._logger.info("Something unexpected happened when dispatching a gf event",{error:e,event:i}),!1}},this.getEventsHistory=async()=>{const{GF_AD_EVENT_HISTORY:e}=await c.storage.local.get({GF_AD_EVENT_HISTORY:""}),t=(e=>{const t=new Date;return t.setTime(t.getTime()+60*e*1e3),t.getTime()})(-this._debounceWindowInMinutes),r=(e||[]).filter((e=>e.createdAt>t));return await c.storage.local.set({GF_AD_EVENT_HISTORY:r}),r},this.shouldBroadcastEvent=async e=>!(await this.getEventsHistory()).some((t=>t.payload===e)),this.pushEventToHistory=async e=>{const t=await this.getEventsHistory();t.push({createdAt:Date.now(),payload:e}),await c.storage.local.set({GF_AD_EVENT_HISTORY:t})},this._partnerApiKey=e,this._userService=t,this._debounceWindowInMinutes=r,this._logger=a.getInstance({title:"Analytics"})}}q.trackEvent=async(e,t)=>{await async function(e){return new Promise(((t,r)=>{try{const i=i=>chrome.runtime.lastError?r({error:chrome.runtime.lastError,message:e}):t(i);c.runtime.sendMessage(e,i)}catch(e){r(e)}}))}({type:D,payload:{eventType:e,eventData:t}})};class V{async handle(e,t){const{eventType:r,eventData:i}=e.payload,{giveFreelyService:s}=t,a=s.getLogger();a.debug("Broadcasting event",{eventType:r,eventData:i});try{const e=await s.trackEvent(r,i);return{type:D,payload:{result:e}}}catch(e){return a.error("Error broadcasting event. Returning false",{eventType:r,eventData:i,error:e}),{type:D,payload:{result:!1}}}}}class X{async handle(e,r){const{activeDomain:i,originalUrl:s,selectedCharity:a}=e.payload,{giveFreelyService:n}=r,o=n.getLogger();o.debug("Activating offer",{activeDomain:i,selectedCharity:a});try{if(!a?.ein||!a?.thirdPartyId)throw new Error("Missing charity information");const e=await n.initializeWfDeviceId();if(!e)throw new Error("Failed to initialize Wildfire device ID");const t=await n.upsertUser(a,e);o.debug("Upserted user",{currentUser:t}),t||o.info("Failed to create user. Using default bucket's user");const r=await n.generateAffiliateUrl(i,s,t);return o.debug("Generated affiliate URL",{affiliateUrl:r}),(async e=>{const t=c.tabs.getCurrent(),r=await t,i=await c.tabs.create({url:e,openerTabId:r?.id,active:!1,pinned:!0});await(async e=>new Promise((t=>{c.tabs.onUpdated.addListener((async function r(i,s){if(i===e&&"complete"===s.status){if(c.tabs.onUpdated.removeListener(r),!(await c.tabs.get(e)).url)return void t(!1);t(!0)}}))})))(i.id)&&setTimeout((()=>{i?.id&&c.tabs.remove(i.id)}),3e4)})(r),await n.updateStanddownHistory(i.Domain),o.debug("Activated offer",{activeDomain:i,selectedCharity:a}),{type:L,payload:{response:!0}}}catch(e){return o.error("Error activating offer",{activeDomain:i,error:e}),n.trackEvent(t.checkoutPopupFailedActivation,{activeDomain:i,error:e}),{type:L,payload:{response:!1}}}}}var Z;!function(e){e[e.Pending=0]="Pending",e[e.Ready=1]="Ready",e[e.Received=2]="Received",e[e.Donated=3]="Donated",e[e.Disqualified=4]="Disqualified"}(Z||(Z={}));class J{constructor(e,t,r){this.adUnitId=e,this.config=t,this.logger=r}async getAnonymousUserCommissions(e,t){try{const r=new URLSearchParams(t).toString(),i=await fetch(`${this.config.apiConfig.baseUri}/${this.config.apiConfig.getAnonymousUserComissionsPath}?${r}`,{method:"GET",headers:{"Content-Type":"application/json","X-AnonymousUserToken":e}});if(!i.ok)throw new Error("Failed to fetch commissions");return await i.json()}catch(e){throw this.logger.error("[GiveFreelyApiClient] Error fetching anonymous user's commissions:",{error:e}),e}}async createOrUpdateAnonymousUser(e,t){try{const r=await fetch(`${this.config.apiConfig.baseUri}/${this.config.apiConfig.createAnonymousUserPath}?adUnitId=${this.adUnitId}`,{method:"PUT",headers:{"Content-Type":"application/json","X-GF-Platform":"adUnitLibrary","X-AnonymousUserToken":t??""},body:JSON.stringify(e)});if(!r.ok)throw new Error("Failed to create/update anonymous user");this.logger.info("[GiveFreelyApiClient] anonymous user created/updated succesfuly.");const i=await r.json(),s=r.headers.get("X-AnonymousUserToken");return{resultUser:{...i,charity:{ein:i.selectedCharity,thirdPartyId:i.selectedCharityThirdPartyIdentifier,name:void 0}},resultToken:s}}catch(e){throw this.logger.error("[GiveFreelyApiClient] Error creating anonymous user:",{error:e}),e}}}class Q{constructor(e,t){this.ANONYMOUS_USER_STORAGE_KEY="gf_anonymous_user_info",this.ANONYMOUS_ENCRIPTED_TOKEN_STORAGE_KEY="gf_anonymous_encrypted_token",this._giveFreelyApiClient=e,this._logger=t,this.user=null}async resetUser(){await f.remove(this.ANONYMOUS_ENCRIPTED_TOKEN_STORAGE_KEY),await f.remove(this.ANONYMOUS_USER_STORAGE_KEY)}async upsertUser(e,t){let r=await this.fetchUser();try{const i={selectedCharity:e?.ein,selectedCharityThirdPartyIdentifier:e?.thirdPartyId,deviceId:t};if(!await this.shouldCreateOrUpdateUser(r,i))return r;const s=await f.get(this.ANONYMOUS_ENCRIPTED_TOKEN_STORAGE_KEY),{resultUser:a,resultToken:n}=await this._giveFreelyApiClient.createOrUpdateAnonymousUser(i,s);n&&await f.set(this.ANONYMOUS_ENCRIPTED_TOKEN_STORAGE_KEY,n),a&&(await f.set(this.ANONYMOUS_USER_STORAGE_KEY,a),r=a)}catch(e){this._logger.error("Error creating/updating anonymous user:",e)}return this.user=r,r}async fetchUser(){const e=await f.get(this.ANONYMOUS_USER_STORAGE_KEY);return this.user=e,e}async shouldCreateOrUpdateUser(e,t){return!e||e.charity?.ein!==t.selectedCharity||e.charity?.thirdPartyId!==t.selectedCharityThirdPartyIdentifier||e.deviceId!==t.deviceId}}const ee=e=>{try{return new URL(e),!0}catch{return!1}},te=new Set,re=new Set,ie={urls:["<all_urls>"],types:["main_frame"]};class se{async handle(e,t){const r=e.payload,{giveFreelyService:i}=t,s=i.getLogger();try{const e=$,t=await f.get(e)||[],i=t.findIndex((e=>e.shopId===r.shopId));return i>=0?t[i]=r:t.push(r),await f.set(e,t),s.debug("Stored Shopify shop ID",{shopInfo:r}),{type:N,payload:{success:!0}}}catch(e){return s.error("Error storing Shopify shop ID",{shopInfo:r,error:e}),{type:N,payload:{success:!1}}}}}class ae{async handle(e,t){const{shopId:r}=e.payload,{giveFreelyService:i}=t,s=i.getLogger();try{const e=$,t=(await f.get(e)||[]).find((e=>e.shopId===r));return t?(s.debug("Found domain for shop ID",{shopId:r,domain:t.domain}),{type:G,payload:{found:!0,domain:t.domain,shopInfo:t}}):(s.debug("No domain found for shop ID",{shopId:r}),{type:G,payload:{found:!1}})}catch(e){return s.error("Error getting domain for shop ID",{shopId:r,error:e}),{type:G,payload:{found:!1}}}}}class ne{constructor(e,t){this.lastCheck=null,this.trackEvent=e,this.logger=t}async initialize(){const e=await f.get(M);e&&(this.lastCheck=e)}getToday(){return(new Date).toISOString().split("T")[0]}async check(){try{const e=this.lastCheck??0,r=this.getToday();(e?new Date(e).toISOString().split("T")[0]:null)!==r&&(await this.trackEvent(t.checkoutPopupHealthCheck,{language:chrome.i18n.getUILanguage()}),this.lastCheck=Date.now(),await f.set(M,this.lastCheck))}catch(e){this.logger.error("Health check failed",{error:e instanceof Error?e.message:"Unknown error"})}}}class oe extends Error{constructor(e="Service not initialized"){super(e),this.name="UninitializedServiceError"}}exports.GiveFreelyService=class{constructor(e){this.partnerApiKey=e,this.dependencies={configService:new P(e)},this.logger=a.getInstance(),this.logger.configure({enabled:!0,title:"Background - GiveFreelyService"}),this.state={initialized:!1,wildfireService:null,partnerFilter:null,config:null,wfDeviceId:null,analytics:null,giveFreelyUserService:null,partnerApiKey:e,healthCheck:null}}assertInitialized(){if(!this.state.initialized)throw new oe}async initialize(e=async()=>(this.logger.debug("Using default partnerFilter."),!0)){if(!this.state.initialized)try{const t=await this.dependencies.configService.getConfig();this.logger.configure({minLevel:t.backgroundMinLogLevel??"error"}),this.logger.info("Initializing GiveFreelyService",{partnerApiKey:this.partnerApiKey});const r=this.createWildfireService(t),s=await(async(e=!0)=>{const t=await f.get(B),r=await W(),i=t!==r;var s;return e&&i&&await(s=r,f.set(B,s)),i})();s&&this.logger.info("New version detected."),this.state.healthCheck=new ne(this.trackEvent.bind(this),this.logger),await this.state.healthCheck.initialize(),await r.refreshCache(s);const n=new J(this.partnerApiKey,t,this.logger);this.state.giveFreelyUserService=new Q(n,this.logger),await this.state.giveFreelyUserService.fetchUser(),this.state.giveFreelyUserService.user?.id||await this.state.giveFreelyUserService.upsertUser(),this.state.analytics=new q(this.partnerApiKey,this.state.giveFreelyUserService),this.logger.addTransport(new i(this)),K.register(k,new H),K.register(R,new j),K.register(O,new z),K.register(F,new Y),K.register(D,new V),K.register(L,new X),K.register(N,new se),K.register(G,new ae),this.state.wildfireService=r,this.state.partnerFilter=e,this.state.config=t,this.state.initialized=!0,d.webRequest&&(d.webRequest.onBeforeRequest.addListener((e=>{const t=e.getLogger();return({requestId:r,url:i,initiator:s})=>{try{if(te.has(r)||re.has(r)||!ee(i))return;const{hostname:a,search:n}=new URL(i);if(a.includes("wild.link"))return t.info("Cashback activation request identified, ignoring affiliate check for request"),void re.add(r);let o;s&&ee(s)&&(o=new URL(s).hostname),e.hasAffiliation([a,o],n)&&(t.info("Affiliation found on url, adding request id to track"),te.add(r))}catch(e){t.error("Error on handleAffiliateOnBeforeRequest",e)}}})(this),ie),d.webRequest.onBeforeRedirect.addListener((e=>{const t=e.getLogger();return async({requestId:r,redirectUrl:i})=>{try{if(!te.has(r))return;t.info("Attempting to find active domain");const{hostname:s}=new URL(i),a=(await e.getActiveDomains()).find((e=>s===e.Domain||s.endsWith(`.${e.Domain}`)));if(!a)return;t.info("Found active domain. Updating standdown state"),await S(a.Domain)}catch(e){t.error("Error on handleAffiliateOnBeforeRedirect",e)}}})(this),ie),d.webRequest.onCompleted.addListener((()=>{const e=this.getLogger();return({requestId:t})=>{try{te.has(t)&&(e.info("Tracking request completed. Request Id:",t),te.delete(t))}catch(t){e.error("Error on handleAffiliateOnCompleted",t)}}})(),ie)),function(e){const t=a.getInstance();c.runtime.onMessage.addListener(((r,i,s)=>{function a(e){s(e)}const n=e(r,0);return n instanceof Promise?(n.then((e=>{e&&a(e)})).catch((e=>{t.warn("Async message callback error:",e)})),!0):n}))}((async(e,t)=>(this.logger.debug("Received message",{type:e.type,payload:e.payload}),this.handleMessage(e)))),this.logger.info("GiveFreelyService initialized successfully")}catch(e){const t=e instanceof Error?e.message:"Unknown error";throw this.logger.error("Failed to initialize GiveFreelyService",{error:t}),new Error(`Failed to initialize GiveFreelyService: ${t}`)}}getConfig(){return this.assertInitialized(),this.state.config}async getCachedConfig(){return this.state.config=await this.dependencies.configService.getConfig(),this.logger.configure({minLevel:this.state.config.backgroundMinLogLevel??"error"}),this.state.config}async getActiveDomains(){return this.assertInitialized(),this.state.wildfireService.getActiveDomains()}async shouldStandDown(e,t){this.assertInitialized();const r=new URL(t);if(!await this.state.partnerFilter(r.hostname))return this.logger.info("Should standdown due to partner filter."),!0;if(this.getConfig().merchantExclusions.some((t=>t.mutePopups&&e===t.domain)))return this.logger.info("Should standdown due to merchant exclusions"),!0;const i=await this.state.wildfireService.shouldStandDown(this.logger,e,t);return i&&this.logger.info("Should standdown due to vendor policy"),i}hasAffiliation(e,t){if(!this.state.wildfireService)return this.logger.warn("Wildfire service not initialized yet"),!1;const r=this.state.wildfireService.hasAffilliation(e,t);return r&&this.logger.info("Affiliation identified"),r}trackEvent(e,t){return this.assertInitialized(),this.state.analytics.track(e,this.state.wfDeviceId?.DeviceID,t)}getLogger(){return this.assertInitialized(),this.logger}async initializeWfDeviceId(){if(this.assertInitialized(),this.state.wfDeviceId=await this.state.wildfireService.getDevice(),!this.state.wfDeviceId)throw new Error("Failed to initialize Wildfire device ID");return this.state.wfDeviceId.DeviceID}getWfDeviceId(){return this.assertInitialized(),this.state.wfDeviceId.DeviceID}upsertUser(e,t){return this.assertInitialized(),this.state.giveFreelyUserService.upsertUser(e,t)}getCurrentUser(){return this.assertInitialized(),this.state.giveFreelyUserService.user}generateAffiliateUrl(e,t,r){return this.assertInitialized(),this.state.wildfireService.generateAffiliateUrl(e,t,r,this.state.partnerApiKey)}updateStanddownHistory(e){return this.assertInitialized(),S(e)}getMerchantRates(e){if(this.assertInitialized(),!this.state.wildfireService)throw new Error("Wildfire service not initialized");return this.state.wildfireService.getMerchantRates(e)}async healthCheck(){this.assertInitialized(),await this.state.healthCheck.check()}async handleMessage(e){this.assertInitialized();const t=K.get(e.type);if(!t)return;const r={giveFreelyService:this};return t.handle(e,r)}createWildfireService(e){this.logger.debug("config",this.state.config);const t=new m(e.wfSecret,e.wfAppId,e.deviceUrl,e.dataUrl,this.logger);return new U(t,this.logger,e.vanityBaseUrl,e.refreshInterval)}async destroy(){this.logger.info("Destroying GiveFreelyService"),await Promise.all([this.dependencies.configService.clearCache(),this.state.wildfireService?.clearCache(),this.state.giveFreelyUserService?.resetUser()]),this.state.initialized=!1,this.state.wildfireService=null,this.state.partnerFilter=null,this.state.analytics=null,this.state.config=null}},exports.browser=c,exports.isAdUnitMessage=e=>K.exists(e);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
class e{log({level:t,message:r,timestamp:i,data:s,title:a}){const n="debug"===t?"log":t,o=a?`[${a}]`:"";void 0!==s?console[n](`[${i}] - ${e.LOGGER_PREFIX} ${o} ${r}`,s):console[n](`[${i}] - ${e.LOGGER_PREFIX} ${o} ${r}`)}}var t,r;e.LOGGER_PREFIX="[GFAdUnit] -",function(e){e.checkoutPopupShown="CHECKOUT-POPUP-SHOWN",e.checkoutPopupNotShown="CHECKOUT-POPUP-NOT-SHOWN",e.checkoutPopupSupressed="CHECKOUT-POPUP-SUPRESSED",e.checkoutPopupDonation="CHECKOUT-POPUP-DONATION",e.checkoutPopupFailedActivation="CHECKOUT-POPUP-FAILED-ACTIVATION",e.checkoutPopupPurchaseConfirmed="CHECKOUT-POPUP-PURCHASE-CONFIRMED",e.checkoutPopupHealthCheck="CHECKOUT-POPUP-HEALTH-CHECK",e.checkouPopupActivationFailed="CHECKOUT-POPUP-ACTIVATION-FAILED",e.checkoutPopupOfferDetailsClicked="CHECKOUT-POPUP-OFFER-DETAILS-CLICKED"}(t||(t={})),function(e){e.adUnitLog="ADUNIT-LOG"}(r||(r={}));class i{constructor(e){this.gfService=e}log({level:e,message:t,data:i,title:s}){this.gfService.trackEvent(r.adUnitLog,{log:{level:e,message:t,data:i,title:s}})}}const s=["debug","info","warn","error"];class a{constructor(t){this.config={minLevel:t?.minLevel??"debug",enabled:t?.enabled??!1,transports:t?.transports??[new e],title:t?.title??""}}static getInstance(e){return a.instance||(a.instance=new a(e)),a.instance}configure(e){this.config={...this.config,...e}}shouldLog(e){return!!this.config.enabled&&s.indexOf(e)>=s.indexOf(this.config.minLevel)}createLogMessage(e,t,r){return{level:e,version:"1.1.2",message:t,timestamp:(new Date).toISOString(),data:r,title:this.config.title}}log(e,t,r){if(!this.shouldLog(e))return;const i=this.createLogMessage(e,t,r);this.config.transports.forEach((e=>e.log(i)))}debug(e,t){this.log("debug",e,t)}info(e,t){this.log("info",e,t)}warn(e,t){this.log("warn",e,t)}error(e,t){this.log("error",e,t)}addTransport(e){this.config.transports.push(e)}clearTransports(){this.config.transports=[]}disable(){this.config.enabled=!1}enable(){this.config.enabled=!0}}const n=3,o=e=>1e3*e;async function g(e,t={}){const{maxAttempts:r=n,delayFn:i=o}=t;let s=null;for(let t=1;t<=r;t+=1)try{return await e()}catch(e){s=e,t<r&&await new Promise((e=>{setTimeout(e,i(t))}))}throw s||new Error("Operation failed after multiple attempts")}const c=chrome;function l(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self&&self;var h={exports:{}};"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,function(e){if(!(globalThis.chrome&&globalThis.chrome.runtime&&globalThis.chrome.runtime.id))throw new Error("This script should only be loaded in a browser extension.");if(globalThis.browser&&globalThis.browser.runtime&&globalThis.browser.runtime.id)e.exports=globalThis.browser;else{const t="The message port closed before a response was received.",r=e=>{const r={alarms:{clear:{minArgs:0,maxArgs:1},clearAll:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getAll:{minArgs:0,maxArgs:0}},bookmarks:{create:{minArgs:1,maxArgs:1},get:{minArgs:1,maxArgs:1},getChildren:{minArgs:1,maxArgs:1},getRecent:{minArgs:1,maxArgs:1},getSubTree:{minArgs:1,maxArgs:1},getTree:{minArgs:0,maxArgs:0},move:{minArgs:2,maxArgs:2},remove:{minArgs:1,maxArgs:1},removeTree:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1},update:{minArgs:2,maxArgs:2}},browserAction:{disable:{minArgs:0,maxArgs:1,fallbackToNoCallback:!0},enable:{minArgs:0,maxArgs:1,fallbackToNoCallback:!0},getBadgeBackgroundColor:{minArgs:1,maxArgs:1},getBadgeText:{minArgs:1,maxArgs:1},getPopup:{minArgs:1,maxArgs:1},getTitle:{minArgs:1,maxArgs:1},openPopup:{minArgs:0,maxArgs:0},setBadgeBackgroundColor:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setBadgeText:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setIcon:{minArgs:1,maxArgs:1},setPopup:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setTitle:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},browsingData:{remove:{minArgs:2,maxArgs:2},removeCache:{minArgs:1,maxArgs:1},removeCookies:{minArgs:1,maxArgs:1},removeDownloads:{minArgs:1,maxArgs:1},removeFormData:{minArgs:1,maxArgs:1},removeHistory:{minArgs:1,maxArgs:1},removeLocalStorage:{minArgs:1,maxArgs:1},removePasswords:{minArgs:1,maxArgs:1},removePluginData:{minArgs:1,maxArgs:1},settings:{minArgs:0,maxArgs:0}},commands:{getAll:{minArgs:0,maxArgs:0}},contextMenus:{remove:{minArgs:1,maxArgs:1},removeAll:{minArgs:0,maxArgs:0},update:{minArgs:2,maxArgs:2}},cookies:{get:{minArgs:1,maxArgs:1},getAll:{minArgs:1,maxArgs:1},getAllCookieStores:{minArgs:0,maxArgs:0},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}},devtools:{inspectedWindow:{eval:{minArgs:1,maxArgs:2,singleCallbackArg:!1}},panels:{create:{minArgs:3,maxArgs:3,singleCallbackArg:!0},elements:{createSidebarPane:{minArgs:1,maxArgs:1}}}},downloads:{cancel:{minArgs:1,maxArgs:1},download:{minArgs:1,maxArgs:1},erase:{minArgs:1,maxArgs:1},getFileIcon:{minArgs:1,maxArgs:2},open:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},pause:{minArgs:1,maxArgs:1},removeFile:{minArgs:1,maxArgs:1},resume:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1},show:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},extension:{isAllowedFileSchemeAccess:{minArgs:0,maxArgs:0},isAllowedIncognitoAccess:{minArgs:0,maxArgs:0}},history:{addUrl:{minArgs:1,maxArgs:1},deleteAll:{minArgs:0,maxArgs:0},deleteRange:{minArgs:1,maxArgs:1},deleteUrl:{minArgs:1,maxArgs:1},getVisits:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1}},i18n:{detectLanguage:{minArgs:1,maxArgs:1},getAcceptLanguages:{minArgs:0,maxArgs:0}},identity:{launchWebAuthFlow:{minArgs:1,maxArgs:1}},idle:{queryState:{minArgs:1,maxArgs:1}},management:{get:{minArgs:1,maxArgs:1},getAll:{minArgs:0,maxArgs:0},getSelf:{minArgs:0,maxArgs:0},setEnabled:{minArgs:2,maxArgs:2},uninstallSelf:{minArgs:0,maxArgs:1}},notifications:{clear:{minArgs:1,maxArgs:1},create:{minArgs:1,maxArgs:2},getAll:{minArgs:0,maxArgs:0},getPermissionLevel:{minArgs:0,maxArgs:0},update:{minArgs:2,maxArgs:2}},pageAction:{getPopup:{minArgs:1,maxArgs:1},getTitle:{minArgs:1,maxArgs:1},hide:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setIcon:{minArgs:1,maxArgs:1},setPopup:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setTitle:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},show:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},permissions:{contains:{minArgs:1,maxArgs:1},getAll:{minArgs:0,maxArgs:0},remove:{minArgs:1,maxArgs:1},request:{minArgs:1,maxArgs:1}},runtime:{getBackgroundPage:{minArgs:0,maxArgs:0},getPlatformInfo:{minArgs:0,maxArgs:0},openOptionsPage:{minArgs:0,maxArgs:0},requestUpdateCheck:{minArgs:0,maxArgs:0},sendMessage:{minArgs:1,maxArgs:3},sendNativeMessage:{minArgs:2,maxArgs:2},setUninstallURL:{minArgs:1,maxArgs:1}},sessions:{getDevices:{minArgs:0,maxArgs:1},getRecentlyClosed:{minArgs:0,maxArgs:1},restore:{minArgs:0,maxArgs:1}},storage:{local:{clear:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}},managed:{get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1}},sync:{clear:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}}},tabs:{captureVisibleTab:{minArgs:0,maxArgs:2},create:{minArgs:1,maxArgs:1},detectLanguage:{minArgs:0,maxArgs:1},discard:{minArgs:0,maxArgs:1},duplicate:{minArgs:1,maxArgs:1},executeScript:{minArgs:1,maxArgs:2},get:{minArgs:1,maxArgs:1},getCurrent:{minArgs:0,maxArgs:0},getZoom:{minArgs:0,maxArgs:1},getZoomSettings:{minArgs:0,maxArgs:1},goBack:{minArgs:0,maxArgs:1},goForward:{minArgs:0,maxArgs:1},highlight:{minArgs:1,maxArgs:1},insertCSS:{minArgs:1,maxArgs:2},move:{minArgs:2,maxArgs:2},query:{minArgs:1,maxArgs:1},reload:{minArgs:0,maxArgs:2},remove:{minArgs:1,maxArgs:1},removeCSS:{minArgs:1,maxArgs:2},sendMessage:{minArgs:2,maxArgs:3},setZoom:{minArgs:1,maxArgs:2},setZoomSettings:{minArgs:1,maxArgs:2},update:{minArgs:1,maxArgs:2}},topSites:{get:{minArgs:0,maxArgs:0}},webNavigation:{getAllFrames:{minArgs:1,maxArgs:1},getFrame:{minArgs:1,maxArgs:1}},webRequest:{handlerBehaviorChanged:{minArgs:0,maxArgs:0}},windows:{create:{minArgs:0,maxArgs:1},get:{minArgs:1,maxArgs:2},getAll:{minArgs:0,maxArgs:1},getCurrent:{minArgs:0,maxArgs:1},getLastFocused:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},update:{minArgs:2,maxArgs:2}}};if(0===Object.keys(r).length)throw new Error("api-metadata.json has not been included in browser-polyfill");class i extends WeakMap{constructor(e,t=void 0){super(t),this.createItem=e}get(e){return this.has(e)||this.set(e,this.createItem(e)),super.get(e)}}const s=(t,r)=>(...i)=>{e.runtime.lastError?t.reject(new Error(e.runtime.lastError.message)):r.singleCallbackArg||i.length<=1&&!1!==r.singleCallbackArg?t.resolve(i[0]):t.resolve(i)},a=e=>1==e?"argument":"arguments",n=(e,t,r)=>new Proxy(t,{apply:(t,i,s)=>r.call(i,e,...s)});let o=Function.call.bind(Object.prototype.hasOwnProperty);const g=(e,t={},r={})=>{let i=Object.create(null),c={has:(t,r)=>r in e||r in i,get(c,l,h){if(l in i)return i[l];if(!(l in e))return;let d=e[l];if("function"==typeof d)if("function"==typeof t[l])d=n(e,e[l],t[l]);else if(o(r,l)){let t=((e,t)=>function(r,...i){if(i.length<t.minArgs)throw new Error(`Expected at least ${t.minArgs} ${a(t.minArgs)} for ${e}(), got ${i.length}`);if(i.length>t.maxArgs)throw new Error(`Expected at most ${t.maxArgs} ${a(t.maxArgs)} for ${e}(), got ${i.length}`);return new Promise(((a,n)=>{if(t.fallbackToNoCallback)try{r[e](...i,s({resolve:a,reject:n},t))}catch(s){console.warn(`${e} API method doesn't seem to support the callback parameter, falling back to call it without a callback: `,s),r[e](...i),t.fallbackToNoCallback=!1,t.noCallback=!0,a()}else t.noCallback?(r[e](...i),a()):r[e](...i,s({resolve:a,reject:n},t))}))})(l,r[l]);d=n(e,e[l],t)}else d=d.bind(e);else if("object"==typeof d&&null!==d&&(o(t,l)||o(r,l)))d=g(d,t[l],r[l]);else{if(!o(r,"*"))return Object.defineProperty(i,l,{configurable:!0,enumerable:!0,get:()=>e[l],set(t){e[l]=t}}),d;d=g(d,t[l],r["*"])}return i[l]=d,d},set:(t,r,s,a)=>(r in i?i[r]=s:e[r]=s,!0),defineProperty:(e,t,r)=>Reflect.defineProperty(i,t,r),deleteProperty:(e,t)=>Reflect.deleteProperty(i,t)},l=Object.create(e);return new Proxy(l,c)},c=e=>({addListener(t,r,...i){t.addListener(e.get(r),...i)},hasListener:(t,r)=>t.hasListener(e.get(r)),removeListener(t,r){t.removeListener(e.get(r))}}),l=new i((e=>"function"!=typeof e?e:function(t){const r=g(t,{},{getContent:{minArgs:0,maxArgs:0}});e(r)})),h=new i((e=>"function"!=typeof e?e:function(t,r,i){let s,a,n=!1,o=new Promise((e=>{s=function(t){n=!0,e(t)}}));try{a=e(t,r,s)}catch(e){a=Promise.reject(e)}const g=!0!==a&&((c=a)&&"object"==typeof c&&"function"==typeof c.then);var c;if(!0!==a&&!g&&!n)return!1;return(g?a:o).then((e=>{i(e)}),(e=>{let t;t=e&&(e instanceof Error||"string"==typeof e.message)?e.message:"An unexpected error occurred",i({__mozWebExtensionPolyfillReject__:!0,message:t})})).catch((e=>{console.error("Failed to send onMessage rejected reply",e)})),!0})),d=({reject:r,resolve:i},s)=>{e.runtime.lastError?e.runtime.lastError.message===t?i():r(new Error(e.runtime.lastError.message)):s&&s.__mozWebExtensionPolyfillReject__?r(new Error(s.message)):i(s)},m=(e,t,r,...i)=>{if(i.length<t.minArgs)throw new Error(`Expected at least ${t.minArgs} ${a(t.minArgs)} for ${e}(), got ${i.length}`);if(i.length>t.maxArgs)throw new Error(`Expected at most ${t.maxArgs} ${a(t.maxArgs)} for ${e}(), got ${i.length}`);return new Promise(((e,t)=>{const s=d.bind(null,{resolve:e,reject:t});i.push(s),r.sendMessage(...i)}))},u={devtools:{network:{onRequestFinished:c(l)}},runtime:{onMessage:c(h),onMessageExternal:c(h),sendMessage:m.bind(null,"sendMessage",{minArgs:1,maxArgs:3})},tabs:{sendMessage:m.bind(null,"sendMessage",{minArgs:2,maxArgs:3})}},f={clear:{minArgs:1,maxArgs:1},get:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}};return r.privacy={network:{"*":f},services:{"*":f},websites:{"*":f}},g(e,u,r)};e.exports=r(chrome)}}(h);var d=l(h.exports);class m{constructor(e,t,r,i,s){this.wfSecret=e,this.wfAppId=t,this.deviceUrl=r,this.dataUrl=i,this._logger=s}async getWildfireDevice(){return(await fetch(this.deviceUrl,{method:"POST",body:JSON.stringify({}),headers:{"Content-Type":"application/json","WF-Secret":this.wfSecret,"WF-App-ID":this.wfAppId}})).json()}async getActiveDomains(){return(await fetch(`${this.dataUrl}/${this.wfAppId}/active-domain/1`)).json()}async getStanddownPolicy(){const e=`${this.dataUrl}/${this.wfAppId}/stand-down-policy/1`;try{const t=await fetch(e);if(t.ok)return t.json();throw new Error(`Failed to fetch standdown policy. Status ${t.status}`)}catch(t){return this._logger.error("Failed to fetch the standdown policy",{err:t,url:e}),{Domains:[],LostAttribution:[],Params:[],Serp:[]}}}async getMerchantRates(){const e=`${this.dataUrl}/${this.wfAppId}/merchant-rate/1`;try{const t=await fetch(e);if(this._logger.debug("Merchant rates response",{response:t}),!t.ok)throw new Error(`Failed to fetch merchant rates. Status ${t.status}`);const r=t.json();return this._logger.debug("Merchant rates",{rates:r}),r}catch(t){return this._logger.error("Failed to fetch the merchant rates",{err:t,url:e}),[]}}}const u="GIVE_FREELY_",f={async get(e){const t=`${u}${e}`;return(await d.storage.local.get(t))[t]||null},async set(e,t){const r=`${u}${e}`;await d.storage.local.set({[r]:t})},async remove(e){const t=Array.isArray(e)?e.map((e=>`${u}${e}`)):`${u}${e}`;await d.storage.local.remove(t)}},A="wfDevice",y="wfDomains",p="wfStanddownPolicy",w="wfStanddownHistory",v="wfLastCacheRefresh",x="wfMerchantRates";async function E(e,t,r){try{const i=(await b())?.[t];if(i&&i>Date.now())return e.info("Standing down because current affiliate stand down session has not expired"),S(t),!0;await this.refreshCache();const s=await this.getStanddownPolicy();if(_(s,new URL(r).search))return e.info("Standing down because url has affiliate params"),S(t),!0;e.info("No need to stand down.")}catch(t){e.error("Exception produced when trying to determine if it should stand down",t)}return!1}function C(e,t){if(!this.standDownPolicy)return this.logger.warn("Couldnt check affilliation. No stand down policy found"),this.getStanddownPolicy().then((e=>{this.standDownPolicy=e})),!1;const r=e.some((e=>I(this.standDownPolicy,e)))||_(this.standDownPolicy,t);return this.logger.info("Affilliation result:",r),r}const b=async()=>f.get(w),S=async(e,t=1)=>{const r=await b()??{};r[e]=Date.now()+60*t*60*1e3,await f.set(w,r)},I=(e,t)=>{if(!t)return!1;const{Domains:r}=e;return r.some((e=>t.includes(e)))},_=(e,t)=>{if(!t)return!1;const{Params:r}=e;return r.some((e=>t.toLowerCase().includes(`?${e.toLowerCase()}`)))||r.some((e=>t.toLowerCase().includes(`&${e.toLowerCase()}`)))};class T{constructor(e,t,r,i){this.userId=e,this.charityEin=t,this.charityThirdPartyIdentifier=r,this.partnerId=i}toString(){return`userId=${this.encodeUserId(this.userId)},charityEin=${this.charityEin},charityThirdPartyIdentifier=${this.charityThirdPartyIdentifier},partnerId=${this.partnerId}`}encodeUserId(e){return btoa(e).replace(/_/g,"/").replace(/-/g,"+")}}class U{constructor(e,t,r,i=21600){if(this.standDownPolicy=null,this.shouldStandDown=E.bind(this),this.hasAffilliation=C.bind(this),!e||!r)throw new Error("WildfireService requires wildfireClient and vanityBaseUrl");this.wildfireClient=e,this.vanityBaseUrl=r.replace(/\/$/,""),this.refreshInterval=i,this.logger=t}async getActiveDomains(){try{await this.refreshCache();const e=await f.get(y);if(!e)throw Error("Failed to retrieve active domains");return e}catch(e){throw this.logger.error("Failed to get active domains:",e),new Error("Failed to retrieve active domains")}}async getMerchantRates(e){try{await this.refreshCache();const t=await f.get(x);if(!t)throw new Error("Failed to retrieve merchant rates");return t[e]}catch(e){throw this.logger.error("Failed to retrieve merchant rates:",e),new Error("Failed to retrieve merchant rates")}}async generateAffiliateUrl(e,t,r,i){if(!e||!t||!i)throw new Error("Missing required parameters for affiliate URL generation");try{const s=await this.getDevice(),a=await this.generateTrackingCode(r,i,s),n=encodeURIComponent(t),o=encodeURIComponent(a);return`${this.vanityBaseUrl}/e?d=${s.DeviceID}&c=${e.ID}&tc=${o}&url=${n}`}catch(e){throw this.logger.error("Failed to generate affiliate URL:",e),new Error("Failed to generate affiliate URL")}}async clearCache(){try{await Promise.all([f.remove(A),f.remove(y),f.remove(v),f.remove(p),f.remove(x)])}catch(e){throw this.logger.error("Failed to clear cache:",e),new Error("Failed to clear cache")}}async refreshCache(e=!1){try{const t=await f.get(v),r=Date.now();(e||!t||r-t>1e3*this.refreshInterval)&&(await Promise.all([this.syncActiveDomains(),this.syncStanddownPolicy(),this.syncMerchantRates()]),await f.set(v,r)),this.standDownPolicy=await this.getStanddownPolicy()}catch(e){throw this.logger.error("Failed to refresh cache:",e),new Error("Failed to refresh cache")}}async getStanddownPolicy(){return await f.get(p)??(()=>{throw new Error("Failed to retrieve standdown policy. Storage value was null")})()}getRetryOptions(){return{delayFn:e=>1e3*e,maxAttempts:3}}async getDevice(){try{const e=await f.get(A);return e?.DeviceID?e:g((()=>this.fetchAndStoreDevice()),this.getRetryOptions())}catch(e){throw this.logger.error("Failed to get device:",e),new Error("Failed to retrieve device information")}}async fetchAndStoreDevice(){const e=await this.wildfireClient.getWildfireDevice();if(!e?.DeviceID)throw new Error("Failed to retrieve device information");return await f.set(A,e),e}async generateTrackingCode(e,t,r){if(!(e?.id&&e?.charity&&e?.charity.ein&&e?.charity.thirdPartyId)){const e=`notregistered-${r.DeviceID}`;return this.logger.info("User not provided. Using GF's default bucket instead.",e),e}return new T(e.id,e.charity.ein,e.charity.thirdPartyId,t).toString()}async syncActiveDomains(){const e=await g((()=>this.wildfireClient.getActiveDomains()),this.getRetryOptions());return await f.set(y,e),e}async syncStanddownPolicy(){const e=await g((()=>this.wildfireClient.getStanddownPolicy()),this.getRetryOptions());return this.logger.info("Updated standdown policy",e),await f.set(p,e),e}async syncMerchantRates(){const e=await g((()=>this.wildfireClient.getMerchantRates()),this.getRetryOptions());return this.logger.debug("Merchant rates",e),await f.set(x,e),e}}class P{constructor(e){this.partnerApiKey=e,this.partnerConfig=null,this.globalConfig=null,this.lastConfigRefresh=0,this.CONFIG_REFRESH_INTERVAL=3e5,this.PARTNER_CONFIG_STORAGE_KEY="gf_partner_config",this.GLOBAL_CONFIG_STORAGE_KEY="gf_global_config"}getRetryOptions(){return{delayFn:e=>1e3*e,maxAttempts:3}}async fetchAndUpdatePartnerConfig(){const e=await g((async()=>{const e=await fetch(`https://cdn.givefreely.com/adunit/config/${this.partnerApiKey}.json`,{cache:"no-store"});if(!e.ok)throw new Error(`Failed to fetch partner config: ${e.statusText}`);return e.json()}),this.getRetryOptions()),t={config:e,lastRefresh:Date.now()};return await f.set(this.PARTNER_CONFIG_STORAGE_KEY,t),e}async fetchAndUpdateGlobalConfig(){const e=await g((async()=>{const e=await fetch("https://cdn.givefreely.com/adunit/config/global.json",{cache:"no-store"});if(!e.ok)throw new Error(`Failed to fetch global config: ${e.statusText}`);return e.json()}),this.getRetryOptions()),t={config:e,lastRefresh:Date.now()};return await f.set(this.GLOBAL_CONFIG_STORAGE_KEY,t),e}async getConfig(){const e=Date.now(),t=await f.get(this.PARTNER_CONFIG_STORAGE_KEY),r=await f.get(this.GLOBAL_CONFIG_STORAGE_KEY);if(this.partnerConfig=t?.config||null,this.globalConfig=r?.config||null,this.lastConfigRefresh=Math.max(t?.lastRefresh||0,r?.lastRefresh||0),!this.partnerConfig||!this.globalConfig||e-this.lastConfigRefresh>this.CONFIG_REFRESH_INTERVAL)try{const[t,r]=await Promise.all([this.fetchAndUpdatePartnerConfig(),this.fetchAndUpdateGlobalConfig()]);this.partnerConfig=t,this.globalConfig=r,this.lastConfigRefresh=e}catch(e){if(!this.partnerConfig||!this.globalConfig)throw e}const i=this.mergeConfigs(this.globalConfig,this.partnerConfig);return((e,t,r)=>{const i=[...t.merchantExclusions??[],...r.merchantExclusions??[]];e.merchantExclusions=i})(i,this.globalConfig,this.partnerConfig),i}async clearCache(){await Promise.all([f.remove(this.PARTNER_CONFIG_STORAGE_KEY),f.remove(this.GLOBAL_CONFIG_STORAGE_KEY)]),this.partnerConfig=null,this.globalConfig=null,this.lastConfigRefresh=0}mergeConfigs(e,t){if(!t)return e;const r={...e};return Object.keys(t).forEach((i=>{if(!Object.prototype.hasOwnProperty.call(t,i))return;const s=e[i],a=t[i];Array.isArray(a)&&Array.isArray(s)?r[i]=a:r[i]=null===a||"object"!=typeof a||"object"!=typeof s?void 0!==a?a:s:this.mergeConfigs(s,a)})),r}getStoredState(e){return e?{config:e.config,lastRefresh:e.lastRefresh}:{config:null,lastRefresh:0}}}const D="TRACK_EVENT",O="GF_HIDE_POPUP",R="GF_GET_POPUP_CONFIG",k="GF_IS_ACTIVE_DOMAIN",F="GF_SHOULD_STAND_DOWN",L="GF_ACTIVATE_OFFER",N="GF_STORE_SHOPIFY_SHOP_ID",$="GF_GET_DOMAIN_BY_SHOP_ID",G="shopify_shop_ids",M="gf_last_health_check";class K{static register(e,t){this.strategies.set(e,t)}static get(e){return this.strategies.get(e)}static exists(e){return!!function(e){return"object"==typeof e&&null!==e&&"type"in e&&"payload"in e&&"string"==typeof e.type}(e)&&this.strategies.has(e.type)}}K.strategies=new Map;class H{async handle(e,t){const{hostname:r}=e.payload,{giveFreelyService:i}=t,s=i.getLogger(),a=await i.getConfig().merchantExclusions,n=await i.getActiveDomains();s.debug("Checking hostname",{hostname:r});try{if(i.healthCheck(),a.some((e=>e.disableDomain&&(r===e.domain||r.endsWith(`.${e.domain}`)))))return s.debug("Hostname is in merchant exclusions",{hostname:r}),{type:k,payload:{isActive:!1,domain:void 0,rates:[]}};if(!n||0===n.length)throw new Error("No active domains");s.debug("Retrieved active domains",{count:n.length});const e=n.find((e=>r===e.Domain||r.endsWith(`.${e.Domain}`)));s.debug("Domain check result",{hostname:r,isActive:!!e});let t=[];if(e?.ID){const r=await i.getMerchantRates(e.Merchant.ID);r&&(s.debug("Retrieved merchant rates",{merchantRates:r}),t=r)}return{type:k,payload:{isActive:!!e,domain:e,rates:t}}}catch(e){return s.error("Error checking domain",{hostname:r,error:e}),{type:k,payload:{isActive:!1,domain:void 0,rates:[]}}}}}class j{async handle(e,t){const{giveFreelyService:r}=t,i=r.getLogger(),s=await r.getCachedConfig();try{return i.debug("Fetching popup config"),{type:R,payload:{config:s}}}catch(e){return i.error("Error fetching popup config",{error:e}),{type:R,payload:{config:null}}}}}class z{async handle(e,t){const{days:r}=e.payload,{giveFreelyService:i}=t,s=i.getLogger();try{const e=new Date;return e.setDate(e.getDate()+r),await f.set("popup_hide",{popupHide:e.getTime()}),s.debug("Popup hidden",{days:r,expiry:e}),{type:O,payload:{ack:!0}}}catch(e){return s.error("Error hiding popup",{days:r,error:e}),{type:O,payload:{ack:!1}}}}}class Y{async handle(e,t){const{activeDomain:r,url:i}=e.payload,{giveFreelyService:s}=t,a=s.getLogger();a.debug("Checking if we should stand down",{activeDomain:r});try{const e=await s.shouldStandDown(r,i);return{type:F,payload:{result:e}}}catch(e){return a.error("Error checking if we should stand down. Returning false",{activeDomain:r,error:e}),{type:F,payload:{result:!1}}}}}const B="version",W=()=>String("1.1.2");class q{constructor(e,t,r=60){this._config=null,this.track=async(e,t,r)=>{null===this._config&&(this._config=await new P(this._partnerApiKey).getConfig());const i={partner:`adUnit_${this._partnerApiKey}`,eventType:e,eventData:{userId:this._userService?.user?.id,libVersion:W(),wfDeviceId:t,...r}};try{const e=JSON.stringify(i);return await this.shouldBroadcastEvent(e)?(await fetch(this._config.eventsUrl,{headers:{"Content-Type":"application/json"},method:"POST",body:e,cache:"no-store"}),await this.pushEventToHistory(e),!0):(this._logger.info("This event has already been broadcasted during the past hour",i),!1)}catch(e){return this._logger.info("Something unexpected happened when dispatching a gf event",{error:e,event:i}),!1}},this.getEventsHistory=async()=>{const{GF_AD_EVENT_HISTORY:e}=await c.storage.local.get({GF_AD_EVENT_HISTORY:""}),t=(e=>{const t=new Date;return t.setTime(t.getTime()+60*e*1e3),t.getTime()})(-this._debounceWindowInMinutes),r=(e||[]).filter((e=>e.createdAt>t));return await c.storage.local.set({GF_AD_EVENT_HISTORY:r}),r},this.shouldBroadcastEvent=async e=>!(await this.getEventsHistory()).some((t=>t.payload===e)),this.pushEventToHistory=async e=>{const t=await this.getEventsHistory();t.push({createdAt:Date.now(),payload:e}),await c.storage.local.set({GF_AD_EVENT_HISTORY:t})},this._partnerApiKey=e,this._userService=t,this._debounceWindowInMinutes=r,this._logger=a.getInstance({title:"Analytics"})}}q.trackEvent=async(e,t)=>{await async function(e){return new Promise(((t,r)=>{try{const i=i=>chrome.runtime.lastError?r({error:chrome.runtime.lastError,message:e}):t(i);c.runtime.sendMessage(e,i)}catch(e){r(e)}}))}({type:D,payload:{eventType:e,eventData:t}})};class V{async handle(e,t){const{eventType:r,eventData:i}=e.payload,{giveFreelyService:s}=t,a=s.getLogger();a.debug("Broadcasting event",{eventType:r,eventData:i});try{const e=await s.trackEvent(r,i);return{type:D,payload:{result:e}}}catch(e){return a.error("Error broadcasting event. Returning false",{eventType:r,eventData:i,error:e}),{type:D,payload:{result:!1}}}}}class X{async handle(e,r){const{activeDomain:i,originalUrl:s,selectedCharity:a}=e.payload,{giveFreelyService:n}=r,o=n.getLogger();o.debug("Activating offer",{activeDomain:i,selectedCharity:a});try{if(!a?.ein||!a?.thirdPartyId)throw new Error("Missing charity information");const e=await n.initializeWfDeviceId();if(!e)throw new Error("Failed to initialize Wildfire device ID");const t=await n.upsertUser(a,e);o.debug("Upserted user",{currentUser:t}),t||o.info("Failed to create user. Using default bucket's user");const r=await n.generateAffiliateUrl(i,s,t);return o.debug("Generated affiliate URL",{affiliateUrl:r}),(async e=>{const t=c.tabs.getCurrent(),r=await t,i=await c.tabs.create({url:e,openerTabId:r?.id,active:!1,pinned:!0});await(async e=>new Promise((t=>{c.tabs.onUpdated.addListener((async function r(i,s){if(i===e&&"complete"===s.status){if(c.tabs.onUpdated.removeListener(r),!(await c.tabs.get(e)).url)return void t(!1);t(!0)}}))})))(i.id)&&setTimeout((()=>{i?.id&&c.tabs.remove(i.id)}),3e4)})(r),await n.updateStanddownHistory(i.Domain),o.debug("Activated offer",{activeDomain:i,selectedCharity:a}),{type:L,payload:{response:!0}}}catch(e){return o.error("Error activating offer",{activeDomain:i,error:e}),n.trackEvent(t.checkoutPopupFailedActivation,{activeDomain:i,error:e}),{type:L,payload:{response:!1}}}}}var Z;!function(e){e[e.Pending=0]="Pending",e[e.Ready=1]="Ready",e[e.Received=2]="Received",e[e.Donated=3]="Donated",e[e.Disqualified=4]="Disqualified"}(Z||(Z={}));class J{constructor(e,t,r){this.adUnitId=e,this.config=t,this.logger=r}async getAnonymousUserCommissions(e,t){try{const r=new URLSearchParams(t).toString(),i=await fetch(`${this.config.apiConfig.baseUri}/${this.config.apiConfig.getAnonymousUserComissionsPath}?${r}`,{method:"GET",headers:{"Content-Type":"application/json","X-AnonymousUserToken":e}});if(!i.ok)throw new Error("Failed to fetch commissions");return await i.json()}catch(e){throw this.logger.error("[GiveFreelyApiClient] Error fetching anonymous user's commissions:",{error:e}),e}}async createOrUpdateAnonymousUser(e,t){try{const r=await fetch(`${this.config.apiConfig.baseUri}/${this.config.apiConfig.createAnonymousUserPath}?adUnitId=${this.adUnitId}`,{method:"PUT",headers:{"Content-Type":"application/json","X-GF-Platform":"adUnitLibrary","X-AnonymousUserToken":t??""},body:JSON.stringify(e)});if(!r.ok)throw new Error("Failed to create/update anonymous user");this.logger.info("[GiveFreelyApiClient] anonymous user created/updated succesfuly.");const i=await r.json(),s=r.headers.get("X-AnonymousUserToken");return{resultUser:{...i,charity:{ein:i.selectedCharity,thirdPartyId:i.selectedCharityThirdPartyIdentifier,name:void 0}},resultToken:s}}catch(e){throw this.logger.error("[GiveFreelyApiClient] Error creating anonymous user:",{error:e}),e}}}class Q{constructor(e,t){this.ANONYMOUS_USER_STORAGE_KEY="gf_anonymous_user_info",this.ANONYMOUS_ENCRIPTED_TOKEN_STORAGE_KEY="gf_anonymous_encrypted_token",this._giveFreelyApiClient=e,this._logger=t,this.user=null}async resetUser(){await f.remove(this.ANONYMOUS_ENCRIPTED_TOKEN_STORAGE_KEY),await f.remove(this.ANONYMOUS_USER_STORAGE_KEY)}async upsertUser(e,t){let r=await this.fetchUser();try{const i={selectedCharity:e?.ein,selectedCharityThirdPartyIdentifier:e?.thirdPartyId,deviceId:t};if(!await this.shouldCreateOrUpdateUser(r,i))return r;const s=await f.get(this.ANONYMOUS_ENCRIPTED_TOKEN_STORAGE_KEY),{resultUser:a,resultToken:n}=await this._giveFreelyApiClient.createOrUpdateAnonymousUser(i,s);n&&await f.set(this.ANONYMOUS_ENCRIPTED_TOKEN_STORAGE_KEY,n),a&&(await f.set(this.ANONYMOUS_USER_STORAGE_KEY,a),r=a)}catch(e){this._logger.error("Error creating/updating anonymous user:",e)}return this.user=r,r}async fetchUser(){const e=await f.get(this.ANONYMOUS_USER_STORAGE_KEY);return this.user=e,e}async shouldCreateOrUpdateUser(e,t){return!e||e.charity?.ein!==t.selectedCharity||e.charity?.thirdPartyId!==t.selectedCharityThirdPartyIdentifier||e.deviceId!==t.deviceId}}const ee=e=>{try{return new URL(e),!0}catch{return!1}},te=new Set,re=new Set,ie={urls:["<all_urls>"],types:["main_frame"]};class se{async handle(e,t){const r=e.payload,{giveFreelyService:i}=t,s=i.getLogger();try{const e=G,t=await f.get(e)||[],i=t.findIndex((e=>e.shopId===r.shopId));return i>=0?t[i]=r:t.push(r),await f.set(e,t),s.debug("Stored Shopify shop ID",{shopInfo:r}),{type:N,payload:{success:!0}}}catch(e){return s.error("Error storing Shopify shop ID",{shopInfo:r,error:e}),{type:N,payload:{success:!1}}}}}class ae{async handle(e,t){const{shopId:r}=e.payload,{giveFreelyService:i}=t,s=i.getLogger();try{const e=G,t=(await f.get(e)||[]).find((e=>e.shopId===r));return t?(s.debug("Found domain for shop ID",{shopId:r,domain:t.domain}),{type:$,payload:{found:!0,domain:t.domain,shopInfo:t}}):(s.debug("No domain found for shop ID",{shopId:r}),{type:$,payload:{found:!1}})}catch(e){return s.error("Error getting domain for shop ID",{shopId:r,error:e}),{type:$,payload:{found:!1}}}}}class ne{constructor(e,t){this.lastCheck=null,this.trackEvent=e,this.logger=t}async initialize(){const e=await f.get(M);e&&(this.lastCheck=e)}getToday(){return(new Date).toISOString().split("T")[0]}async check(){try{const e=this.lastCheck??0,r=this.getToday();(e?new Date(e).toISOString().split("T")[0]:null)!==r&&(await this.trackEvent(t.checkoutPopupHealthCheck,{language:chrome.i18n.getUILanguage()}),this.lastCheck=Date.now(),await f.set(M,this.lastCheck))}catch(e){this.logger.error("Health check failed",{error:e instanceof Error?e.message:"Unknown error"})}}}class oe extends Error{constructor(e="Service not initialized"){super(e),this.name="UninitializedServiceError"}}class GiveFreelyService{constructor(e){this.partnerApiKey=e,this.dependencies={configService:new P(e)},this.logger=a.getInstance(),this.logger.configure({enabled:!0,title:"Background - GiveFreelyService"}),this.state={initialized:!1,wildfireService:null,partnerFilter:null,config:null,wfDeviceId:null,analytics:null,giveFreelyUserService:null,partnerApiKey:e,healthCheck:null}}assertInitialized(){if(!this.state.initialized)throw new oe}async initialize(e=async()=>(this.logger.debug("Using default partnerFilter."),!0)){if(!this.state.initialized)try{const t=await this.dependencies.configService.getConfig();this.logger.configure({minLevel:t.backgroundMinLogLevel??"error"}),this.logger.info("Initializing GiveFreelyService",{partnerApiKey:this.partnerApiKey});const r=this.createWildfireService(t),s=await(async(e=!0)=>{const t=await f.get(B),r=await W(),i=t!==r;var s;return e&&i&&await(s=r,f.set(B,s)),i})();s&&this.logger.info("New version detected."),this.state.healthCheck=new ne(this.trackEvent.bind(this),this.logger),await this.state.healthCheck.initialize(),await r.refreshCache(s);const n=new J(this.partnerApiKey,t,this.logger);this.state.giveFreelyUserService=new Q(n,this.logger),await this.state.giveFreelyUserService.fetchUser(),this.state.giveFreelyUserService.user?.id||await this.state.giveFreelyUserService.upsertUser(),this.state.analytics=new q(this.partnerApiKey,this.state.giveFreelyUserService),this.logger.addTransport(new i(this)),K.register(k,new H),K.register(R,new j),K.register(O,new z),K.register(F,new Y),K.register(D,new V),K.register(L,new X),K.register(N,new se),K.register($,new ae),this.state.wildfireService=r,this.state.partnerFilter=e,this.state.config=t,this.state.initialized=!0,d.webRequest&&(d.webRequest.onBeforeRequest.addListener((e=>{const t=e.getLogger();return({requestId:r,url:i,initiator:s})=>{try{if(te.has(r)||re.has(r)||!ee(i))return;const{hostname:a,search:n}=new URL(i);if(a.includes("wild.link"))return t.info("Cashback activation request identified, ignoring affiliate check for request"),void re.add(r);let o;s&&ee(s)&&(o=new URL(s).hostname),e.hasAffiliation([a,o],n)&&(t.info("Affiliation found on url, adding request id to track"),te.add(r))}catch(e){t.error("Error on handleAffiliateOnBeforeRequest",e)}}})(this),ie),d.webRequest.onBeforeRedirect.addListener((e=>{const t=e.getLogger();return async({requestId:r,redirectUrl:i})=>{try{if(!te.has(r))return;t.info("Attempting to find active domain");const{hostname:s}=new URL(i),a=(await e.getActiveDomains()).find((e=>s===e.Domain||s.endsWith(`.${e.Domain}`)));if(!a)return;t.info("Found active domain. Updating standdown state"),await S(a.Domain)}catch(e){t.error("Error on handleAffiliateOnBeforeRedirect",e)}}})(this),ie),d.webRequest.onCompleted.addListener((()=>{const e=this.getLogger();return({requestId:t})=>{try{te.has(t)&&(e.info("Tracking request completed. Request Id:",t),te.delete(t))}catch(t){e.error("Error on handleAffiliateOnCompleted",t)}}})(),ie)),function(e){const t=a.getInstance();c.runtime.onMessage.addListener(((r,i,s)=>{function a(e){s(e)}const n=e(r,0);return n instanceof Promise?(n.then((e=>{e&&a(e)})).catch((e=>{t.warn("Async message callback error:",e)})),!0):n}))}((async(e,t)=>(this.logger.debug("Received message",{type:e.type,payload:e.payload}),this.handleMessage(e)))),this.logger.info("GiveFreelyService initialized successfully")}catch(e){const t=e instanceof Error?e.message:"Unknown error";throw this.logger.error("Failed to initialize GiveFreelyService",{error:t}),new Error(`Failed to initialize GiveFreelyService: ${t}`)}}getConfig(){return this.assertInitialized(),this.state.config}async getCachedConfig(){return this.state.config=await this.dependencies.configService.getConfig(),this.logger.configure({minLevel:this.state.config.backgroundMinLogLevel??"error"}),this.state.config}async getActiveDomains(){return this.assertInitialized(),this.state.wildfireService.getActiveDomains()}async shouldStandDown(e,t){this.assertInitialized();const r=new URL(t);if(!await this.state.partnerFilter(r.hostname))return this.logger.info("Should standdown due to partner filter."),!0;if(this.getConfig().merchantExclusions.some((t=>t.mutePopups&&e===t.domain)))return this.logger.info("Should standdown due to merchant exclusions"),!0;const i=await this.state.wildfireService.shouldStandDown(this.logger,e,t);return i&&this.logger.info("Should standdown due to vendor policy"),i}hasAffiliation(e,t){if(!this.state.wildfireService)return this.logger.warn("Wildfire service not initialized yet"),!1;const r=this.state.wildfireService.hasAffilliation(e,t);return r&&this.logger.info("Affiliation identified"),r}trackEvent(e,t){return this.assertInitialized(),this.state.analytics.track(e,this.state.wfDeviceId?.DeviceID,t)}getLogger(){return this.assertInitialized(),this.logger}async initializeWfDeviceId(){if(this.assertInitialized(),this.state.wfDeviceId=await this.state.wildfireService.getDevice(),!this.state.wfDeviceId)throw new Error("Failed to initialize Wildfire device ID");return this.state.wfDeviceId.DeviceID}getWfDeviceId(){return this.assertInitialized(),this.state.wfDeviceId.DeviceID}upsertUser(e,t){return this.assertInitialized(),this.state.giveFreelyUserService.upsertUser(e,t)}getCurrentUser(){return this.assertInitialized(),this.state.giveFreelyUserService.user}generateAffiliateUrl(e,t,r){return this.assertInitialized(),this.state.wildfireService.generateAffiliateUrl(e,t,r,this.state.partnerApiKey)}updateStanddownHistory(e){return this.assertInitialized(),S(e)}getMerchantRates(e){if(this.assertInitialized(),!this.state.wildfireService)throw new Error("Wildfire service not initialized");return this.state.wildfireService.getMerchantRates(e)}async healthCheck(){this.assertInitialized(),await this.state.healthCheck.check()}async handleMessage(e){this.assertInitialized();const t=K.get(e.type);if(!t)return;const r={giveFreelyService:this};return t.handle(e,r)}createWildfireService(e){this.logger.debug("config",this.state.config);const t=new m(e.wfSecret,e.wfAppId,e.deviceUrl,e.dataUrl,this.logger);return new U(t,this.logger,e.vanityBaseUrl,e.refreshInterval)}async destroy(){this.logger.info("Destroying GiveFreelyService"),await Promise.all([this.dependencies.configService.clearCache(),this.state.wildfireService?.clearCache(),this.state.giveFreelyUserService?.resetUser()]),this.state.initialized=!1,this.state.wildfireService=null,this.state.partnerFilter=null,this.state.analytics=null,this.state.config=null}}const ge=K.exists;export{GiveFreelyService,c as browser,ge as isAdUnitMessage};
|
|
1
|
+
class e{log({level:t,message:r,timestamp:i,data:s,title:a}){const n="debug"===t?"log":t,o=a?`[${a}]`:"";void 0!==s?console[n](`[${i}] - ${e.LOGGER_PREFIX} ${o} ${r}`,s):console[n](`[${i}] - ${e.LOGGER_PREFIX} ${o} ${r}`)}}var t,r;e.LOGGER_PREFIX="[GFAdUnit] -",function(e){e.checkoutPopupShown="CHECKOUT-POPUP-SHOWN",e.checkoutPopupNotShown="CHECKOUT-POPUP-NOT-SHOWN",e.checkoutPopupSupressed="CHECKOUT-POPUP-SUPRESSED",e.checkoutPopupDonation="CHECKOUT-POPUP-DONATION",e.checkoutPopupFailedActivation="CHECKOUT-POPUP-FAILED-ACTIVATION",e.checkoutPopupPurchaseConfirmed="CHECKOUT-POPUP-PURCHASE-CONFIRMED",e.checkoutPopupHealthCheck="CHECKOUT-POPUP-HEALTH-CHECK",e.checkouPopupActivationFailed="CHECKOUT-POPUP-ACTIVATION-FAILED",e.checkoutPopupOfferDetailsClicked="CHECKOUT-POPUP-OFFER-DETAILS-CLICKED"}(t||(t={})),function(e){e.adUnitLog="ADUNIT-LOG"}(r||(r={}));class i{constructor(e){this.gfService=e}log({level:e,message:t,data:i,title:s}){this.gfService.trackEvent(r.adUnitLog,{log:{level:e,message:t,data:i,title:s}})}}const s=["debug","info","warn","error"];class a{constructor(t){this.config={minLevel:t?.minLevel??"debug",enabled:t?.enabled??!1,transports:t?.transports??[new e],title:t?.title??""}}static getInstance(e){return a.instance||(a.instance=new a(e)),a.instance}configure(e){this.config={...this.config,...e}}shouldLog(e){return!!this.config.enabled&&s.indexOf(e)>=s.indexOf(this.config.minLevel)}createLogMessage(e,t,r){return{level:e,version:"1.1.2",message:t,timestamp:(new Date).toISOString(),data:r,title:this.config.title}}log(e,t,r){if(!this.shouldLog(e))return;const i=this.createLogMessage(e,t,r);this.config.transports.forEach((e=>e.log(i)))}debug(e,t){this.log("debug",e,t)}info(e,t){this.log("info",e,t)}warn(e,t){this.log("warn",e,t)}error(e,t){this.log("error",e,t)}addTransport(e){this.config.transports.push(e)}clearTransports(){this.config.transports=[]}disable(){this.config.enabled=!1}enable(){this.config.enabled=!0}}const n=3,o=e=>1e3*e;async function g(e,t={}){const{maxAttempts:r=n,delayFn:i=o}=t;let s=null;for(let t=1;t<=r;t+=1)try{return await e()}catch(e){s=e,t<r&&await new Promise((e=>{setTimeout(e,i(t))}))}throw s||new Error("Operation failed after multiple attempts")}const c=chrome;function l(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self&&self;var h={exports:{}};"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,function(e){if(!(globalThis.chrome&&globalThis.chrome.runtime&&globalThis.chrome.runtime.id))throw new Error("This script should only be loaded in a browser extension.");if(globalThis.browser&&globalThis.browser.runtime&&globalThis.browser.runtime.id)e.exports=globalThis.browser;else{const t="The message port closed before a response was received.",r=e=>{const r={alarms:{clear:{minArgs:0,maxArgs:1},clearAll:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getAll:{minArgs:0,maxArgs:0}},bookmarks:{create:{minArgs:1,maxArgs:1},get:{minArgs:1,maxArgs:1},getChildren:{minArgs:1,maxArgs:1},getRecent:{minArgs:1,maxArgs:1},getSubTree:{minArgs:1,maxArgs:1},getTree:{minArgs:0,maxArgs:0},move:{minArgs:2,maxArgs:2},remove:{minArgs:1,maxArgs:1},removeTree:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1},update:{minArgs:2,maxArgs:2}},browserAction:{disable:{minArgs:0,maxArgs:1,fallbackToNoCallback:!0},enable:{minArgs:0,maxArgs:1,fallbackToNoCallback:!0},getBadgeBackgroundColor:{minArgs:1,maxArgs:1},getBadgeText:{minArgs:1,maxArgs:1},getPopup:{minArgs:1,maxArgs:1},getTitle:{minArgs:1,maxArgs:1},openPopup:{minArgs:0,maxArgs:0},setBadgeBackgroundColor:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setBadgeText:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setIcon:{minArgs:1,maxArgs:1},setPopup:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setTitle:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},browsingData:{remove:{minArgs:2,maxArgs:2},removeCache:{minArgs:1,maxArgs:1},removeCookies:{minArgs:1,maxArgs:1},removeDownloads:{minArgs:1,maxArgs:1},removeFormData:{minArgs:1,maxArgs:1},removeHistory:{minArgs:1,maxArgs:1},removeLocalStorage:{minArgs:1,maxArgs:1},removePasswords:{minArgs:1,maxArgs:1},removePluginData:{minArgs:1,maxArgs:1},settings:{minArgs:0,maxArgs:0}},commands:{getAll:{minArgs:0,maxArgs:0}},contextMenus:{remove:{minArgs:1,maxArgs:1},removeAll:{minArgs:0,maxArgs:0},update:{minArgs:2,maxArgs:2}},cookies:{get:{minArgs:1,maxArgs:1},getAll:{minArgs:1,maxArgs:1},getAllCookieStores:{minArgs:0,maxArgs:0},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}},devtools:{inspectedWindow:{eval:{minArgs:1,maxArgs:2,singleCallbackArg:!1}},panels:{create:{minArgs:3,maxArgs:3,singleCallbackArg:!0},elements:{createSidebarPane:{minArgs:1,maxArgs:1}}}},downloads:{cancel:{minArgs:1,maxArgs:1},download:{minArgs:1,maxArgs:1},erase:{minArgs:1,maxArgs:1},getFileIcon:{minArgs:1,maxArgs:2},open:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},pause:{minArgs:1,maxArgs:1},removeFile:{minArgs:1,maxArgs:1},resume:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1},show:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},extension:{isAllowedFileSchemeAccess:{minArgs:0,maxArgs:0},isAllowedIncognitoAccess:{minArgs:0,maxArgs:0}},history:{addUrl:{minArgs:1,maxArgs:1},deleteAll:{minArgs:0,maxArgs:0},deleteRange:{minArgs:1,maxArgs:1},deleteUrl:{minArgs:1,maxArgs:1},getVisits:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1}},i18n:{detectLanguage:{minArgs:1,maxArgs:1},getAcceptLanguages:{minArgs:0,maxArgs:0}},identity:{launchWebAuthFlow:{minArgs:1,maxArgs:1}},idle:{queryState:{minArgs:1,maxArgs:1}},management:{get:{minArgs:1,maxArgs:1},getAll:{minArgs:0,maxArgs:0},getSelf:{minArgs:0,maxArgs:0},setEnabled:{minArgs:2,maxArgs:2},uninstallSelf:{minArgs:0,maxArgs:1}},notifications:{clear:{minArgs:1,maxArgs:1},create:{minArgs:1,maxArgs:2},getAll:{minArgs:0,maxArgs:0},getPermissionLevel:{minArgs:0,maxArgs:0},update:{minArgs:2,maxArgs:2}},pageAction:{getPopup:{minArgs:1,maxArgs:1},getTitle:{minArgs:1,maxArgs:1},hide:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setIcon:{minArgs:1,maxArgs:1},setPopup:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setTitle:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},show:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},permissions:{contains:{minArgs:1,maxArgs:1},getAll:{minArgs:0,maxArgs:0},remove:{minArgs:1,maxArgs:1},request:{minArgs:1,maxArgs:1}},runtime:{getBackgroundPage:{minArgs:0,maxArgs:0},getPlatformInfo:{minArgs:0,maxArgs:0},openOptionsPage:{minArgs:0,maxArgs:0},requestUpdateCheck:{minArgs:0,maxArgs:0},sendMessage:{minArgs:1,maxArgs:3},sendNativeMessage:{minArgs:2,maxArgs:2},setUninstallURL:{minArgs:1,maxArgs:1}},sessions:{getDevices:{minArgs:0,maxArgs:1},getRecentlyClosed:{minArgs:0,maxArgs:1},restore:{minArgs:0,maxArgs:1}},storage:{local:{clear:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}},managed:{get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1}},sync:{clear:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}}},tabs:{captureVisibleTab:{minArgs:0,maxArgs:2},create:{minArgs:1,maxArgs:1},detectLanguage:{minArgs:0,maxArgs:1},discard:{minArgs:0,maxArgs:1},duplicate:{minArgs:1,maxArgs:1},executeScript:{minArgs:1,maxArgs:2},get:{minArgs:1,maxArgs:1},getCurrent:{minArgs:0,maxArgs:0},getZoom:{minArgs:0,maxArgs:1},getZoomSettings:{minArgs:0,maxArgs:1},goBack:{minArgs:0,maxArgs:1},goForward:{minArgs:0,maxArgs:1},highlight:{minArgs:1,maxArgs:1},insertCSS:{minArgs:1,maxArgs:2},move:{minArgs:2,maxArgs:2},query:{minArgs:1,maxArgs:1},reload:{minArgs:0,maxArgs:2},remove:{minArgs:1,maxArgs:1},removeCSS:{minArgs:1,maxArgs:2},sendMessage:{minArgs:2,maxArgs:3},setZoom:{minArgs:1,maxArgs:2},setZoomSettings:{minArgs:1,maxArgs:2},update:{minArgs:1,maxArgs:2}},topSites:{get:{minArgs:0,maxArgs:0}},webNavigation:{getAllFrames:{minArgs:1,maxArgs:1},getFrame:{minArgs:1,maxArgs:1}},webRequest:{handlerBehaviorChanged:{minArgs:0,maxArgs:0}},windows:{create:{minArgs:0,maxArgs:1},get:{minArgs:1,maxArgs:2},getAll:{minArgs:0,maxArgs:1},getCurrent:{minArgs:0,maxArgs:1},getLastFocused:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},update:{minArgs:2,maxArgs:2}}};if(0===Object.keys(r).length)throw new Error("api-metadata.json has not been included in browser-polyfill");class i extends WeakMap{constructor(e,t=void 0){super(t),this.createItem=e}get(e){return this.has(e)||this.set(e,this.createItem(e)),super.get(e)}}const s=(t,r)=>(...i)=>{e.runtime.lastError?t.reject(new Error(e.runtime.lastError.message)):r.singleCallbackArg||i.length<=1&&!1!==r.singleCallbackArg?t.resolve(i[0]):t.resolve(i)},a=e=>1==e?"argument":"arguments",n=(e,t,r)=>new Proxy(t,{apply:(t,i,s)=>r.call(i,e,...s)});let o=Function.call.bind(Object.prototype.hasOwnProperty);const g=(e,t={},r={})=>{let i=Object.create(null),c={has:(t,r)=>r in e||r in i,get(c,l,h){if(l in i)return i[l];if(!(l in e))return;let d=e[l];if("function"==typeof d)if("function"==typeof t[l])d=n(e,e[l],t[l]);else if(o(r,l)){let t=((e,t)=>function(r,...i){if(i.length<t.minArgs)throw new Error(`Expected at least ${t.minArgs} ${a(t.minArgs)} for ${e}(), got ${i.length}`);if(i.length>t.maxArgs)throw new Error(`Expected at most ${t.maxArgs} ${a(t.maxArgs)} for ${e}(), got ${i.length}`);return new Promise(((a,n)=>{if(t.fallbackToNoCallback)try{r[e](...i,s({resolve:a,reject:n},t))}catch(s){console.warn(`${e} API method doesn't seem to support the callback parameter, falling back to call it without a callback: `,s),r[e](...i),t.fallbackToNoCallback=!1,t.noCallback=!0,a()}else t.noCallback?(r[e](...i),a()):r[e](...i,s({resolve:a,reject:n},t))}))})(l,r[l]);d=n(e,e[l],t)}else d=d.bind(e);else if("object"==typeof d&&null!==d&&(o(t,l)||o(r,l)))d=g(d,t[l],r[l]);else{if(!o(r,"*"))return Object.defineProperty(i,l,{configurable:!0,enumerable:!0,get:()=>e[l],set(t){e[l]=t}}),d;d=g(d,t[l],r["*"])}return i[l]=d,d},set:(t,r,s,a)=>(r in i?i[r]=s:e[r]=s,!0),defineProperty:(e,t,r)=>Reflect.defineProperty(i,t,r),deleteProperty:(e,t)=>Reflect.deleteProperty(i,t)},l=Object.create(e);return new Proxy(l,c)},c=e=>({addListener(t,r,...i){t.addListener(e.get(r),...i)},hasListener:(t,r)=>t.hasListener(e.get(r)),removeListener(t,r){t.removeListener(e.get(r))}}),l=new i((e=>"function"!=typeof e?e:function(t){const r=g(t,{},{getContent:{minArgs:0,maxArgs:0}});e(r)})),h=new i((e=>"function"!=typeof e?e:function(t,r,i){let s,a,n=!1,o=new Promise((e=>{s=function(t){n=!0,e(t)}}));try{a=e(t,r,s)}catch(e){a=Promise.reject(e)}const g=!0!==a&&((c=a)&&"object"==typeof c&&"function"==typeof c.then);var c;if(!0!==a&&!g&&!n)return!1;return(g?a:o).then((e=>{i(e)}),(e=>{let t;t=e&&(e instanceof Error||"string"==typeof e.message)?e.message:"An unexpected error occurred",i({__mozWebExtensionPolyfillReject__:!0,message:t})})).catch((e=>{console.error("Failed to send onMessage rejected reply",e)})),!0})),d=({reject:r,resolve:i},s)=>{e.runtime.lastError?e.runtime.lastError.message===t?i():r(new Error(e.runtime.lastError.message)):s&&s.__mozWebExtensionPolyfillReject__?r(new Error(s.message)):i(s)},m=(e,t,r,...i)=>{if(i.length<t.minArgs)throw new Error(`Expected at least ${t.minArgs} ${a(t.minArgs)} for ${e}(), got ${i.length}`);if(i.length>t.maxArgs)throw new Error(`Expected at most ${t.maxArgs} ${a(t.maxArgs)} for ${e}(), got ${i.length}`);return new Promise(((e,t)=>{const s=d.bind(null,{resolve:e,reject:t});i.push(s),r.sendMessage(...i)}))},u={devtools:{network:{onRequestFinished:c(l)}},runtime:{onMessage:c(h),onMessageExternal:c(h),sendMessage:m.bind(null,"sendMessage",{minArgs:1,maxArgs:3})},tabs:{sendMessage:m.bind(null,"sendMessage",{minArgs:2,maxArgs:3})}},f={clear:{minArgs:1,maxArgs:1},get:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}};return r.privacy={network:{"*":f},services:{"*":f},websites:{"*":f}},g(e,u,r)};e.exports=r(chrome)}}(h);var d=l(h.exports);class m{constructor(e,t,r,i,s){this.wfSecret=e,this.wfAppId=t,this.deviceUrl=r,this.dataUrl=i,this._logger=s}async getWildfireDevice(){return(await fetch(this.deviceUrl,{method:"POST",body:JSON.stringify({}),headers:{"Content-Type":"application/json","WF-Secret":this.wfSecret,"WF-App-ID":this.wfAppId}})).json()}async getActiveDomains(){return(await fetch(`${this.dataUrl}/${this.wfAppId}/active-domain/1`)).json()}async getStanddownPolicy(){const e=`${this.dataUrl}/${this.wfAppId}/stand-down-policy/1`;try{const t=await fetch(e);if(t.ok)return t.json();throw new Error(`Failed to fetch standdown policy. Status ${t.status}`)}catch(t){return this._logger.error("Failed to fetch the standdown policy",{err:t,url:e}),{Domains:[],LostAttribution:[],Params:[],Serp:[]}}}async getMerchantRates(){const e=`${this.dataUrl}/${this.wfAppId}/merchant-rate/1`;try{const t=await fetch(e);if(this._logger.debug("Merchant rates response",{response:t}),!t.ok)throw new Error(`Failed to fetch merchant rates. Status ${t.status}`);const r=t.json();return this._logger.debug("Merchant rates",{rates:r}),r}catch(t){return this._logger.error("Failed to fetch the merchant rates",{err:t,url:e}),[]}}}const u="GIVE_FREELY_",f={async get(e){const t=`${u}${e}`;return(await d.storage.local.get(t))[t]||null},async set(e,t){const r=`${u}${e}`;await d.storage.local.set({[r]:t})},async remove(e){const t=Array.isArray(e)?e.map((e=>`${u}${e}`)):`${u}${e}`;await d.storage.local.remove(t)}},A="wfDevice",y="wfDomains",p="wfStanddownPolicy",w="wfStanddownHistory",v="wfLastCacheRefresh",x="wfMerchantRates";async function E(e,t,r){try{const i=(await b())?.[t];if(i&&i>Date.now())return e.info("Standing down because current affiliate stand down session has not expired"),S(t),!0;await this.refreshCache();const s=await this.getStanddownPolicy();if(_(s,new URL(r).search))return e.info("Standing down because url has affiliate params"),S(t),!0;e.info("No need to stand down.")}catch(t){e.error("Exception produced when trying to determine if it should stand down",t)}return!1}function C(e,t){if(!this.standDownPolicy)return this.logger.warn("Couldnt check affilliation. No stand down policy found"),this.getStanddownPolicy().then((e=>{this.standDownPolicy=e})),!1;const r=e.some((e=>I(this.standDownPolicy,e)))||_(this.standDownPolicy,t);return this.logger.info("Affilliation result:",r),r}const b=async()=>f.get(w),S=async(e,t=1)=>{const r=await b()??{};r[e]=Date.now()+60*t*60*1e3,await f.set(w,r)},I=(e,t)=>{if(!t)return!1;const{Domains:r}=e;return r.some((e=>t.includes(e)))},_=(e,t)=>{if(!t)return!1;const{Params:r}=e;return r.some((e=>t.toLowerCase().includes(`?${e.toLowerCase()}`)))||r.some((e=>t.toLowerCase().includes(`&${e.toLowerCase()}`)))};class T{constructor(e,t,r,i){this.userId=e,this.charityEin=t,this.charityThirdPartyIdentifier=r,this.partnerId=i}toString(){return`userId=${this.encodeUserId(this.userId)},charityEin=${this.charityEin},charityThirdPartyIdentifier=${this.charityThirdPartyIdentifier},partnerId=${this.partnerId}`}encodeUserId(e){return btoa(e).replace(/_/g,"/").replace(/-/g,"+")}}class U{constructor(e,t,r,i=21600){if(this.standDownPolicy=null,this.shouldStandDown=E.bind(this),this.hasAffilliation=C.bind(this),!e||!r)throw new Error("WildfireService requires wildfireClient and vanityBaseUrl");this.wildfireClient=e,this.vanityBaseUrl=r.replace(/\/$/,""),this.refreshInterval=i,this.logger=t}async getActiveDomains(){try{await this.refreshCache();const e=await f.get(y);if(!e)throw Error("Failed to retrieve active domains");return e}catch(e){throw this.logger.error("Failed to get active domains:",e),new Error("Failed to retrieve active domains")}}async getMerchantRates(e){try{await this.refreshCache();const t=await f.get(x);if(!t)throw new Error("Failed to retrieve merchant rates");return t[e]}catch(e){throw this.logger.error("Failed to retrieve merchant rates:",e),new Error("Failed to retrieve merchant rates")}}async generateAffiliateUrl(e,t,r,i){if(!e||!t||!i)throw new Error("Missing required parameters for affiliate URL generation");try{const s=await this.getDevice(),a=await this.generateTrackingCode(r,i,s),n=encodeURIComponent(t),o=encodeURIComponent(a);return`${this.vanityBaseUrl}/e?d=${s.DeviceID}&c=${e.ID}&tc=${o}&url=${n}`}catch(e){throw this.logger.error("Failed to generate affiliate URL:",e),new Error("Failed to generate affiliate URL")}}async clearCache(){try{await Promise.all([f.remove(A),f.remove(y),f.remove(v),f.remove(p),f.remove(x)])}catch(e){throw this.logger.error("Failed to clear cache:",e),new Error("Failed to clear cache")}}async refreshCache(e=!1){try{const t=await f.get(v),r=Date.now();(e||!t||r-t>1e3*this.refreshInterval)&&(await Promise.all([this.syncActiveDomains(),this.syncStanddownPolicy(),this.syncMerchantRates()]),await f.set(v,r)),this.standDownPolicy=await this.getStanddownPolicy()}catch(e){throw this.logger.error("Failed to refresh cache:",e),new Error("Failed to refresh cache")}}async getStanddownPolicy(){return await f.get(p)??(()=>{throw new Error("Failed to retrieve standdown policy. Storage value was null")})()}getRetryOptions(){return{delayFn:e=>1e3*e,maxAttempts:3}}async getDevice(){try{const e=await f.get(A);return e?.DeviceID?e:g((()=>this.fetchAndStoreDevice()),this.getRetryOptions())}catch(e){throw this.logger.error("Failed to get device:",e),new Error("Failed to retrieve device information")}}async fetchAndStoreDevice(){const e=await this.wildfireClient.getWildfireDevice();if(!e?.DeviceID)throw new Error("Failed to retrieve device information");return await f.set(A,e),e}async generateTrackingCode(e,t,r){if(!(e?.id&&e?.charity&&e?.charity.ein&&e?.charity.thirdPartyId)){const e=`notregistered-${r.DeviceID}`;return this.logger.info("User not provided. Using GF's default bucket instead.",e),e}return new T(e.id,e.charity.ein,e.charity.thirdPartyId,t).toString()}async syncActiveDomains(){const e=await g((()=>this.wildfireClient.getActiveDomains()),this.getRetryOptions());return await f.set(y,e),e}async syncStanddownPolicy(){const e=await g((()=>this.wildfireClient.getStanddownPolicy()),this.getRetryOptions());return this.logger.info("Updated standdown policy",e),await f.set(p,e),e}async syncMerchantRates(){const e=await g((()=>this.wildfireClient.getMerchantRates()),this.getRetryOptions());return this.logger.debug("Merchant rates",e),await f.set(x,e),e}}class P{constructor(e){this.partnerApiKey=e,this.partnerConfig=null,this.globalConfig=null,this.lastConfigRefresh=0,this.CONFIG_REFRESH_INTERVAL=3e5,this.PARTNER_CONFIG_STORAGE_KEY="gf_partner_config",this.GLOBAL_CONFIG_STORAGE_KEY="gf_global_config"}getRetryOptions(){return{delayFn:e=>1e3*e,maxAttempts:3}}async fetchAndUpdatePartnerConfig(){const e=await g((async()=>{const e=await fetch(`https://cdn.givefreely.com/adunit/config/${this.partnerApiKey}.json`,{cache:"no-store"});if(!e.ok)throw new Error(`Failed to fetch partner config: ${e.statusText}`);return e.json()}),this.getRetryOptions()),t={config:e,lastRefresh:Date.now()};return await f.set(this.PARTNER_CONFIG_STORAGE_KEY,t),e}async fetchAndUpdateGlobalConfig(){const e=await g((async()=>{const e=await fetch("https://cdn.givefreely.com/adunit/config/global.json",{cache:"no-store"});if(!e.ok)throw new Error(`Failed to fetch global config: ${e.statusText}`);return e.json()}),this.getRetryOptions()),t={config:e,lastRefresh:Date.now()};return await f.set(this.GLOBAL_CONFIG_STORAGE_KEY,t),e}async getConfig(){const e=Date.now(),t=await f.get(this.PARTNER_CONFIG_STORAGE_KEY),r=await f.get(this.GLOBAL_CONFIG_STORAGE_KEY);if(this.partnerConfig=t?.config||null,this.globalConfig=r?.config||null,this.lastConfigRefresh=Math.max(t?.lastRefresh||0,r?.lastRefresh||0),!this.partnerConfig||!this.globalConfig||e-this.lastConfigRefresh>this.CONFIG_REFRESH_INTERVAL)try{const[t,r]=await Promise.all([this.fetchAndUpdatePartnerConfig(),this.fetchAndUpdateGlobalConfig()]);this.partnerConfig=t,this.globalConfig=r,this.lastConfigRefresh=e}catch(e){if(!this.partnerConfig||!this.globalConfig)throw e}const i=this.mergeConfigs(this.globalConfig,this.partnerConfig);return((e,t,r)=>{const i=[...t.merchantExclusions??[],...r.merchantExclusions??[]];e.merchantExclusions=i})(i,this.globalConfig,this.partnerConfig),i}async clearCache(){await Promise.all([f.remove(this.PARTNER_CONFIG_STORAGE_KEY),f.remove(this.GLOBAL_CONFIG_STORAGE_KEY)]),this.partnerConfig=null,this.globalConfig=null,this.lastConfigRefresh=0}mergeConfigs(e,t){if(!t)return e;const r={...e};return Object.keys(t).forEach((i=>{if(!Object.prototype.hasOwnProperty.call(t,i))return;const s=e[i],a=t[i];Array.isArray(a)&&Array.isArray(s)?r[i]=a:r[i]=null===a||"object"!=typeof a||"object"!=typeof s?void 0!==a?a:s:this.mergeConfigs(s,a)})),r}getStoredState(e){return e?{config:e.config,lastRefresh:e.lastRefresh}:{config:null,lastRefresh:0}}}const D="TRACK_EVENT",O="GF_HIDE_POPUP",R="GF_GET_POPUP_CONFIG",k="GF_IS_ACTIVE_DOMAIN",F="GF_SHOULD_STAND_DOWN",L="GF_ACTIVATE_OFFER",N="GF_STORE_SHOPIFY_SHOP_ID",$="GF_GET_DOMAIN_BY_SHOP_ID",G="shopify_shop_ids",M="gf_last_health_check";class K{static register(e,t){this.strategies.set(e,t)}static get(e){return this.strategies.get(e)}static exists(e){return!!function(e){return"object"==typeof e&&null!==e&&"type"in e&&"payload"in e&&"string"==typeof e.type}(e)&&this.strategies.has(e.type)}}K.strategies=new Map;class H{async handle(e,t){const{hostname:r}=e.payload,{giveFreelyService:i}=t,s=i.getLogger(),a=await i.getConfig().merchantExclusions,n=await i.getActiveDomains();s.debug("Checking hostname",{hostname:r});try{if(i.healthCheck(),a.some((e=>e.disableDomain&&(r===e.domain||r.endsWith(`.${e.domain}`)))))return s.debug("Hostname is in merchant exclusions",{hostname:r}),{type:k,payload:{isActive:!1,domain:void 0,rates:[]}};if(!n||0===n.length)throw new Error("No active domains");s.debug("Retrieved active domains",{count:n.length});const e=n.find((e=>r===e.Domain||r.endsWith(`.${e.Domain}`)));s.debug("Domain check result",{hostname:r,isActive:!!e});let t=[];if(e?.ID){const r=await i.getMerchantRates(e.Merchant.ID);r&&(s.debug("Retrieved merchant rates",{merchantRates:r}),t=r)}return{type:k,payload:{isActive:!!e,domain:e,rates:t}}}catch(e){return s.error("Error checking domain",{hostname:r,error:e}),{type:k,payload:{isActive:!1,domain:void 0,rates:[]}}}}}class j{async handle(e,t){const{giveFreelyService:r}=t,i=r.getLogger(),s=await r.getCachedConfig();try{return i.debug("Fetching popup config"),{type:R,payload:{config:s}}}catch(e){return i.error("Error fetching popup config",{error:e}),{type:R,payload:{config:null}}}}}class z{async handle(e,t){const{days:r}=e.payload,{giveFreelyService:i}=t,s=i.getLogger();try{const e=new Date;return e.setDate(e.getDate()+r),await f.set("popup_hide",{popupHide:e.getTime()}),s.debug("Popup hidden",{days:r,expiry:e}),{type:O,payload:{ack:!0}}}catch(e){return s.error("Error hiding popup",{days:r,error:e}),{type:O,payload:{ack:!1}}}}}class Y{async handle(e,t){const{activeDomain:r,url:i}=e.payload,{giveFreelyService:s}=t,a=s.getLogger();a.debug("Checking if we should stand down",{activeDomain:r});try{const e=await s.shouldStandDown(r,i);return{type:F,payload:{result:e}}}catch(e){return a.error("Error checking if we should stand down. Returning false",{activeDomain:r,error:e}),{type:F,payload:{result:!1}}}}}const B="version",W=()=>String("1.1.2");class q{constructor(e,t,r=60){this._config=null,this.track=async(e,t,r)=>{null===this._config&&(this._config=await new P(this._partnerApiKey).getConfig());const i={partner:`adUnit_${this._partnerApiKey}`,eventType:e,eventData:{userId:this._userService?.user?.id,libVersion:W(),wfDeviceId:t,...r}};try{const e=JSON.stringify(i);return await this.shouldBroadcastEvent(e)?(await fetch(this._config.eventsUrl,{headers:{"Content-Type":"application/json"},method:"POST",body:e,cache:"no-store"}),await this.pushEventToHistory(e),!0):(this._logger.info("This event has already been broadcasted during the past hour",i),!1)}catch(e){return this._logger.info("Something unexpected happened when dispatching a gf event",{error:e,event:i}),!1}},this.getEventsHistory=async()=>{const{GF_AD_EVENT_HISTORY:e}=await c.storage.local.get({GF_AD_EVENT_HISTORY:""}),t=(e=>{const t=new Date;return t.setTime(t.getTime()+60*e*1e3),t.getTime()})(-this._debounceWindowInMinutes),r=(e||[]).filter((e=>e.createdAt>t));return await c.storage.local.set({GF_AD_EVENT_HISTORY:r}),r},this.shouldBroadcastEvent=async e=>!(await this.getEventsHistory()).some((t=>t.payload===e)),this.pushEventToHistory=async e=>{const t=await this.getEventsHistory();t.push({createdAt:Date.now(),payload:e}),await c.storage.local.set({GF_AD_EVENT_HISTORY:t})},this._partnerApiKey=e,this._userService=t,this._debounceWindowInMinutes=r,this._logger=a.getInstance({title:"Analytics"})}}q.trackEvent=async(e,t)=>{await async function(e){return new Promise(((t,r)=>{try{const i=i=>chrome.runtime.lastError?r({error:chrome.runtime.lastError,message:e}):t(i);c.runtime.sendMessage(e,i)}catch(e){r(e)}}))}({type:D,payload:{eventType:e,eventData:t}})};class V{async handle(e,t){const{eventType:r,eventData:i}=e.payload,{giveFreelyService:s}=t,a=s.getLogger();a.debug("Broadcasting event",{eventType:r,eventData:i});try{const e=await s.trackEvent(r,i);return{type:D,payload:{result:e}}}catch(e){return a.error("Error broadcasting event. Returning false",{eventType:r,eventData:i,error:e}),{type:D,payload:{result:!1}}}}}class X{async handle(e,r){const{activeDomain:i,originalUrl:s,selectedCharity:a}=e.payload,{giveFreelyService:n}=r,o=n.getLogger();o.debug("Activating offer",{activeDomain:i,selectedCharity:a});try{if(!a?.ein||!a?.thirdPartyId)throw new Error("Missing charity information");const e=await n.initializeWfDeviceId();if(!e)throw new Error("Failed to initialize Wildfire device ID");const t=await n.upsertUser(a,e);o.debug("Upserted user",{currentUser:t}),t||o.info("Failed to create user. Using default bucket's user");const r=await n.generateAffiliateUrl(i,s,t);return o.debug("Generated affiliate URL",{affiliateUrl:r}),(async e=>{const t=c.tabs.getCurrent(),r=await t,i=await c.tabs.create({url:e,openerTabId:r?.id,active:!1,pinned:!0});await(async e=>new Promise((t=>{c.tabs.onUpdated.addListener((async function r(i,s){if(i===e&&"complete"===s.status){if(c.tabs.onUpdated.removeListener(r),!(await c.tabs.get(e)).url)return void t(!1);t(!0)}}))})))(i.id)&&setTimeout((()=>{i?.id&&c.tabs.remove(i.id)}),3e4)})(r),await n.updateStanddownHistory(i.Domain),o.debug("Activated offer",{activeDomain:i,selectedCharity:a}),{type:L,payload:{response:!0}}}catch(e){return o.error("Error activating offer",{activeDomain:i,error:e}),n.trackEvent(t.checkoutPopupFailedActivation,{activeDomain:i,error:e}),{type:L,payload:{response:!1}}}}}var Z;!function(e){e[e.Pending=0]="Pending",e[e.Ready=1]="Ready",e[e.Received=2]="Received",e[e.Donated=3]="Donated",e[e.Disqualified=4]="Disqualified"}(Z||(Z={}));class J{constructor(e,t,r){this.adUnitId=e,this.config=t,this.logger=r}async getAnonymousUserCommissions(e,t){try{const r=new URLSearchParams(t).toString(),i=await fetch(`${this.config.apiConfig.baseUri}/${this.config.apiConfig.getAnonymousUserComissionsPath}?${r}`,{method:"GET",headers:{"Content-Type":"application/json","X-AnonymousUserToken":e}});if(!i.ok)throw new Error("Failed to fetch commissions");return await i.json()}catch(e){throw this.logger.error("[GiveFreelyApiClient] Error fetching anonymous user's commissions:",{error:e}),e}}async createOrUpdateAnonymousUser(e,t){try{const r=await fetch(`${this.config.apiConfig.baseUri}/${this.config.apiConfig.createAnonymousUserPath}?adUnitId=${this.adUnitId}`,{method:"PUT",headers:{"Content-Type":"application/json","X-GF-Platform":"adUnitLibrary","X-AnonymousUserToken":t??""},body:JSON.stringify(e)});if(!r.ok)throw new Error("Failed to create/update anonymous user");this.logger.info("[GiveFreelyApiClient] anonymous user created/updated succesfuly.");const i=await r.json(),s=r.headers.get("X-AnonymousUserToken");return{resultUser:{...i,charity:{ein:i.selectedCharity,thirdPartyId:i.selectedCharityThirdPartyIdentifier,name:void 0}},resultToken:s}}catch(e){throw this.logger.error("[GiveFreelyApiClient] Error creating anonymous user:",{error:e}),e}}}class Q{constructor(e,t){this.ANONYMOUS_USER_STORAGE_KEY="gf_anonymous_user_info",this.ANONYMOUS_ENCRIPTED_TOKEN_STORAGE_KEY="gf_anonymous_encrypted_token",this._giveFreelyApiClient=e,this._logger=t,this.user=null}async resetUser(){await f.remove(this.ANONYMOUS_ENCRIPTED_TOKEN_STORAGE_KEY),await f.remove(this.ANONYMOUS_USER_STORAGE_KEY)}async upsertUser(e,t){let r=await this.fetchUser();try{const i={selectedCharity:e?.ein,selectedCharityThirdPartyIdentifier:e?.thirdPartyId,deviceId:t};if(!await this.shouldCreateOrUpdateUser(r,i))return r;const s=await f.get(this.ANONYMOUS_ENCRIPTED_TOKEN_STORAGE_KEY),{resultUser:a,resultToken:n}=await this._giveFreelyApiClient.createOrUpdateAnonymousUser(i,s);n&&await f.set(this.ANONYMOUS_ENCRIPTED_TOKEN_STORAGE_KEY,n),a&&(await f.set(this.ANONYMOUS_USER_STORAGE_KEY,a),r=a)}catch(e){this._logger.error("Error creating/updating anonymous user:",e)}return this.user=r,r}async fetchUser(){const e=await f.get(this.ANONYMOUS_USER_STORAGE_KEY);return this.user=e,e}async shouldCreateOrUpdateUser(e,t){return!e||e.charity?.ein!==t.selectedCharity||e.charity?.thirdPartyId!==t.selectedCharityThirdPartyIdentifier||e.deviceId!==t.deviceId}}const ee=e=>{try{return new URL(e),!0}catch{return!1}},te=new Set,re=new Set,ie={urls:["<all_urls>"],types:["main_frame"]};class se{async handle(e,t){const r=e.payload,{giveFreelyService:i}=t,s=i.getLogger();try{const e=G,t=await f.get(e)||[],i=t.findIndex((e=>e.shopId===r.shopId));return i>=0?t[i]=r:t.push(r),await f.set(e,t),s.debug("Stored Shopify shop ID",{shopInfo:r}),{type:N,payload:{success:!0}}}catch(e){return s.error("Error storing Shopify shop ID",{shopInfo:r,error:e}),{type:N,payload:{success:!1}}}}}class ae{async handle(e,t){const{shopId:r}=e.payload,{giveFreelyService:i}=t,s=i.getLogger();try{const e=G,t=(await f.get(e)||[]).find((e=>e.shopId===r));return t?(s.debug("Found domain for shop ID",{shopId:r,domain:t.domain}),{type:$,payload:{found:!0,domain:t.domain,shopInfo:t}}):(s.debug("No domain found for shop ID",{shopId:r}),{type:$,payload:{found:!1}})}catch(e){return s.error("Error getting domain for shop ID",{shopId:r,error:e}),{type:$,payload:{found:!1}}}}}class ne{constructor(e,t){this.lastCheck=null,this.trackEvent=e,this.logger=t}async initialize(){const e=await f.get(M);e&&(this.lastCheck=e)}getToday(){return(new Date).toISOString().split("T")[0]}async check(){try{const e=this.lastCheck??0,r=this.getToday();(e?new Date(e).toISOString().split("T")[0]:null)!==r&&(await this.trackEvent(t.checkoutPopupHealthCheck,{language:chrome.i18n.getUILanguage()}),this.lastCheck=Date.now(),await f.set(M,this.lastCheck))}catch(e){this.logger.error("Health check failed",{error:e instanceof Error?e.message:"Unknown error"})}}}class oe extends Error{constructor(e="Service not initialized"){super(e),this.name="UninitializedServiceError"}}class GiveFreelyService{constructor(e){this.partnerApiKey=e,this.dependencies={configService:new P(e)},this.logger=a.getInstance(),this.logger.configure({enabled:!0,title:"Background - GiveFreelyService"}),this.state={initialized:!1,wildfireService:null,partnerFilter:null,config:null,wfDeviceId:null,analytics:null,giveFreelyUserService:null,partnerApiKey:e,healthCheck:null}}assertInitialized(){if(!this.state.initialized)throw new oe}async initialize(e=async()=>(this.logger.debug("Using default partnerFilter."),!0)){if(!this.state.initialized)try{const t=await this.dependencies.configService.getConfig();this.logger.configure({minLevel:t.backgroundMinLogLevel??"error"}),this.logger.info("Initializing GiveFreelyService",{partnerApiKey:this.partnerApiKey});const r=this.createWildfireService(t),s=await(async(e=!0)=>{const t=await f.get(B),r=await W(),i=t!==r;var s;return e&&i&&await(s=r,f.set(B,s)),i})();s&&this.logger.info("New version detected."),this.state.healthCheck=new ne(this.trackEvent.bind(this),this.logger),await this.state.healthCheck.initialize(),await r.refreshCache(s);const n=new J(this.partnerApiKey,t,this.logger);this.state.giveFreelyUserService=new Q(n,this.logger),await this.state.giveFreelyUserService.fetchUser(),this.state.giveFreelyUserService.user?.id||await this.state.giveFreelyUserService.upsertUser(),this.state.analytics=new q(this.partnerApiKey,this.state.giveFreelyUserService),this.logger.addTransport(new i(this)),K.register(k,new H),K.register(R,new j),K.register(O,new z),K.register(F,new Y),K.register(D,new V),K.register(L,new X),K.register(N,new se),K.register($,new ae),this.state.wildfireService=r,this.state.partnerFilter=e,this.state.config=t,this.state.initialized=!0,d.webRequest&&(d.webRequest.onBeforeRequest.addListener((e=>{const t=e.getLogger();return({requestId:r,url:i,initiator:s})=>{try{if(te.has(r)||re.has(r)||!ee(i))return;const{hostname:a,search:n}=new URL(i);if(a.includes("wild.link"))return t.info("Cashback activation request identified, ignoring affiliate check for request"),void re.add(r);let o;s&&ee(s)&&(o=new URL(s).hostname),e.hasAffiliation([a,o],n)&&(t.info("Affiliation found on url, adding request id to track"),te.add(r))}catch(e){t.error("Error on handleAffiliateOnBeforeRequest",e)}}})(this),ie),d.webRequest.onBeforeRedirect.addListener((e=>{const t=e.getLogger();return async({requestId:r,redirectUrl:i})=>{try{if(!te.has(r))return;t.info("Attempting to find active domain");const{hostname:s}=new URL(i),a=(await e.getActiveDomains()).find((e=>s===e.Domain||s.endsWith(`.${e.Domain}`)));if(!a)return;t.info("Found active domain. Updating standdown state"),await S(a.Domain)}catch(e){t.error("Error on handleAffiliateOnBeforeRedirect",e)}}})(this),ie),d.webRequest.onCompleted.addListener((()=>{const e=this.getLogger();return({requestId:t})=>{try{te.has(t)&&(e.info("Tracking request completed. Request Id:",t),te.delete(t))}catch(t){e.error("Error on handleAffiliateOnCompleted",t)}}})(),ie)),function(e){const t=a.getInstance();c.runtime.onMessage.addListener(((r,i,s)=>{function a(e){s(e)}const n=e(r,0);return n instanceof Promise?(n.then((e=>{e&&a(e)})).catch((e=>{t.warn("Async message callback error:",e)})),!0):n}))}((async(e,t)=>(this.logger.debug("Received message",{type:e.type,payload:e.payload}),this.handleMessage(e)))),this.logger.info("GiveFreelyService initialized successfully")}catch(e){const t=e instanceof Error?e.message:"Unknown error";throw this.logger.error("Failed to initialize GiveFreelyService",{error:t}),new Error(`Failed to initialize GiveFreelyService: ${t}`)}}getConfig(){return this.assertInitialized(),this.state.config}async getCachedConfig(){return this.state.config=await this.dependencies.configService.getConfig(),this.logger.configure({minLevel:this.state.config.backgroundMinLogLevel??"error"}),this.state.config}async getActiveDomains(){return this.assertInitialized(),this.state.wildfireService.getActiveDomains()}async shouldStandDown(e,t){this.assertInitialized();const r=new URL(t);if(!await this.state.partnerFilter(r.hostname))return this.logger.info("Should standdown due to partner filter."),!0;if(this.getConfig().merchantExclusions.some((t=>t.mutePopups&&e===t.domain)))return this.logger.info("Should standdown due to merchant exclusions"),!0;const i=await this.state.wildfireService.shouldStandDown(this.logger,e,t);return i&&this.logger.info("Should standdown due to vendor policy"),i}hasAffiliation(e,t){if(!this.state.wildfireService)return this.logger.warn("Wildfire service not initialized yet"),!1;const r=this.state.wildfireService.hasAffilliation(e,t);return r&&this.logger.info("Affiliation identified"),r}trackEvent(e,t){return this.assertInitialized(),this.state.analytics.track(e,this.state.wfDeviceId?.DeviceID,t)}getLogger(){return this.assertInitialized(),this.logger}async initializeWfDeviceId(){if(this.assertInitialized(),this.state.wfDeviceId=await this.state.wildfireService.getDevice(),!this.state.wfDeviceId)throw new Error("Failed to initialize Wildfire device ID");return this.state.wfDeviceId.DeviceID}getWfDeviceId(){return this.assertInitialized(),this.state.wfDeviceId.DeviceID}upsertUser(e,t){return this.assertInitialized(),this.state.giveFreelyUserService.upsertUser(e,t)}getCurrentUser(){return this.assertInitialized(),this.state.giveFreelyUserService.user}generateAffiliateUrl(e,t,r){return this.assertInitialized(),this.state.wildfireService.generateAffiliateUrl(e,t,r,this.state.partnerApiKey)}updateStanddownHistory(e){return this.assertInitialized(),S(e)}getMerchantRates(e){if(this.assertInitialized(),!this.state.wildfireService)throw new Error("Wildfire service not initialized");return this.state.wildfireService.getMerchantRates(e)}async healthCheck(){this.assertInitialized(),await this.state.healthCheck.check()}async handleMessage(e){this.assertInitialized();const t=K.get(e.type);if(!t)return;const r={giveFreelyService:this};return t.handle(e,r)}createWildfireService(e){this.logger.debug("config",this.state.config);const t=new m(e.wfSecret,e.wfAppId,e.deviceUrl,e.dataUrl,this.logger);return new U(t,this.logger,e.vanityBaseUrl,e.refreshInterval)}async destroy(){this.logger.info("Destroying GiveFreelyService"),await Promise.all([this.dependencies.configService.clearCache(),this.state.wildfireService?.clearCache(),this.state.giveFreelyUserService?.resetUser()]),this.state.initialized=!1,this.state.wildfireService=null,this.state.partnerFilter=null,this.state.analytics=null,this.state.config=null}}const ge=e=>K.exists(e);export{GiveFreelyService,c as browser,ge as isAdUnitMessage};
|
package/package.json
CHANGED
|
@@ -1,146 +1,3 @@
|
|
|
1
|
-
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
2
|
-
|
|
3
|
-
type GfCheckoutPopup = {
|
|
4
|
-
checkoutPopupMasterSwitch: boolean;
|
|
5
|
-
checkoutPopupMainTitle: string;
|
|
6
|
-
checkoutPopupSubTitle: string;
|
|
7
|
-
checkoutPopupCharitySelectString: string;
|
|
8
|
-
checkoutPopupSixthBoxWording: string;
|
|
9
|
-
checkoutPopupSelectCharityButtonBgColor: string;
|
|
10
|
-
checkoutPopupOfferDetailsLabelString: string;
|
|
11
|
-
checkoutPopupWhyAmISeeingThisLabelString: string;
|
|
12
|
-
checkoutPopupMainCloseButtonVisible: boolean;
|
|
13
|
-
checkoutPopupSupressCloseButtonVisible: boolean;
|
|
14
|
-
checkoutPopupCharities: GfCheckoutPopupCharity[];
|
|
15
|
-
checkoutPopupDonationTitle: string;
|
|
16
|
-
checkoutPopupDonationWording: string;
|
|
17
|
-
checkoutPopupSupressTitle: string;
|
|
18
|
-
checkoutPopupNeverSuppressionAllowed: boolean;
|
|
19
|
-
checkoutPopupExternalUrls: GiveFreelyExternalUrlsConfig;
|
|
20
|
-
checkoutPopupMinLogLevel: LogLevel;
|
|
21
|
-
checkoutPopupOnlyShowLanguageMatch?: boolean;
|
|
22
|
-
};
|
|
23
|
-
interface GiveFreelyExternalUrlsConfig {
|
|
24
|
-
giveFreelyTermsAndConditions: string;
|
|
25
|
-
giveFreelyWhyAmISeeingThis: string;
|
|
26
|
-
}
|
|
27
|
-
interface GfCheckoutPopupCharity {
|
|
28
|
-
logo: string;
|
|
29
|
-
title: string;
|
|
30
|
-
ein: string;
|
|
31
|
-
tpi: string;
|
|
32
|
-
}
|
|
33
|
-
interface BaseGiveFreelyConfig {
|
|
34
|
-
deviceUrl: string;
|
|
35
|
-
dataUrl: string;
|
|
36
|
-
vanityBaseUrl: string;
|
|
37
|
-
refreshInterval: number;
|
|
38
|
-
eventsUrl: string;
|
|
39
|
-
apiConfig: ApiGiveFreelyConfig;
|
|
40
|
-
merchantExclusions: MerchantExclusion[];
|
|
41
|
-
merchantPopupRules: MerchantPopupRule[];
|
|
42
|
-
externalUrls: GiveFreelyExternalUrlsConfig;
|
|
43
|
-
backgroundMinLogLevel: LogLevel;
|
|
44
|
-
minVersion: string;
|
|
45
|
-
}
|
|
46
|
-
interface PartnerGiveFreelyConfig extends BaseGiveFreelyConfig, GfCheckoutPopup {
|
|
47
|
-
wfAppId: string;
|
|
48
|
-
wfSecret: string;
|
|
49
|
-
displayName: string;
|
|
50
|
-
}
|
|
51
|
-
type GiveFreelyConfig = PartnerGiveFreelyConfig;
|
|
52
|
-
interface MerchantExclusion {
|
|
53
|
-
domain: string;
|
|
54
|
-
mutePopups: boolean;
|
|
55
|
-
disableDomain: boolean;
|
|
56
|
-
}
|
|
57
|
-
interface MerchantPopupRule {
|
|
58
|
-
name: string;
|
|
59
|
-
domain: string;
|
|
60
|
-
rules: Array<{
|
|
61
|
-
rule: string;
|
|
62
|
-
}>;
|
|
63
|
-
}
|
|
64
|
-
interface ApiGiveFreelyConfig {
|
|
65
|
-
baseUri: string;
|
|
66
|
-
createAnonymousUserPath: string;
|
|
67
|
-
getAnonymousUserComissionsPath: string;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
declare const MESSAGE_TYPES: {
|
|
71
|
-
readonly HIDE_POPUP: "GF_HIDE_POPUP";
|
|
72
|
-
readonly GET_POPUP_CONFIG: "GF_GET_POPUP_CONFIG";
|
|
73
|
-
readonly IS_ACTIVE_DOMAIN: "GF_IS_ACTIVE_DOMAIN";
|
|
74
|
-
readonly SHOULD_STAND_DOWN: "GF_SHOULD_STAND_DOWN";
|
|
75
|
-
readonly ACTIVATE_OFFER: "GF_ACTIVATE_OFFER";
|
|
76
|
-
readonly STORE_SHOPIFY_SHOP_ID: "GF_STORE_SHOPIFY_SHOP_ID";
|
|
77
|
-
readonly GET_DOMAIN_BY_SHOP_ID: "GF_GET_DOMAIN_BY_SHOP_ID";
|
|
78
|
-
readonly DATA_REQUEST: "DATA_REQUEST";
|
|
79
|
-
readonly DATA_RESPONSE: "DATA_RESPONSE";
|
|
80
|
-
readonly ERROR: "ERROR";
|
|
81
|
-
readonly STATE_UPDATE: "STATE_UPDATE";
|
|
82
|
-
readonly STATE_SYNC: "STATE_SYNC";
|
|
83
|
-
readonly TRACK_EVENT: "TRACK_EVENT";
|
|
84
|
-
readonly STATE_UPDATE_RESPONSE: "STATE_UPDATE_RESPONSE";
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
interface WildfireActiveDomain {
|
|
88
|
-
ID: number;
|
|
89
|
-
Domain: string;
|
|
90
|
-
Merchant: WildfireActiveDomainMerchant;
|
|
91
|
-
}
|
|
92
|
-
declare const PERCENTAGE = "PERCENTAGE";
|
|
93
|
-
declare const FLAT = "FLAT";
|
|
94
|
-
declare type RateKindMap = {
|
|
95
|
-
[PERCENTAGE]: undefined;
|
|
96
|
-
[FLAT]: string;
|
|
97
|
-
};
|
|
98
|
-
interface WildfireActiveDomainMerchant {
|
|
99
|
-
ID: number;
|
|
100
|
-
Name: string;
|
|
101
|
-
MaxRate: WildfireRate<typeof PERCENTAGE> | WildfireRate<typeof FLAT> | null;
|
|
102
|
-
}
|
|
103
|
-
interface WildfireRate<K extends keyof RateKindMap> {
|
|
104
|
-
Kind: K;
|
|
105
|
-
Amount: string;
|
|
106
|
-
Currency?: RateKindMap[K];
|
|
107
|
-
BoostedOffer?: WildfireBoostedOffer;
|
|
108
|
-
}
|
|
109
|
-
interface WildfireBoostedOffer {
|
|
110
|
-
OriginalMaxRate: string;
|
|
111
|
-
Multiplier: string;
|
|
112
|
-
EndDate: string;
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Interface for each merchant rate by product/offer
|
|
116
|
-
*/
|
|
117
|
-
interface WildfireMerchantRateDetail<K extends keyof RateKindMap> extends WildfireRate<K> {
|
|
118
|
-
ID: number;
|
|
119
|
-
Name: string;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
interface GiveFreelyCharity {
|
|
123
|
-
ein: string;
|
|
124
|
-
thirdPartyId: string;
|
|
125
|
-
name?: string;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
declare enum PopupEvent {
|
|
129
|
-
checkoutPopupShown = "CHECKOUT-POPUP-SHOWN",
|
|
130
|
-
checkoutPopupNotShown = "CHECKOUT-POPUP-NOT-SHOWN",
|
|
131
|
-
checkoutPopupSupressed = "CHECKOUT-POPUP-SUPRESSED",
|
|
132
|
-
checkoutPopupDonation = "CHECKOUT-POPUP-DONATION",
|
|
133
|
-
checkoutPopupFailedActivation = "CHECKOUT-POPUP-FAILED-ACTIVATION",
|
|
134
|
-
checkoutPopupPurchaseConfirmed = "CHECKOUT-POPUP-PURCHASE-CONFIRMED",
|
|
135
|
-
checkoutPopupHealthCheck = "CHECKOUT-POPUP-HEALTH-CHECK",
|
|
136
|
-
checkouPopupActivationFailed = "CHECKOUT-POPUP-ACTIVATION-FAILED",// TODO: Is this redundant of "checkoutPopupFailedActivation"?
|
|
137
|
-
checkoutPopupOfferDetailsClicked = "CHECKOUT-POPUP-OFFER-DETAILS-CLICKED"
|
|
138
|
-
}
|
|
139
|
-
declare enum LoggingEvent {
|
|
140
|
-
adUnitLog = "ADUNIT-LOG"
|
|
141
|
-
}
|
|
142
|
-
type EventType = PopupEvent | LoggingEvent;
|
|
143
|
-
|
|
144
1
|
/**
|
|
145
2
|
* Filters out domains where the popup should stand down.
|
|
146
3
|
* Should return false in case the popup should not be shown for a specific domain
|
|
@@ -170,166 +27,9 @@ declare class GiveFreelyService {
|
|
|
170
27
|
destroy(): Promise<void>;
|
|
171
28
|
}
|
|
172
29
|
|
|
173
|
-
/**
|
|
174
|
-
* Context object passed to all message strategies containing shared dependencies.
|
|
175
|
-
* This allows strategies to access only the dependencies they need while maintaining
|
|
176
|
-
* a consistent interface.
|
|
177
|
-
*/
|
|
178
|
-
interface MessageContext {
|
|
179
|
-
giveFreelyService: GiveFreelyService;
|
|
180
|
-
}
|
|
181
|
-
/**
|
|
182
|
-
* Core interface for the Strategy pattern implementation.
|
|
183
|
-
* Each concrete strategy implements this interface to handle a specific message type.
|
|
184
|
-
* The handle method is typed to ensure type safety between request and response payloads.
|
|
185
|
-
*/
|
|
186
|
-
interface MessageStrategy {
|
|
187
|
-
/**
|
|
188
|
-
* Processes an incoming message and returns a response
|
|
189
|
-
* @param message - The incoming message with type and payload
|
|
190
|
-
* @param context - Shared context containing dependencies
|
|
191
|
-
* @returns Promise resolving to a typed response message
|
|
192
|
-
*/
|
|
193
|
-
handle(message: AnyRequestMessage, context: MessageContext): Promise<AnyResponseMessage>;
|
|
194
|
-
}
|
|
195
|
-
/**
|
|
196
|
-
* Registry for message strategies implementing the Strategy pattern.
|
|
197
|
-
* Provides a centralized location to register and retrieve strategies based on message type.
|
|
198
|
-
* This avoids the need for large switch statements and makes adding new message types easier.
|
|
199
|
-
*/
|
|
200
|
-
declare class MessageStrategyRegistry {
|
|
201
|
-
/** Map of message types to their corresponding strategy implementations */
|
|
202
|
-
private static strategies;
|
|
203
|
-
/**
|
|
204
|
-
* Registers a new strategy for a specific message type
|
|
205
|
-
* @param messageType - The type of message this strategy handles
|
|
206
|
-
* @param strategy - The strategy implementation
|
|
207
|
-
*/
|
|
208
|
-
static register(messageType: typeof MESSAGE_TYPES[keyof typeof MESSAGE_TYPES], strategy: MessageStrategy): void;
|
|
209
|
-
/**
|
|
210
|
-
* Retrieves the strategy for a given message type
|
|
211
|
-
* @param messageType - The type of message to handle
|
|
212
|
-
* @returns The corresponding strategy or undefined if not found
|
|
213
|
-
*/
|
|
214
|
-
static get(messageType: string): MessageStrategy | undefined;
|
|
215
|
-
/**
|
|
216
|
-
* Retrieves true if a strategy exists for a given message
|
|
217
|
-
* @param messageType - The type of message to handle
|
|
218
|
-
*/
|
|
219
|
-
static exists(message: unknown): boolean;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
interface ShopifyShopInfo {
|
|
223
|
-
shopId: number;
|
|
224
|
-
domain: WildfireActiveDomain;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Each key in this interface is a unique message type
|
|
229
|
-
* and for each key, we store two objects:
|
|
230
|
-
* - `request`: shape of the data you must send
|
|
231
|
-
* - `response`: shape of the data you'll receive
|
|
232
|
-
* If this were to grow, we can break it down into smaller interfaces
|
|
233
|
-
*/
|
|
234
|
-
interface RequestResponseMap {
|
|
235
|
-
[MESSAGE_TYPES.IS_ACTIVE_DOMAIN]: {
|
|
236
|
-
request: {
|
|
237
|
-
hostname: string;
|
|
238
|
-
};
|
|
239
|
-
response: {
|
|
240
|
-
isActive: boolean;
|
|
241
|
-
domain: WildfireActiveDomain | undefined;
|
|
242
|
-
rates: WildfireMerchantRateDetail<keyof RateKindMap>[];
|
|
243
|
-
};
|
|
244
|
-
};
|
|
245
|
-
[MESSAGE_TYPES.SHOULD_STAND_DOWN]: {
|
|
246
|
-
request: {
|
|
247
|
-
activeDomain: string;
|
|
248
|
-
url: string;
|
|
249
|
-
};
|
|
250
|
-
response: {
|
|
251
|
-
result: boolean;
|
|
252
|
-
};
|
|
253
|
-
};
|
|
254
|
-
[MESSAGE_TYPES.TRACK_EVENT]: {
|
|
255
|
-
request: {
|
|
256
|
-
eventType: EventType;
|
|
257
|
-
eventData?: unknown;
|
|
258
|
-
wfDeviceId?: string;
|
|
259
|
-
};
|
|
260
|
-
response: {
|
|
261
|
-
result: boolean;
|
|
262
|
-
};
|
|
263
|
-
};
|
|
264
|
-
[MESSAGE_TYPES.STATE_UPDATE]: {
|
|
265
|
-
request: {
|
|
266
|
-
path: string[];
|
|
267
|
-
value: unknown;
|
|
268
|
-
source: 'content' | 'background';
|
|
269
|
-
};
|
|
270
|
-
response: {
|
|
271
|
-
ack: boolean;
|
|
272
|
-
};
|
|
273
|
-
};
|
|
274
|
-
[MESSAGE_TYPES.GET_POPUP_CONFIG]: {
|
|
275
|
-
request: {};
|
|
276
|
-
response: {
|
|
277
|
-
config: GiveFreelyConfig | null;
|
|
278
|
-
};
|
|
279
|
-
};
|
|
280
|
-
[MESSAGE_TYPES.HIDE_POPUP]: {
|
|
281
|
-
request: {
|
|
282
|
-
days: number;
|
|
283
|
-
};
|
|
284
|
-
response: {
|
|
285
|
-
ack: boolean;
|
|
286
|
-
};
|
|
287
|
-
};
|
|
288
|
-
[MESSAGE_TYPES.ACTIVATE_OFFER]: {
|
|
289
|
-
request: {
|
|
290
|
-
activeDomain: WildfireActiveDomain;
|
|
291
|
-
originalUrl: string;
|
|
292
|
-
selectedCharity: GiveFreelyCharity;
|
|
293
|
-
};
|
|
294
|
-
response: {
|
|
295
|
-
response: boolean;
|
|
296
|
-
};
|
|
297
|
-
};
|
|
298
|
-
[MESSAGE_TYPES.STORE_SHOPIFY_SHOP_ID]: {
|
|
299
|
-
request: ShopifyShopInfo;
|
|
300
|
-
response: {
|
|
301
|
-
success: boolean;
|
|
302
|
-
};
|
|
303
|
-
};
|
|
304
|
-
[MESSAGE_TYPES.GET_DOMAIN_BY_SHOP_ID]: {
|
|
305
|
-
request: {
|
|
306
|
-
shopId: number;
|
|
307
|
-
};
|
|
308
|
-
response: {
|
|
309
|
-
found: boolean;
|
|
310
|
-
domain?: WildfireActiveDomain;
|
|
311
|
-
shopInfo?: ShopifyShopInfo;
|
|
312
|
-
};
|
|
313
|
-
};
|
|
314
|
-
}
|
|
315
|
-
type RequestMessage<K extends keyof RequestResponseMap> = {
|
|
316
|
-
type: K;
|
|
317
|
-
payload: RequestResponseMap[K]['request'];
|
|
318
|
-
};
|
|
319
|
-
type ResponseMessage<K extends keyof RequestResponseMap> = {
|
|
320
|
-
type: K;
|
|
321
|
-
payload: RequestResponseMap[K]['response'];
|
|
322
|
-
};
|
|
323
|
-
type AnyRequestMessage = {
|
|
324
|
-
[K in keyof RequestResponseMap]: RequestMessage<K>;
|
|
325
|
-
}[keyof RequestResponseMap];
|
|
326
|
-
type AnyResponseMessage = {
|
|
327
|
-
[K in keyof RequestResponseMap]: ResponseMessage<K>;
|
|
328
|
-
}[keyof RequestResponseMap];
|
|
329
|
-
|
|
330
30
|
declare const api: typeof chrome;
|
|
331
31
|
|
|
332
|
-
declare const isAdUnitMessage:
|
|
32
|
+
declare const isAdUnitMessage: (message: unknown) => boolean;
|
|
333
33
|
|
|
334
34
|
/**
|
|
335
35
|
* Initializes the GiveFreely AdUnit UI by creating a
|