├── .env.example ├── .eslintignore ├── .eslintrc.js ├── .github └── ISSUE_TEMPLATE │ ├── 01-bug-report.yaml │ ├── 02-feature-request.yaml │ ├── 03-question.yaml │ └── config.yml ├── .gitignore ├── .husky └── pre-commit ├── .lintstagedrc.mjs ├── .swcrc ├── .twosky.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── SECURITY.md ├── babel.config.js ├── bamboo-specs ├── bamboo.yaml ├── build-beta.yaml ├── build-firefox-beta.yaml ├── build-release.yaml ├── deploy-beta.yaml ├── deploy-firefox-beta.yaml ├── deploy-release.yaml ├── increment.yaml ├── permissions-beta.yaml ├── permissions-firefox-beta.yaml ├── permissions-release.yaml ├── scripts │ └── archive-source.sh └── tests.yaml ├── jest.config.ts ├── package.json ├── postcss.config.js ├── src ├── PERMISSIONS.md ├── _locales │ ├── ar │ │ └── messages.json │ ├── be │ │ └── messages.json │ ├── bg │ │ └── messages.json │ ├── ca │ │ └── messages.json │ ├── cs │ │ └── messages.json │ ├── da │ │ └── messages.json │ ├── de │ │ └── messages.json │ ├── el │ │ └── messages.json │ ├── en │ │ └── messages.json │ ├── es │ │ └── messages.json │ ├── fa │ │ └── messages.json │ ├── fi │ │ └── messages.json │ ├── fr │ │ └── messages.json │ ├── he │ │ └── messages.json │ ├── hr │ │ └── messages.json │ ├── hu │ │ └── messages.json │ ├── hy │ │ └── messages.json │ ├── id │ │ └── messages.json │ ├── it │ │ └── messages.json │ ├── ja │ │ └── messages.json │ ├── ko │ │ └── messages.json │ ├── lt │ │ └── messages.json │ ├── mk │ │ └── messages.json │ ├── ms │ │ └── messages.json │ ├── nb │ │ └── messages.json │ ├── nl │ │ └── messages.json │ ├── pl │ │ └── messages.json │ ├── pt_BR │ │ └── messages.json │ ├── pt_PT │ │ └── messages.json │ ├── ro │ │ └── messages.json │ ├── ru │ │ └── messages.json │ ├── sk │ │ └── messages.json │ ├── sl │ │ └── messages.json │ ├── sr-Latn │ │ └── messages.json │ ├── sv │ │ └── messages.json │ ├── tr │ │ └── messages.json │ ├── uk │ │ └── messages.json │ ├── vi │ │ └── messages.json │ ├── zh_CN │ │ └── messages.json │ └── zh_TW │ │ └── messages.json ├── assets │ ├── README.md │ ├── fonts │ │ └── roboto │ │ │ ├── bold.woff │ │ │ ├── bold.woff2 │ │ │ ├── medium.woff │ │ │ ├── medium.woff2 │ │ │ ├── regular.woff │ │ │ └── regular.woff2 │ ├── images │ │ ├── add-device.svg │ │ ├── all-locations.svg │ │ ├── birthday25.svg │ │ ├── clouds.svg │ │ ├── confirm-email.svg │ │ ├── confused.svg │ │ ├── connect-devices.svg │ │ ├── flags │ │ │ ├── ad.svg │ │ │ ├── ae.svg │ │ │ ├── af.svg │ │ │ ├── ag.svg │ │ │ ├── ai.svg │ │ │ ├── al.svg │ │ │ ├── am.svg │ │ │ ├── an.svg │ │ │ ├── ao.svg │ │ │ ├── ar.svg │ │ │ ├── at.svg │ │ │ ├── au.svg │ │ │ ├── aw.svg │ │ │ ├── ax.svg │ │ │ ├── az.svg │ │ │ ├── ba.svg │ │ │ ├── bb.svg │ │ │ ├── bd.svg │ │ │ ├── be.svg │ │ │ ├── bf.svg │ │ │ ├── bg.svg │ │ │ ├── bh.svg │ │ │ ├── bi.svg │ │ │ ├── bj.svg │ │ │ ├── bm.svg │ │ │ ├── bn.svg │ │ │ ├── bo.svg │ │ │ ├── br.svg │ │ │ ├── bs.svg │ │ │ ├── bt.svg │ │ │ ├── bw.svg │ │ │ ├── by.svg │ │ │ ├── bz.svg │ │ │ ├── ca.svg │ │ │ ├── cd.svg │ │ │ ├── cf.svg │ │ │ ├── cg.svg │ │ │ ├── ch.svg │ │ │ ├── ci.svg │ │ │ ├── cl.svg │ │ │ ├── cm.svg │ │ │ ├── cn.svg │ │ │ ├── co.svg │ │ │ ├── cr.svg │ │ │ ├── cu.svg │ │ │ ├── cv.svg │ │ │ ├── cy.svg │ │ │ ├── cz.svg │ │ │ ├── de.svg │ │ │ ├── dj.svg │ │ │ ├── dk.svg │ │ │ ├── dm.svg │ │ │ ├── do.svg │ │ │ ├── dz.svg │ │ │ ├── ec.svg │ │ │ ├── ee.svg │ │ │ ├── eg.svg │ │ │ ├── er.svg │ │ │ ├── es.svg │ │ │ ├── et.svg │ │ │ ├── fi.svg │ │ │ ├── fj.svg │ │ │ ├── fm.svg │ │ │ ├── fr.svg │ │ │ ├── ga.svg │ │ │ ├── gb.svg │ │ │ ├── gd.svg │ │ │ ├── ge.svg │ │ │ ├── gg.svg │ │ │ ├── gh.svg │ │ │ ├── gi.svg │ │ │ ├── gm.svg │ │ │ ├── gn.svg │ │ │ ├── gq.svg │ │ │ ├── gr.svg │ │ │ ├── gt.svg │ │ │ ├── gw.svg │ │ │ ├── gy.svg │ │ │ ├── hk.svg │ │ │ ├── hn.svg │ │ │ ├── hr.svg │ │ │ ├── ht.svg │ │ │ ├── hu.svg │ │ │ ├── id.svg │ │ │ ├── ie.svg │ │ │ ├── il.svg │ │ │ ├── in.svg │ │ │ ├── iq.svg │ │ │ ├── ir.svg │ │ │ ├── is.svg │ │ │ ├── it.svg │ │ │ ├── je.svg │ │ │ ├── jm.svg │ │ │ ├── jo.svg │ │ │ ├── jp.svg │ │ │ ├── ke.svg │ │ │ ├── kg.svg │ │ │ ├── kh.svg │ │ │ ├── km.svg │ │ │ ├── kn.svg │ │ │ ├── kp.svg │ │ │ ├── kr.svg │ │ │ ├── kw.svg │ │ │ ├── ky.svg │ │ │ ├── kz.svg │ │ │ ├── la.svg │ │ │ ├── lb.svg │ │ │ ├── lc.svg │ │ │ ├── li.svg │ │ │ ├── lk.svg │ │ │ ├── lr.svg │ │ │ ├── ls.svg │ │ │ ├── lt.svg │ │ │ ├── lu.svg │ │ │ ├── lv.svg │ │ │ ├── ly.svg │ │ │ ├── ma.svg │ │ │ ├── mc.svg │ │ │ ├── md.svg │ │ │ ├── me.svg │ │ │ ├── mg.svg │ │ │ ├── mk.svg │ │ │ ├── ml.svg │ │ │ ├── mm.svg │ │ │ ├── mn.svg │ │ │ ├── mo.svg │ │ │ ├── mr.svg │ │ │ ├── ms.svg │ │ │ ├── mt.svg │ │ │ ├── mu.svg │ │ │ ├── mv.svg │ │ │ ├── mw.svg │ │ │ ├── mx.svg │ │ │ ├── my.svg │ │ │ ├── mz.svg │ │ │ ├── na.svg │ │ │ ├── ne.svg │ │ │ ├── ng.svg │ │ │ ├── ni.svg │ │ │ ├── nl.svg │ │ │ ├── no.svg │ │ │ ├── np.svg │ │ │ ├── nz.svg │ │ │ ├── om.svg │ │ │ ├── pa.svg │ │ │ ├── pe.svg │ │ │ ├── pg.svg │ │ │ ├── ph.svg │ │ │ ├── pk.svg │ │ │ ├── pl.svg │ │ │ ├── pr.svg │ │ │ ├── pt.svg │ │ │ ├── pw.svg │ │ │ ├── py.svg │ │ │ ├── qa.svg │ │ │ ├── ro.svg │ │ │ ├── rs.svg │ │ │ ├── ru.svg │ │ │ ├── rw.svg │ │ │ ├── sa.svg │ │ │ ├── sb.svg │ │ │ ├── sc.svg │ │ │ ├── sd.svg │ │ │ ├── se.svg │ │ │ ├── sg.svg │ │ │ ├── sh.svg │ │ │ ├── si.svg │ │ │ ├── sk.svg │ │ │ ├── sl.svg │ │ │ ├── sm.svg │ │ │ ├── sn.svg │ │ │ ├── so.svg │ │ │ ├── sr.svg │ │ │ ├── st.svg │ │ │ ├── sv.svg │ │ │ ├── sy.svg │ │ │ ├── sz.svg │ │ │ ├── tc.svg │ │ │ ├── td.svg │ │ │ ├── tg.svg │ │ │ ├── th.svg │ │ │ ├── tj.svg │ │ │ ├── tl.svg │ │ │ ├── tm.svg │ │ │ ├── tn.svg │ │ │ ├── to.svg │ │ │ ├── tr.svg │ │ │ ├── tt.svg │ │ │ ├── tw.svg │ │ │ ├── tz.svg │ │ │ ├── ua.svg │ │ │ ├── ug.svg │ │ │ ├── us.svg │ │ │ ├── uy.svg │ │ │ ├── uz.svg │ │ │ ├── vc.svg │ │ │ ├── ve.svg │ │ │ ├── vg.svg │ │ │ ├── vn.svg │ │ │ ├── vu.svg │ │ │ ├── ws.svg │ │ │ ├── ye.svg │ │ │ ├── za.svg │ │ │ └── zw.svg │ │ ├── icons │ │ │ ├── birthday25-off-19.png │ │ │ ├── birthday25-off-38.png │ │ │ ├── birthday25-on-19.png │ │ │ ├── birthday25-on-38.png │ │ │ ├── disabled-128.png │ │ │ ├── disabled-16.png │ │ │ ├── disabled-19.png │ │ │ ├── disabled-38.png │ │ │ ├── enabled-128.png │ │ │ ├── enabled-16.png │ │ │ ├── enabled-19.png │ │ │ ├── enabled-38.png │ │ │ ├── favicon.svg │ │ │ ├── traffic-off-128.png │ │ │ ├── traffic-off-19.png │ │ │ └── traffic-off-38.png │ │ ├── lock.svg │ │ ├── logo-vpn-dark.svg │ │ ├── logo-vpn.svg │ │ ├── mobile-edge-promo.png │ │ ├── newsletter.svg │ │ ├── ninja-like.svg │ │ ├── no-search-result.svg │ │ ├── not-found.svg │ │ ├── onboarding-no-logging-policy.svg │ │ ├── onboarding-sites-app-exclusions.svg │ │ ├── onboarding-unique-protocol.svg │ │ ├── preloader.svg │ │ ├── products.svg │ │ ├── rate │ │ │ ├── ninja1.svg │ │ │ ├── ninja2.svg │ │ │ ├── ninja3.svg │ │ │ ├── ninja4.svg │ │ │ └── ninja5.svg │ │ ├── referral.svg │ │ ├── signed-out.svg │ │ ├── space.svg │ │ ├── star-active.svg │ │ ├── star.svg │ │ ├── temp-promo-offer.svg │ │ ├── thankyou-ninja.svg │ │ ├── tired.svg │ │ ├── unlimited-data.svg │ │ ├── unlimited.svg │ │ └── vpn-blocked-error-ninja.svg │ ├── motion │ │ ├── off-dark.webm │ │ ├── off-light.webm │ │ ├── on-dark.webm │ │ ├── on-light.webm │ │ ├── switch-off-dark.webm │ │ ├── switch-off-light.webm │ │ ├── switch-on-dark.webm │ │ └── switch-on-light.webm │ └── prebuild-data │ │ └── exclusion-services.json ├── background │ ├── abTestManager │ │ ├── ABTestManager.ts │ │ ├── constants.ts │ │ └── index.ts │ ├── actions.ts │ ├── api │ │ ├── Api.ts │ │ ├── CustomError.ts │ │ ├── accountApi.ts │ │ ├── apiTypes.ts │ │ ├── authApi.ts │ │ ├── fallbackApi.ts │ │ ├── index.ts │ │ ├── telemetryApi.ts │ │ └── vpnApi.ts │ ├── appStatus │ │ ├── AppStatus.ts │ │ └── index.ts │ ├── auth │ │ ├── auth.ts │ │ ├── index.ts │ │ ├── socialAuthSchema.ts │ │ └── thankYouPageSchema.ts │ ├── authentication │ │ ├── authCache.ts │ │ ├── authService.ts │ │ └── index.ts │ ├── browserAction.ts │ ├── browserActionIcon.ts │ ├── browserApi │ │ ├── index.ts │ │ ├── runtime.ts │ │ ├── storage.ts │ │ └── webrtc.ts │ ├── config.ts │ ├── connectivity │ │ ├── connectivity.proto │ │ ├── connectivityService │ │ │ ├── fsm │ │ │ │ ├── actions.ts │ │ │ │ ├── constants.ts │ │ │ │ ├── delays.ts │ │ │ │ ├── events.ts │ │ │ │ ├── index.ts │ │ │ │ └── machine.ts │ │ │ ├── index.ts │ │ │ └── main.ts │ │ ├── endpointConnectivity │ │ │ ├── EndpointConnectivity.ts │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── pingHelpers.ts │ │ ├── protobufCompiled.js │ │ ├── switcher.ts │ │ └── websocket │ │ │ ├── index.ts │ │ │ └── websocketFactory.ts │ ├── consent.ts │ ├── constants.ts │ ├── contextMenu.ts │ ├── credentials │ │ ├── Credentials.ts │ │ ├── credentialsService.ts │ │ └── index.ts │ ├── dns │ │ ├── Dns.ts │ │ ├── dnsConstants.ts │ │ └── index.ts │ ├── emailConfirmationService │ │ ├── emailConfirmationService.ts │ │ └── index.ts │ ├── endpoints │ │ ├── Endpoint.ts │ │ ├── Endpoints.ts │ │ ├── Location.ts │ │ ├── LocationWithPing.ts │ │ ├── index.ts │ │ └── locationsService.ts │ ├── exclusions │ │ ├── ExclusionDto.ts │ │ ├── ExclusionNode.ts │ │ ├── ExclusionsService.ts │ │ ├── ExclusionsTree.ts │ │ ├── exclusions-helpers.ts │ │ ├── exclusions │ │ │ ├── ExclusionsHandler.ts │ │ │ └── ExclusionsManager.ts │ │ ├── index.ts │ │ └── services │ │ │ ├── Service.ts │ │ │ └── ServicesManager.ts │ ├── flagsStorage.ts │ ├── flagsStorageData.ts │ ├── forwarder │ │ ├── forwarder.ts │ │ └── index.ts │ ├── helpers.ts │ ├── hintPopup.ts │ ├── index.html │ ├── index.ts │ ├── limitedOfferService │ │ ├── index.ts │ │ └── limitedOfferService.ts │ ├── main │ │ ├── index.ts │ │ └── main.ts │ ├── management │ │ ├── Management.ts │ │ └── index.ts │ ├── messaging │ │ ├── index.ts │ │ ├── messaging.ts │ │ └── messagingTypes.ts │ ├── mobileEdgePromoService │ │ ├── index.ts │ │ └── mobileEdgePromoService.ts │ ├── networkConnectionObserver │ │ ├── index.ts │ │ ├── networkConnectionObserverAbstract.ts │ │ ├── networkConnectionObserverMv2.ts │ │ └── networkConnectionObserverMv3.ts │ ├── notifications.ts │ ├── permissionsChecker │ │ ├── PermissionsChecker.ts │ │ ├── index.ts │ │ └── permissionsError.ts │ ├── popupData │ │ ├── PopupData.ts │ │ ├── index.ts │ │ └── popupOpenedCounter.ts │ ├── postinstall.ts │ ├── promoNotifications.ts │ ├── providers │ │ ├── accountProvider.ts │ │ ├── authProvider.ts │ │ ├── telemetryProvider.ts │ │ └── vpnProvider.ts │ ├── proxy │ │ ├── abstractProxyApi.ts │ │ ├── chrome │ │ │ ├── pacGenerator.ts │ │ │ ├── proxyApi.ts │ │ │ ├── proxyAuthTrigger │ │ │ │ ├── abstractProxyAuthTrigger.ts │ │ │ │ ├── index.ts │ │ │ │ ├── mv2 │ │ │ │ │ ├── ProxyAuthTrigger.ts │ │ │ │ │ └── index.ts │ │ │ │ └── mv3 │ │ │ │ │ ├── ProxyAuthTrigger.ts │ │ │ │ │ └── index.ts │ │ │ └── proxySettingsUtils.ts │ │ ├── endpointsTldExclusions.ts │ │ ├── firefox │ │ │ └── proxyApi.ts │ │ ├── index.ts │ │ ├── proxy.ts │ │ └── proxyConsts.ts │ ├── rateModal │ │ ├── RateModal.ts │ │ └── index.ts │ ├── routability │ │ ├── NonRoutableService.ts │ │ ├── constants.ts │ │ ├── nonRoutable.ts │ │ └── utils.ts │ ├── savedLocations │ │ ├── SavedLocations.ts │ │ └── index.ts │ ├── schema │ │ ├── auth │ │ │ ├── authAccessToken.ts │ │ │ ├── authState.ts │ │ │ └── index.ts │ │ ├── connectivity │ │ │ ├── context.ts │ │ │ ├── data.ts │ │ │ ├── index.ts │ │ │ └── state.ts │ │ ├── credentials │ │ │ ├── credentialsState.ts │ │ │ ├── index.ts │ │ │ ├── trackInstallResponse.ts │ │ │ └── vpnTokenData.ts │ │ ├── dns │ │ │ ├── dnsServerData.ts │ │ │ ├── dnsState.ts │ │ │ └── index.ts │ │ ├── endpoints │ │ │ ├── endpointInterface.ts │ │ │ ├── endpointsState.ts │ │ │ ├── index.ts │ │ │ ├── location.ts │ │ │ ├── locationsService.ts │ │ │ └── pingsCache.ts │ │ ├── exclusions │ │ │ ├── exclusions │ │ │ │ ├── exclusion.ts │ │ │ │ ├── exclusionsHandler.ts │ │ │ │ ├── exclusionsManagerState.ts │ │ │ │ └── index.ts │ │ │ ├── exclusionsServices │ │ │ │ ├── index.ts │ │ │ │ ├── service.ts │ │ │ │ └── servicesManagerState.ts │ │ │ ├── exclusionsState.ts │ │ │ └── index.ts │ │ ├── fallbackApi │ │ │ ├── fallbackInfo.ts │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── limitedOffer │ │ │ ├── index.ts │ │ │ └── limitedOfferService.ts │ │ ├── permissionsChecker │ │ │ ├── index.ts │ │ │ └── permissionsCheckerState.ts │ │ ├── popupData │ │ │ ├── index.ts │ │ │ └── popupOpenedCounter.ts │ │ ├── proxy │ │ │ ├── accessCredentials.ts │ │ │ ├── canControlProxy.ts │ │ │ ├── endpointsTldExclusions.ts │ │ │ ├── index.ts │ │ │ ├── proxyConfigInterface.ts │ │ │ └── proxyState.ts │ │ ├── sessionStorage │ │ │ ├── StorageData.ts │ │ │ └── index.ts │ │ └── updateService │ │ │ ├── index.ts │ │ │ └── updateServiceState.ts │ ├── settings │ │ ├── SettingsService.ts │ │ ├── index.ts │ │ └── settings.ts │ ├── stateStorage │ │ ├── helper.ts │ │ ├── index.ts │ │ ├── mv2 │ │ │ ├── index.ts │ │ │ └── stateStorage.ts │ │ ├── mv3 │ │ │ ├── index.ts │ │ │ └── stateStorage.ts │ │ └── stateStorage.abstract.ts │ ├── tabs.ts │ ├── telemetry │ │ ├── Telemetry.ts │ │ ├── index.ts │ │ ├── telemetryEnums.ts │ │ └── telemetryTypes.ts │ ├── timers │ │ ├── AbstractTimers.ts │ │ ├── Mv2Timers.ts │ │ ├── Mv3Timers.ts │ │ ├── alarmApi.ts │ │ └── index.ts │ ├── uninstall.ts │ ├── updateService.ts │ └── windowsApi.ts ├── common │ ├── components │ │ ├── DotsLoader │ │ │ ├── DotsLoader.tsx │ │ │ ├── dots-loader.pcss │ │ │ └── index.ts │ │ ├── SearchHighlighter │ │ │ ├── SearchHighlighter.tsx │ │ │ ├── helpers.ts │ │ │ ├── highlighter.pcss │ │ │ └── index.ts │ │ ├── constants.ts │ │ └── ui │ │ │ ├── useOutsideClick.tsx │ │ │ └── useOutsideFocus.tsx │ ├── constants.ts │ ├── data-processors.ts │ ├── exclusionsConstants.ts │ ├── fetch-config.ts │ ├── forwarderHelpers.ts │ ├── helpers.ts │ ├── i18n.ts │ ├── is-locations-number-acceptable.ts │ ├── log-storage │ │ ├── LogStorageManager.ts │ │ ├── index.ts │ │ ├── log-storage.ts │ │ └── storageProvider │ │ │ ├── LogStorageProvider.ts │ │ │ ├── browserProvider │ │ │ ├── BrowserLogStorageProvider.ts │ │ │ ├── index.ts │ │ │ └── logsSchema.ts │ │ │ └── memoryProvider │ │ │ ├── MemoryLogStorageProvider.ts │ │ │ └── index.ts │ ├── logger.ts │ ├── messenger.ts │ ├── notifier.ts │ ├── permissions.ts │ ├── prefs.ts │ ├── preloadTheme.ts │ ├── reactTranslator.ts │ ├── schema │ │ └── endpoints │ │ │ └── vpnInfo.ts │ ├── styles │ │ └── media.pcss │ ├── telemetry │ │ ├── TelemetryStore.ts │ │ ├── index.ts │ │ └── useTelemetryPageViewEvent.ts │ ├── translator.ts │ ├── useAppearanceTheme.ts │ └── utils │ │ ├── auth.ts │ │ ├── date.ts │ │ ├── error.ts │ │ ├── promo.ts │ │ ├── string.ts │ │ └── url.ts ├── consent │ ├── ConsentPage │ │ ├── ConsentPage.tsx │ │ ├── consent-page.pcss │ │ └── index.ts │ ├── index.html │ └── index.tsx ├── content-scripts │ ├── auth.ts │ └── custom-dns-links.ts ├── custom-protocol-handler │ ├── authFirefox.ts │ └── index.html ├── declaration.d.ts ├── export │ ├── export-logs.ts │ ├── index.html │ └── index.ts ├── offscreen │ ├── index.html │ └── index.ts ├── options │ ├── components │ │ ├── About │ │ │ ├── About.tsx │ │ │ ├── about.pcss │ │ │ └── index.ts │ │ ├── Account │ │ │ ├── Account.tsx │ │ │ ├── Features │ │ │ │ ├── Features.tsx │ │ │ │ ├── features.pcss │ │ │ │ └── index.ts │ │ │ ├── account.pcss │ │ │ └── index.ts │ │ ├── App │ │ │ ├── App.tsx │ │ │ ├── app.pcss │ │ │ └── index.ts │ │ ├── Exclusions │ │ │ ├── Actions │ │ │ │ ├── Actions.tsx │ │ │ │ ├── RemoveAllModal │ │ │ │ │ ├── RemoveAllModal.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── SelectListModal │ │ │ │ │ ├── SelectListModal.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── actions.pcss │ │ │ │ ├── fileHelpers.ts │ │ │ │ └── index.ts │ │ │ ├── ChildrenList │ │ │ │ ├── ChildrenList.tsx │ │ │ │ ├── ResetServiceModal │ │ │ │ │ ├── ResetServiceModal.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── SubdomainModal │ │ │ │ │ ├── SubdomainModal.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── index.ts │ │ │ ├── Exclusion │ │ │ │ ├── Exclusion.tsx │ │ │ │ ├── exclusion.pcss │ │ │ │ └── index.ts │ │ │ ├── Exclusions.tsx │ │ │ ├── ExclusionsModal │ │ │ │ ├── AddExclusionsModal │ │ │ │ │ ├── AddExclusionModal.tsx │ │ │ │ │ ├── ManualMode │ │ │ │ │ │ ├── ManualMode.tsx │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── manual-mode.pcss │ │ │ │ │ ├── ServiceMode │ │ │ │ │ │ ├── ServiceCategory.tsx │ │ │ │ │ │ ├── ServiceMode.tsx │ │ │ │ │ │ ├── ServiceRow.tsx │ │ │ │ │ │ ├── ServicesList.tsx │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── service-mode.pcss │ │ │ │ │ ├── add-exclusion-modal.pcss │ │ │ │ │ └── index.ts │ │ │ │ ├── ConfirmAddModal │ │ │ │ │ ├── ConfirmAddModal.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── index.ts │ │ │ ├── List │ │ │ │ ├── List.tsx │ │ │ │ └── index.ts │ │ │ ├── Loader │ │ │ │ ├── Loader.tsx │ │ │ │ ├── index.ts │ │ │ │ └── loader.pcss │ │ │ ├── ModeSelectorModal │ │ │ │ ├── ModeSelectorModal.tsx │ │ │ │ └── index.ts │ │ │ ├── Search │ │ │ │ ├── ExclusionsSearch │ │ │ │ │ ├── ExclusionsSearch.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── ServicesSearch │ │ │ │ │ ├── ServicesSearch.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── index.ts │ │ │ ├── exclusions.pcss │ │ │ └── index.ts │ │ ├── FreeGbs │ │ │ ├── AddDevice.tsx │ │ │ ├── ConfirmEmail.tsx │ │ │ ├── FreeGbs.tsx │ │ │ ├── InviteFriend.tsx │ │ │ ├── free-gbs.pcss │ │ │ └── index.ts │ │ ├── General │ │ │ ├── ContextMenus.tsx │ │ │ ├── DnsSettings │ │ │ │ ├── DnsSettings.tsx │ │ │ │ ├── DnsSettingsButton.tsx │ │ │ │ ├── DnsSettingsServer.tsx │ │ │ │ ├── DnsSettingsServerModal.tsx │ │ │ │ ├── DnsSettingsServerModalAdd.tsx │ │ │ │ ├── DnsSettingsServerModalEdit.tsx │ │ │ │ ├── dns-settings.pcss │ │ │ │ ├── index.ts │ │ │ │ └── validate.ts │ │ │ ├── General.tsx │ │ │ ├── HelpUsImprove.tsx │ │ │ ├── QuickConnect.tsx │ │ │ ├── Theme.tsx │ │ │ ├── WebRTC.tsx │ │ │ ├── general.pcss │ │ │ └── index.ts │ │ ├── Preloader │ │ │ ├── Preloader.tsx │ │ │ ├── index.ts │ │ │ └── preloader.pcss │ │ ├── Sidebar │ │ │ ├── Rate │ │ │ │ ├── Rate.tsx │ │ │ │ ├── RateStar.tsx │ │ │ │ ├── index.ts │ │ │ │ └── rate.pcss │ │ │ ├── Sidebar.tsx │ │ │ ├── SidebarLink.tsx │ │ │ ├── index.ts │ │ │ └── sidebar.pcss │ │ ├── SignedOut │ │ │ ├── SignedOut.tsx │ │ │ ├── index.ts │ │ │ └── signedout.pcss │ │ ├── Support │ │ │ ├── BugReporter │ │ │ │ ├── BugReporter.tsx │ │ │ │ ├── bug-report.pcss │ │ │ │ ├── index.ts │ │ │ │ └── requestMachine.ts │ │ │ ├── Support.tsx │ │ │ ├── index.ts │ │ │ └── support.pcss │ │ └── ui │ │ │ ├── Button │ │ │ ├── Button.tsx │ │ │ ├── button.pcss │ │ │ └── index.ts │ │ │ ├── Checkbox │ │ │ ├── Checkbox.tsx │ │ │ ├── checkbox.pcss │ │ │ └── index.ts │ │ │ ├── Controls │ │ │ ├── Controls.tsx │ │ │ ├── ControlsSelect.tsx │ │ │ ├── ControlsSwitch.tsx │ │ │ ├── controls.pcss │ │ │ └── index.ts │ │ │ ├── Icon │ │ │ ├── Icon.tsx │ │ │ ├── IconButton.tsx │ │ │ ├── Icons.tsx │ │ │ ├── icons.pcss │ │ │ └── index.ts │ │ │ ├── Input │ │ │ ├── Input.tsx │ │ │ ├── TextArea.tsx │ │ │ ├── index.ts │ │ │ └── input.pcss │ │ │ ├── Modal │ │ │ ├── Modal.tsx │ │ │ ├── index.ts │ │ │ └── modal.pcss │ │ │ ├── Notifications │ │ │ ├── NotificationItem.tsx │ │ │ ├── Notifications.tsx │ │ │ ├── index.ts │ │ │ └── notifications.pcss │ │ │ ├── Radio │ │ │ ├── Radio.tsx │ │ │ ├── index.ts │ │ │ └── radio.pcss │ │ │ ├── ReactPortal.tsx │ │ │ ├── Select │ │ │ ├── Select.tsx │ │ │ ├── index.ts │ │ │ └── select.pcss │ │ │ ├── Switch │ │ │ ├── Switch.tsx │ │ │ ├── index.ts │ │ │ └── switch.pcss │ │ │ └── Title │ │ │ ├── Title.tsx │ │ │ ├── index.ts │ │ │ └── title.pcss │ ├── hooks │ │ ├── useMessageHandler.ts │ │ └── useQueryStringData.ts │ ├── index.html │ ├── index.tsx │ ├── react.d.ts │ ├── stores │ │ ├── AuthStore.ts │ │ ├── ExclusionsStore.ts │ │ ├── GlobalStore.ts │ │ ├── NotificationsStore │ │ │ ├── ErrorNotification.ts │ │ │ ├── Notification.ts │ │ │ ├── NotificationsStore.ts │ │ │ ├── SuccessNotification.ts │ │ │ └── index.ts │ │ ├── RootStore.ts │ │ ├── SettingsStore.ts │ │ ├── UiStore.ts │ │ ├── consts.ts │ │ └── index.ts │ └── styles │ │ ├── common.pcss │ │ ├── fonts.pcss │ │ ├── main.pcss │ │ └── vars.pcss ├── popup │ ├── actions │ │ └── popupActions │ │ │ └── index.ts │ ├── components │ │ ├── App │ │ │ ├── App.tsx │ │ │ ├── AppLoaders.tsx │ │ │ └── index.ts │ │ ├── Authentication │ │ │ ├── Authentication.tsx │ │ │ ├── Authorization │ │ │ │ ├── Authorization.tsx │ │ │ │ ├── authorization.pcss │ │ │ │ └── index.ts │ │ │ ├── BackButton.tsx │ │ │ ├── Checkbox │ │ │ │ ├── Checkbox.tsx │ │ │ │ ├── checkbox.pcss │ │ │ │ └── index.ts │ │ │ ├── ConfirmEmail │ │ │ │ ├── ConfirmEmail.tsx │ │ │ │ └── index.ts │ │ │ ├── EmailAuth │ │ │ │ ├── EmailAuth.tsx │ │ │ │ └── index.ts │ │ │ ├── InputField.tsx │ │ │ ├── Newsletter │ │ │ │ ├── Newsletter.tsx │ │ │ │ ├── index.ts │ │ │ │ └── newsletter.pcss │ │ │ ├── Onboarding │ │ │ │ ├── Onboarding.tsx │ │ │ │ ├── index.ts │ │ │ │ └── onboarding.pcss │ │ │ ├── PasswordField.tsx │ │ │ ├── PolicyAgreement │ │ │ │ ├── PolicyAgreement.tsx │ │ │ │ ├── PolicyAgreementModal.tsx │ │ │ │ ├── index.ts │ │ │ │ └── policy-agreement.pcss │ │ │ ├── RegistrationForm │ │ │ │ ├── RegistrationForm.tsx │ │ │ │ └── index.ts │ │ │ ├── ScreenShot.tsx │ │ │ ├── SignInForm │ │ │ │ ├── SignInForm.tsx │ │ │ │ └── index.ts │ │ │ ├── Submit.tsx │ │ │ ├── TwoFactorForm │ │ │ │ ├── TwoFactorForm.tsx │ │ │ │ └── index.ts │ │ │ ├── UpgradeScreen │ │ │ │ ├── UpgradeScreen.tsx │ │ │ │ ├── index.ts │ │ │ │ └── upgrade-screen.pcss │ │ │ ├── auth.pcss │ │ │ └── index.ts │ │ ├── ConnectionsLimitError │ │ │ ├── ConnectionsLimitError.tsx │ │ │ └── index.ts │ │ ├── ExtraOptions │ │ │ ├── ExtraOptions.tsx │ │ │ ├── Option.tsx │ │ │ ├── extra-options.pcss │ │ │ └── index.ts │ │ ├── GlobalError │ │ │ ├── GlobalError.tsx │ │ │ ├── global-error.pcss │ │ │ └── index.ts │ │ ├── Header │ │ │ ├── Header.tsx │ │ │ ├── header.pcss │ │ │ └── index.ts │ │ ├── HostPermissionsError │ │ │ ├── HostPermissionsError.tsx │ │ │ ├── host-permissions-error.pcss │ │ │ └── index.ts │ │ ├── InfoMessage │ │ │ ├── FeedbackMessage.tsx │ │ │ ├── InfoMessage.tsx │ │ │ ├── TrafficInfo.tsx │ │ │ ├── index.ts │ │ │ └── info-message.pcss │ │ ├── LimitedOfferModal │ │ │ ├── LimitedOfferDetails.tsx │ │ │ ├── LimitedOfferModal.tsx │ │ │ ├── LimitedOfferNotice.tsx │ │ │ ├── index.ts │ │ │ ├── limited-offer-details.pcss │ │ │ └── limited-offer-notice.pcss │ │ ├── Locations │ │ │ ├── FastestSkeleton.tsx │ │ │ ├── Location.tsx │ │ │ ├── Locations.tsx │ │ │ ├── Reload.tsx │ │ │ ├── Search.tsx │ │ │ ├── TabButtons.tsx │ │ │ ├── endpoints.pcss │ │ │ └── index.ts │ │ ├── MobileEdgePromo │ │ │ ├── MobileEdgePromo.tsx │ │ │ ├── MobileEdgePromoBanner.tsx │ │ │ ├── MobileEdgePromoModal.tsx │ │ │ ├── index.ts │ │ │ ├── mobile-edge-promo-banner.pcss │ │ │ └── mobile-edge-promo-modal.pcss │ │ ├── NoLocationsError │ │ │ ├── NoLocationsError.tsx │ │ │ ├── index.ts │ │ │ └── no-locations-error.pcss │ │ ├── Ping │ │ │ ├── Ping.tsx │ │ │ ├── index.ts │ │ │ └── ping.pcss │ │ ├── PingDotsLoader │ │ │ ├── PingDotsLoader.tsx │ │ │ ├── index.ts │ │ │ └── ping-dots-loader.pcss │ │ ├── PromoNotificationModal │ │ │ ├── PromoNotificationModal.tsx │ │ │ ├── index.ts │ │ │ └── promo-notification-modal.pcss │ │ ├── RatePopup │ │ │ ├── RatePopup.tsx │ │ │ ├── index.ts │ │ │ └── rate.pcss │ │ ├── ReviewPopup │ │ │ ├── ConfirmRateModal.tsx │ │ │ ├── RateModal.tsx │ │ │ ├── ReviewPopup.tsx │ │ │ ├── constants.ts │ │ │ ├── index.ts │ │ │ └── rate-modal.pcss │ │ ├── ServerErrorPopup │ │ │ ├── ServerErrorPopup.tsx │ │ │ ├── index.ts │ │ │ └── server-error-popup.pcss │ │ ├── Settings │ │ │ ├── BackgroundAnimation │ │ │ │ ├── BackgroundAnimation.tsx │ │ │ │ ├── animationStateMachine.ts │ │ │ │ └── index.ts │ │ │ ├── CurrentEndpoint │ │ │ │ ├── CurrentEndpoint.tsx │ │ │ │ ├── endpoint.pcss │ │ │ │ └── index.ts │ │ │ ├── ExclusionsScreen │ │ │ │ ├── ExclusionsScreen.tsx │ │ │ │ └── index.ts │ │ │ ├── GlobalControl │ │ │ │ ├── ExcludeSite │ │ │ │ │ ├── ExcludeSite.tsx │ │ │ │ │ ├── exclude-site.pcss │ │ │ │ │ └── index.ts │ │ │ │ ├── GlobalControl.tsx │ │ │ │ ├── HintPopup │ │ │ │ │ ├── HintPopup.tsx │ │ │ │ │ ├── hint-popup.pcss │ │ │ │ │ └── index.ts │ │ │ │ └── index.ts │ │ │ ├── Settings.tsx │ │ │ ├── SiteInfo │ │ │ │ ├── Info.tsx │ │ │ │ ├── SiteInfo.tsx │ │ │ │ ├── index.ts │ │ │ │ └── site-info.pcss │ │ │ ├── Status │ │ │ │ ├── Status.tsx │ │ │ │ ├── index.ts │ │ │ │ └── status.pcss │ │ │ ├── TrafficLimitExceeded │ │ │ │ ├── TrafficLimitExceeded.tsx │ │ │ │ └── index.ts │ │ │ ├── Warning │ │ │ │ ├── Warning.tsx │ │ │ │ ├── index.ts │ │ │ │ └── warning.pcss │ │ │ ├── index.ts │ │ │ └── settings.pcss │ │ ├── SkeletonLoading │ │ │ ├── SkeletonLoading.tsx │ │ │ └── index.ts │ │ ├── VpnBlockedError │ │ │ ├── VpnBlockedDetails.tsx │ │ │ ├── VpnBlockedError.tsx │ │ │ ├── VpnBlockedNotice.tsx │ │ │ ├── index.ts │ │ │ ├── vpn-blocked-details.pcss │ │ │ └── vpn-blocked-notice.pcss │ │ └── ui │ │ │ ├── CloseButton │ │ │ ├── CloseButton.tsx │ │ │ ├── close-button.pcss │ │ │ └── index.ts │ │ │ ├── DotsIndicator │ │ │ ├── DotsIndicator.tsx │ │ │ ├── dots-indicator.pcss │ │ │ └── index.ts │ │ │ ├── Icon.tsx │ │ │ ├── Icons.tsx │ │ │ ├── SkeletonEndpoint │ │ │ ├── SkeletonEndpoint.tsx │ │ │ ├── index.ts │ │ │ └── skeleton-endpoint.pcss │ │ │ ├── SkeletonHeader │ │ │ ├── SkeletonHeader.tsx │ │ │ └── index.ts │ │ │ ├── Slider │ │ │ ├── Slider.tsx │ │ │ ├── index.ts │ │ │ └── slider.pcss │ │ │ └── icon.pcss │ ├── constants.ts │ ├── index.html │ ├── index.tsx │ ├── stores │ │ ├── AuthStore.ts │ │ ├── GlobalStore.ts │ │ ├── RootStore.ts │ │ ├── SettingsStore.ts │ │ ├── UiStore.ts │ │ ├── VpnStore.ts │ │ ├── constants.ts │ │ └── index.ts │ └── styles │ │ ├── animation.pcss │ │ ├── button.pcss │ │ ├── common.pcss │ │ ├── fonts.pcss │ │ ├── form.pcss │ │ ├── main.pcss │ │ ├── modal.pcss │ │ └── vars.pcss └── worker │ └── worker.ts ├── tasks ├── appConfig.ts ├── bundle-runner.ts ├── bundle.ts ├── chrome │ ├── manifest.chrome.ts │ └── webpack.chrome.ts ├── compile-protobuf.sh ├── consts.ts ├── crx.ts ├── edge │ ├── manifest.edge.ts │ └── webpack.edge.ts ├── firefox │ ├── manifest.firefox.ts │ └── webpack.firefox.ts ├── helpers.ts ├── manifest.common.json ├── opera │ ├── manifest.opera.ts │ └── webpack.opera.ts ├── resources.ts ├── size-limit-plugin.ts ├── translations │ ├── README.md │ ├── config.json │ ├── download.js │ ├── helpers.js │ ├── index.js │ ├── locales-constants.js │ ├── unused.js │ ├── upload.js │ └── validate.js ├── webpack.common.ts └── xpi.ts ├── tests ├── __mocks__ │ ├── browserApiMock.ts │ ├── fetchMock.ts │ ├── fileMock.ts │ ├── index.ts │ ├── sessionMock.ts │ └── styleMock.ts ├── __setups__ │ ├── chrome.ts │ └── jest-setup.ts ├── background │ ├── abTestManager │ │ └── ABTestManager.test.ts │ ├── actions.test.ts │ ├── api │ │ ├── api.test.ts │ │ └── fallbackApi.test.ts │ ├── appStatus │ │ └── appStatus.test.ts │ ├── authentication │ │ └── AuthService.test.ts │ ├── credentials │ │ ├── Credentials.test.ts │ │ └── CredentialsService.test.ts │ ├── endpoints │ │ ├── endpoints.test.ts │ │ └── locationsService.test.ts │ ├── exclusions │ │ ├── ExclusionNode.test.ts │ │ ├── ExclusionsService.test.ts │ │ ├── ExclusionsTree.test.ts │ │ ├── exclusions-helpers.test.ts │ │ ├── exclusions.json │ │ ├── exclusions │ │ │ └── ExclusionsHandler.test.ts │ │ ├── services.json │ │ └── services │ │ │ └── ServicesManager.test.ts │ ├── management │ │ └── management.test.ts │ ├── notification.test.ts │ ├── permissionChecker │ │ └── permissionsChecker.test.ts │ ├── providers │ │ ├── authProvider.test.ts │ │ └── telemetryProvider.test.ts │ ├── proxy │ │ └── pacGenerator.test.ts │ ├── rateModal.test.ts │ ├── routability │ │ └── NonRoutableService.test.ts │ ├── savedLocations │ │ └── SavedLocations.test.ts │ ├── settings │ │ └── SettingsService.test.ts │ └── telemetry │ │ └── Telemetry.test.ts ├── certificate-test.pem ├── common │ ├── helpers.test.ts │ ├── log-storage.test.ts │ ├── notifier.test.ts │ ├── prefs.test.ts │ └── utils │ │ ├── date.test.ts │ │ ├── string.test.ts │ │ └── url.test.ts ├── content-scripts │ └── custom-links.test.ts ├── custom-test-env.ts └── options │ ├── components │ └── Exclusions │ │ ├── ExclusionsModal │ │ └── AddExclusionsModal │ │ │ └── ServiceMode │ │ │ └── ServiceRow.test.ts │ │ └── ImportExport │ │ └── fileHelpers.test.ts │ └── hooks │ └── useQueryStringData.test.ts ├── tsconfig.eslint.json ├── tsconfig.json └── yarn.lock /.env.example: -------------------------------------------------------------------------------- 1 | STAGE_ENV=test 2 | VPN_API_URL= 3 | AUTH_API_URL= 4 | FORWARDER_DOMAIN= 5 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | /coverage 4 | /private 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | 'blank_issues_enabled': true 2 | 'contact_links': 3 | - 'about': > 4 | Please check AdGuard VPN Knowledgebase for more useful information about our products 5 | 'name': 'Knowledgebase' 6 | 'url': 'https://adguard-vpn.com/kb/' 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | .DS_Store 4 | .idea/ 5 | /yarn-error.log 6 | /coverage 7 | /private 8 | .env 9 | .eslintcache 10 | tsconfig.eslint.tsbuildinfo 11 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | yarn lint-staged 5 | -------------------------------------------------------------------------------- /.lintstagedrc.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | "*.{ts,tsx,js,jsx}": (filenames) => [ 3 | `eslint --cache ${filenames.join(' ')}`, 4 | ], 5 | }; 6 | -------------------------------------------------------------------------------- /.swcrc: -------------------------------------------------------------------------------- 1 | { 2 | "jsc": { 3 | "parser": { 4 | "syntax": "typescript", 5 | "decorators": true 6 | }, 7 | "transform": { 8 | "legacyDecorator": true, 9 | "decoratorMetadata": true 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | Please report security issues to `devteam@adguard.com` 6 | -------------------------------------------------------------------------------- /bamboo-specs/bamboo.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | !include 'tests.yaml' 3 | 4 | --- 5 | !include 'increment.yaml' 6 | 7 | --- 8 | !include 'build-beta.yaml' 9 | 10 | --- 11 | !include 'build-firefox-beta.yaml' 12 | 13 | --- 14 | !include 'build-release.yaml' 15 | 16 | --- 17 | !include 'deploy-beta.yaml' 18 | 19 | --- 20 | !include 'deploy-firefox-beta.yaml' 21 | 22 | --- 23 | !include 'deploy-release.yaml' 24 | 25 | --- 26 | !include 'permissions-beta.yaml' 27 | 28 | --- 29 | !include 'permissions-firefox-beta.yaml' 30 | 31 | --- 32 | !include 'permissions-release.yaml' 33 | -------------------------------------------------------------------------------- /bamboo-specs/permissions-beta.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | deployment: 4 | name: vpn-extension - deploy beta 5 | deployment-permissions: 6 | - groups: 7 | - extensions-developers 8 | - adguard-qa 9 | permissions: 10 | - view 11 | environment-permissions: 12 | - Chrome WebStore: 13 | - groups: 14 | - extensions-developers 15 | permissions: 16 | - view 17 | - deploy 18 | - static.adtidy.net: 19 | - groups: 20 | - extensions-developers 21 | permissions: 22 | - view 23 | - deploy 24 | - GitHub: 25 | - groups: 26 | - extensions-developers 27 | permissions: 28 | - view 29 | - deploy 30 | -------------------------------------------------------------------------------- /bamboo-specs/permissions-firefox-beta.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | deployment: 4 | name: vpn-extension - deploy firefox beta 5 | deployment-permissions: 6 | - groups: 7 | - extensions-developers 8 | - adguard-qa 9 | permissions: 10 | - view 11 | environment-permissions: 12 | - static.adtidy.net: 13 | - groups: 14 | - extensions-developers 15 | permissions: 16 | - view 17 | - deploy 18 | -------------------------------------------------------------------------------- /bamboo-specs/permissions-release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | deployment: 4 | name: vpn-extension - deploy release 5 | deployment-permissions: 6 | - groups: 7 | - extensions-developers 8 | - adguard-qa 9 | permissions: 10 | - view 11 | environment-permissions: 12 | - Chrome WebStore: 13 | - groups: 14 | - extensions-developers 15 | permissions: 16 | - view 17 | - deploy 18 | - Addons Mozilla: 19 | - groups: 20 | - extensions-developers 21 | permissions: 22 | - view 23 | - deploy 24 | - Edge Addons: 25 | - groups: 26 | - extensions-developers 27 | permissions: 28 | - view 29 | - deploy 30 | - GitHub: 31 | - groups: 32 | - extensions-developers 33 | permissions: 34 | - view 35 | - deploy 36 | -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | verbose: true, 3 | testEnvironment: '/tests/custom-test-env.ts', 4 | collectCoverage: true, 5 | collectCoverageFrom: [ 6 | '**/src/background/**/*.{js,jsx}', 7 | '**/src/common/**/*.{js,jsx}', 8 | '!**/node_modules/**', 9 | ], 10 | setupFiles: ['/tests/__setups__/chrome.ts'], 11 | setupFilesAfterEnv: ['/tests/__setups__/jest-setup.ts'], 12 | moduleNameMapper: { 13 | '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '/tests/__mocks__/fileMock.ts', 14 | '\\.(css|less|pcss)$': '/tests/__mocks__/styleMock.ts', 15 | }, 16 | transform: { 17 | '^.+\\.(t|j)sx?$': '@swc/jest', 18 | }, 19 | transformIgnorePatterns: ['node_modules/(?!(is-ip|ip-regex|@vespaiach/axios-fetch-adapter)/)'], 20 | }; 21 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | const postcssPresetEnv = require('postcss-preset-env'); 2 | const postcssGlobalData = require('@csstools/postcss-global-data'); 3 | 4 | module.exports = { 5 | plugins: [ 6 | // should go before postcss-custom-media to correct inject media rules 7 | postcssGlobalData({ files: ['./src/common/styles/media.pcss'] }), 8 | 'postcss-import', 9 | // should go before postcssPresetEnv with nesting-rules enabled 10 | 'postcss-nested', 11 | [postcssPresetEnv, { stage: 3, features: { 'nesting-rules': true } }], 12 | 'postcss-custom-media', 13 | ], 14 | }; 15 | -------------------------------------------------------------------------------- /src/assets/fonts/roboto/bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdguardTeam/AdGuardVPNExtension/e1c00ae9637fc9bae84b9808b55e7b8a287f129c/src/assets/fonts/roboto/bold.woff -------------------------------------------------------------------------------- /src/assets/fonts/roboto/bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdguardTeam/AdGuardVPNExtension/e1c00ae9637fc9bae84b9808b55e7b8a287f129c/src/assets/fonts/roboto/bold.woff2 -------------------------------------------------------------------------------- /src/assets/fonts/roboto/medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdguardTeam/AdGuardVPNExtension/e1c00ae9637fc9bae84b9808b55e7b8a287f129c/src/assets/fonts/roboto/medium.woff -------------------------------------------------------------------------------- /src/assets/fonts/roboto/medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdguardTeam/AdGuardVPNExtension/e1c00ae9637fc9bae84b9808b55e7b8a287f129c/src/assets/fonts/roboto/medium.woff2 -------------------------------------------------------------------------------- /src/assets/fonts/roboto/regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdguardTeam/AdGuardVPNExtension/e1c00ae9637fc9bae84b9808b55e7b8a287f129c/src/assets/fonts/roboto/regular.woff -------------------------------------------------------------------------------- /src/assets/fonts/roboto/regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdguardTeam/AdGuardVPNExtension/e1c00ae9637fc9bae84b9808b55e7b8a287f129c/src/assets/fonts/roboto/regular.woff2 -------------------------------------------------------------------------------- /src/assets/images/flags/ae.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/am.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/ar.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/at.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/ax.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/bd.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/be.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/bf.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/bg.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/bh.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/bn.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/bo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/bs.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/bt.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/bw.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/bz.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/cf.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/cg.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/ci.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/cl.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/cr.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/cu.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/cv.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/cz.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/dj.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/dk.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/ee.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/et.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/fi.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/fr.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/ga.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/gh.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/gm.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/gn.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/gr.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/gt.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/gw.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/gy.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/hu.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/id.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/ie.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/il.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/in.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/is.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/it.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/jm.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/jo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/jp.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/kw.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/la.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/li.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/lt.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/lu.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/lv.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/ma.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/mc.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/md.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/mg.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/ml.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/mm.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/mt.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/mu.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/mv.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/mw.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/mx.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/ne.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/ng.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/ni.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/nl.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/no.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/om.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/pe.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/pl.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/pw.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/py.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/qa.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/ro.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/ru.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/sd.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/se.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/sl.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/sm.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/sn.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/so.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/sr.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/td.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/tg.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/th.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/tl.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/tt.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/tw.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/tz.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/ua.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/vc.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/vn.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/ye.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/flags/za.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/icons/birthday25-off-19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdguardTeam/AdGuardVPNExtension/e1c00ae9637fc9bae84b9808b55e7b8a287f129c/src/assets/images/icons/birthday25-off-19.png -------------------------------------------------------------------------------- /src/assets/images/icons/birthday25-off-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdguardTeam/AdGuardVPNExtension/e1c00ae9637fc9bae84b9808b55e7b8a287f129c/src/assets/images/icons/birthday25-off-38.png -------------------------------------------------------------------------------- /src/assets/images/icons/birthday25-on-19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdguardTeam/AdGuardVPNExtension/e1c00ae9637fc9bae84b9808b55e7b8a287f129c/src/assets/images/icons/birthday25-on-19.png -------------------------------------------------------------------------------- /src/assets/images/icons/birthday25-on-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdguardTeam/AdGuardVPNExtension/e1c00ae9637fc9bae84b9808b55e7b8a287f129c/src/assets/images/icons/birthday25-on-38.png -------------------------------------------------------------------------------- /src/assets/images/icons/disabled-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdguardTeam/AdGuardVPNExtension/e1c00ae9637fc9bae84b9808b55e7b8a287f129c/src/assets/images/icons/disabled-128.png -------------------------------------------------------------------------------- /src/assets/images/icons/disabled-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdguardTeam/AdGuardVPNExtension/e1c00ae9637fc9bae84b9808b55e7b8a287f129c/src/assets/images/icons/disabled-16.png -------------------------------------------------------------------------------- /src/assets/images/icons/disabled-19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdguardTeam/AdGuardVPNExtension/e1c00ae9637fc9bae84b9808b55e7b8a287f129c/src/assets/images/icons/disabled-19.png -------------------------------------------------------------------------------- /src/assets/images/icons/disabled-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdguardTeam/AdGuardVPNExtension/e1c00ae9637fc9bae84b9808b55e7b8a287f129c/src/assets/images/icons/disabled-38.png -------------------------------------------------------------------------------- /src/assets/images/icons/enabled-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdguardTeam/AdGuardVPNExtension/e1c00ae9637fc9bae84b9808b55e7b8a287f129c/src/assets/images/icons/enabled-128.png -------------------------------------------------------------------------------- /src/assets/images/icons/enabled-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdguardTeam/AdGuardVPNExtension/e1c00ae9637fc9bae84b9808b55e7b8a287f129c/src/assets/images/icons/enabled-16.png -------------------------------------------------------------------------------- /src/assets/images/icons/enabled-19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdguardTeam/AdGuardVPNExtension/e1c00ae9637fc9bae84b9808b55e7b8a287f129c/src/assets/images/icons/enabled-19.png -------------------------------------------------------------------------------- /src/assets/images/icons/enabled-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdguardTeam/AdGuardVPNExtension/e1c00ae9637fc9bae84b9808b55e7b8a287f129c/src/assets/images/icons/enabled-38.png -------------------------------------------------------------------------------- /src/assets/images/icons/traffic-off-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdguardTeam/AdGuardVPNExtension/e1c00ae9637fc9bae84b9808b55e7b8a287f129c/src/assets/images/icons/traffic-off-128.png -------------------------------------------------------------------------------- /src/assets/images/icons/traffic-off-19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdguardTeam/AdGuardVPNExtension/e1c00ae9637fc9bae84b9808b55e7b8a287f129c/src/assets/images/icons/traffic-off-19.png -------------------------------------------------------------------------------- /src/assets/images/icons/traffic-off-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdguardTeam/AdGuardVPNExtension/e1c00ae9637fc9bae84b9808b55e7b8a287f129c/src/assets/images/icons/traffic-off-38.png -------------------------------------------------------------------------------- /src/assets/images/lock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/images/mobile-edge-promo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdguardTeam/AdGuardVPNExtension/e1c00ae9637fc9bae84b9808b55e7b8a287f129c/src/assets/images/mobile-edge-promo.png -------------------------------------------------------------------------------- /src/assets/images/no-search-result.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/images/preloader.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/star-active.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/images/star.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/motion/off-dark.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdguardTeam/AdGuardVPNExtension/e1c00ae9637fc9bae84b9808b55e7b8a287f129c/src/assets/motion/off-dark.webm -------------------------------------------------------------------------------- /src/assets/motion/off-light.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdguardTeam/AdGuardVPNExtension/e1c00ae9637fc9bae84b9808b55e7b8a287f129c/src/assets/motion/off-light.webm -------------------------------------------------------------------------------- /src/assets/motion/on-dark.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdguardTeam/AdGuardVPNExtension/e1c00ae9637fc9bae84b9808b55e7b8a287f129c/src/assets/motion/on-dark.webm -------------------------------------------------------------------------------- /src/assets/motion/on-light.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdguardTeam/AdGuardVPNExtension/e1c00ae9637fc9bae84b9808b55e7b8a287f129c/src/assets/motion/on-light.webm -------------------------------------------------------------------------------- /src/assets/motion/switch-off-dark.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdguardTeam/AdGuardVPNExtension/e1c00ae9637fc9bae84b9808b55e7b8a287f129c/src/assets/motion/switch-off-dark.webm -------------------------------------------------------------------------------- /src/assets/motion/switch-off-light.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdguardTeam/AdGuardVPNExtension/e1c00ae9637fc9bae84b9808b55e7b8a287f129c/src/assets/motion/switch-off-light.webm -------------------------------------------------------------------------------- /src/assets/motion/switch-on-dark.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdguardTeam/AdGuardVPNExtension/e1c00ae9637fc9bae84b9808b55e7b8a287f129c/src/assets/motion/switch-on-dark.webm -------------------------------------------------------------------------------- /src/assets/motion/switch-on-light.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdguardTeam/AdGuardVPNExtension/e1c00ae9637fc9bae84b9808b55e7b8a287f129c/src/assets/motion/switch-on-light.webm -------------------------------------------------------------------------------- /src/background/abTestManager/constants.ts: -------------------------------------------------------------------------------- 1 | // AG-21492 2 | // this is what is sent to backend 3 | export const AG21492_APPLICATION_REG_TEST_EXPERIMENT_ID = 'application_reg_AG21492'; 4 | // this is what backend sends back 5 | export const AG21492_DEFAULT_FLOW_VERSION_ID = 'reg_AG21492_def'; // do not delete, this is for informational purposes 6 | -------------------------------------------------------------------------------- /src/background/abTestManager/index.ts: -------------------------------------------------------------------------------- 1 | import { ABTestManager } from './ABTestManager'; 2 | import { AG21492_APPLICATION_REG_TEST_EXPERIMENT_ID } from './constants'; 3 | 4 | export const abTestManager = new ABTestManager([AG21492_APPLICATION_REG_TEST_EXPERIMENT_ID]); 5 | -------------------------------------------------------------------------------- /src/background/api/CustomError.ts: -------------------------------------------------------------------------------- 1 | export class CustomError extends Error { 2 | private status: string | number; 3 | 4 | constructor(status: string | number, ...params: any) { 5 | super(...params); 6 | // Maintains proper stack trace for where our error was thrown (only available on V8) 7 | if (Error.captureStackTrace) { 8 | Error.captureStackTrace(this, CustomError); 9 | } 10 | this.name = 'CustomError'; 11 | // Custom debugging information 12 | this.status = status; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/background/api/apiTypes.ts: -------------------------------------------------------------------------------- 1 | import { type Method } from 'axios'; 2 | 3 | export type AuthCredentials = { 4 | username: string; 5 | password: string; 6 | twoFactor: string; 7 | marketingConsent: boolean | null; 8 | locale: string; 9 | clientId: string; 10 | appId: string; 11 | 12 | /** 13 | * Optional email confirmation code. 14 | */ 15 | code?: string; 16 | }; 17 | 18 | export type RequestProps = { 19 | path: string; 20 | method: Method; 21 | }; 22 | -------------------------------------------------------------------------------- /src/background/api/index.ts: -------------------------------------------------------------------------------- 1 | export { vpnApi } from './vpnApi'; 2 | export { authApi } from './authApi'; 3 | export { accountApi, type VpnSubscriptionData } from './accountApi'; 4 | -------------------------------------------------------------------------------- /src/background/appStatus/index.ts: -------------------------------------------------------------------------------- 1 | import { proxy } from '../proxy'; 2 | import { settings } from '../settings'; 3 | import pJSON from '../../../package.json'; 4 | 5 | import { AppStatus } from './AppStatus'; 6 | 7 | export const appStatus = new AppStatus(proxy, settings, pJSON.version); 8 | -------------------------------------------------------------------------------- /src/background/auth/index.ts: -------------------------------------------------------------------------------- 1 | export { auth, type AuthInterface } from './auth'; 2 | -------------------------------------------------------------------------------- /src/background/auth/socialAuthSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | /** 4 | * This is a schema for the data that is returned from the social auth endpoint. 5 | */ 6 | const socialAuthUnderScoreSchema = z.object({ 7 | access_token: z.string(), 8 | expires_in: z.union([z.string().transform(Number), z.number()]), 9 | token_type: z.string(), 10 | state: z.string(), 11 | }); 12 | 13 | /** 14 | * This is a schema for the data that is returned from the social auth endpoint after transforming the keys. 15 | */ 16 | export const socialAuthSchema = socialAuthUnderScoreSchema.transform((data) => ({ 17 | accessToken: data.access_token, 18 | expiresIn: data.expires_in, 19 | tokenType: data.token_type, 20 | state: data.state, 21 | })); 22 | 23 | export type SocialAuthData = z.infer; 24 | -------------------------------------------------------------------------------- /src/background/auth/thankYouPageSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | const stringToBoolTransformer = z.string().transform((value) => value.toLowerCase() === 'true'); 4 | 5 | export const thankYouPageSchema = z.object({ 6 | token: z.string(), 7 | redirectUrl: z.string(), 8 | newUser: z.boolean().or(stringToBoolTransformer).transform((value) => Boolean(value)), 9 | }); 10 | 11 | export type ThankYouPageData = z.infer; 12 | -------------------------------------------------------------------------------- /src/background/authentication/index.ts: -------------------------------------------------------------------------------- 1 | export { authCache } from './authCache'; 2 | -------------------------------------------------------------------------------- /src/background/browserAction.ts: -------------------------------------------------------------------------------- 1 | import browser from 'webextension-polyfill'; 2 | 3 | import { browserApi } from './browserApi'; 4 | 5 | // There are different browser actions implementation depending on manifest version: 6 | // old browserAction API for manifest version 2 7 | // Action API for manifest version 3 8 | export const browserAction = browserApi.runtime.isManifestVersion2() ? browser.browserAction : browser.action; 9 | -------------------------------------------------------------------------------- /src/background/browserApi/index.ts: -------------------------------------------------------------------------------- 1 | import browser from 'webextension-polyfill'; 2 | 3 | import { type BrowserRuntime, runtime } from './runtime'; 4 | import { Storage } from './storage'; 5 | 6 | export type BrowserApi = { 7 | runtime: BrowserRuntime; 8 | storage: Storage; 9 | }; 10 | 11 | export const browserApi: BrowserApi = { 12 | runtime, 13 | storage: new Storage(browser), 14 | }; 15 | -------------------------------------------------------------------------------- /src/background/connectivity/connectivityService/fsm/constants.ts: -------------------------------------------------------------------------------- 1 | // Connection shouldn't be faster than specified time, because it causes ugly UI experience 2 | export const MIN_CONNECTION_DURATION_MS = 500; 3 | -------------------------------------------------------------------------------- /src/background/connectivity/connectivityService/fsm/delays.ts: -------------------------------------------------------------------------------- 1 | import { type ConnectivityContext } from '../../../schema/connectivity'; 2 | 3 | /** 4 | * Possible delays for connectivity finite state machine. 5 | */ 6 | export enum ConnectivityDelayType { 7 | RetryDelay = 'retryDelay', 8 | } 9 | 10 | /** 11 | * Connection retry delay function. 12 | * 13 | * @param context FSM context. 14 | * @returns Property used to keep growing delay between re-connections. 15 | */ 16 | function retryDelay(context: ConnectivityContext) { 17 | return context.currentReconnectionDelayMs; 18 | } 19 | 20 | /** 21 | * FSM delays config. 22 | */ 23 | export const connectivityDelays = { 24 | [ConnectivityDelayType.RetryDelay]: retryDelay, 25 | }; 26 | -------------------------------------------------------------------------------- /src/background/connectivity/connectivityService/fsm/index.ts: -------------------------------------------------------------------------------- 1 | // Connectivity FSM and interpreter factory functions. 2 | export * from './machine'; 3 | // Connectivity event types. 4 | export * from './events'; 5 | // Connectivity constants. 6 | export * from './constants'; 7 | -------------------------------------------------------------------------------- /src/background/connectivity/connectivityService/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-cycle */ 2 | // Global connectivity service instance. 3 | export { connectivityService } from './main'; 4 | // Connectivity service event types and constants. 5 | export { ConnectivityEventType, MIN_CONNECTION_DURATION_MS } from './fsm'; 6 | -------------------------------------------------------------------------------- /src/background/connectivity/endpointConnectivity/index.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/no-cycle 2 | import { EndpointConnectivity } from './EndpointConnectivity'; 3 | 4 | export const endpointConnectivity = new EndpointConnectivity(); 5 | -------------------------------------------------------------------------------- /src/background/connectivity/index.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/no-cycle 2 | import { endpointConnectivity } from './endpointConnectivity'; 3 | 4 | export const connectivity = { 5 | endpointConnectivity, 6 | }; 7 | -------------------------------------------------------------------------------- /src/background/connectivity/websocket/index.ts: -------------------------------------------------------------------------------- 1 | export { websocketFactory } from './websocketFactory'; 2 | -------------------------------------------------------------------------------- /src/background/connectivity/websocket/websocketFactory.ts: -------------------------------------------------------------------------------- 1 | import { log } from '../../../common/logger'; 2 | 3 | export const websocketFactory = (() => { 4 | let ws: WebSocket; 5 | 6 | /** 7 | * Creates new websocket and closes the old one if found 8 | * @param url 9 | */ 10 | const createWebsocket = (url: string): WebSocket => { 11 | if (!url) { 12 | throw new Error('Url expected to be provided'); 13 | } 14 | 15 | // Close previously opened websocket 16 | if (ws) { 17 | try { 18 | ws.close(); 19 | } catch (e) { 20 | log.debug(e); 21 | } 22 | } 23 | 24 | ws = new WebSocket(url); 25 | ws.binaryType = 'arraybuffer'; 26 | 27 | return ws; 28 | }; 29 | 30 | return { 31 | createWebsocket, 32 | }; 33 | })(); 34 | -------------------------------------------------------------------------------- /src/background/constants.ts: -------------------------------------------------------------------------------- 1 | export const ERROR_STATUSES = { 2 | NETWORK_ERROR: 'network.error', 3 | UNAUTHORIZED: 401, 4 | }; 5 | 6 | export const SUPPORT_EMAIL = 'support@adguard-vpn.com'; 7 | -------------------------------------------------------------------------------- /src/background/credentials/index.ts: -------------------------------------------------------------------------------- 1 | import { browserApi } from '../browserApi'; 2 | import { permissionsError } from '../permissionsChecker/permissionsError'; 3 | import { proxy } from '../proxy'; 4 | import { vpnProvider } from '../providers/vpnProvider'; 5 | // eslint-disable-next-line import/no-cycle 6 | import { auth } from '../auth'; 7 | 8 | import { Credentials } from './Credentials'; 9 | 10 | export const credentials = new Credentials({ 11 | browserApi, 12 | permissionsError, 13 | proxy, 14 | vpnProvider, 15 | auth, 16 | }); 17 | -------------------------------------------------------------------------------- /src/background/dns/index.ts: -------------------------------------------------------------------------------- 1 | import { Dns } from './Dns'; 2 | 3 | export const dns = new Dns(); 4 | -------------------------------------------------------------------------------- /src/background/emailConfirmationService/index.ts: -------------------------------------------------------------------------------- 1 | export { emailConfirmationService } from './emailConfirmationService'; 2 | -------------------------------------------------------------------------------- /src/background/endpoints/Endpoint.ts: -------------------------------------------------------------------------------- 1 | import type { EndpointInterface } from '../schema'; 2 | 3 | /** 4 | * Class representing endpoint structure 5 | */ 6 | export class Endpoint implements EndpointInterface { 7 | id: string; 8 | 9 | ipv4Address: string; 10 | 11 | ipv6Address: string; 12 | 13 | domainName: string; 14 | 15 | publicKey: string; 16 | 17 | constructor({ 18 | id, 19 | ipv4Address, 20 | ipv6Address, 21 | domainName, 22 | publicKey, 23 | }: EndpointInterface) { 24 | this.id = id; 25 | this.ipv4Address = ipv4Address; 26 | this.ipv6Address = ipv6Address; 27 | this.domainName = domainName; 28 | this.publicKey = publicKey; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/background/endpoints/index.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/no-cycle 2 | import endpoints from './Endpoints'; 3 | 4 | export { endpoints }; 5 | -------------------------------------------------------------------------------- /src/background/exclusions/index.ts: -------------------------------------------------------------------------------- 1 | import { ExclusionsService } from './ExclusionsService'; 2 | 3 | export const exclusions = new ExclusionsService(); 4 | -------------------------------------------------------------------------------- /src/background/exclusions/services/Service.ts: -------------------------------------------------------------------------------- 1 | import type { ServiceCategory, ServiceInterface } from '../../schema'; 2 | 3 | export class Service implements ServiceInterface { 4 | serviceId: string; 5 | 6 | serviceName: string; 7 | 8 | iconUrl: string; 9 | 10 | modifiedTime: string; 11 | 12 | categories: ServiceCategory[]; 13 | 14 | domains: string[]; 15 | 16 | constructor(service: ServiceInterface) { 17 | this.serviceId = service.serviceId; 18 | this.serviceName = service.serviceName; 19 | this.iconUrl = service.iconUrl; 20 | this.categories = service.categories; 21 | this.modifiedTime = service.modifiedTime; 22 | this.domains = service.domains; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/background/flagsStorageData.ts: -------------------------------------------------------------------------------- 1 | import zod from 'zod'; 2 | 3 | import { FLAGS_FIELDS } from '../common/constants'; 4 | 5 | export const flagsStorageDataScheme = zod.record(zod.string().or(zod.boolean())); 6 | 7 | export type FlagsStorageData = zod.infer; 8 | 9 | export const FLAG_STORAGE_DEFAULTS: FlagsStorageData = { 10 | // onboarding should be displayed for new users and on first run (AG-10009) 11 | [FLAGS_FIELDS.SHOW_ONBOARDING]: true, 12 | // upgrade screen should be displayed for non-premium users after onboarding screen 13 | [FLAGS_FIELDS.SHOW_UPGRADE_SCREEN]: true, 14 | }; 15 | -------------------------------------------------------------------------------- /src/background/forwarder/index.ts: -------------------------------------------------------------------------------- 1 | export { forwarder } from './forwarder'; 2 | -------------------------------------------------------------------------------- /src/background/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | Background page 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/background/index.ts: -------------------------------------------------------------------------------- 1 | import { main } from './main'; 2 | 3 | main(); 4 | -------------------------------------------------------------------------------- /src/background/limitedOfferService/index.ts: -------------------------------------------------------------------------------- 1 | export { limitedOfferService } from './limitedOfferService'; 2 | export type { LimitedOfferData } from './limitedOfferService'; 3 | -------------------------------------------------------------------------------- /src/background/main/index.ts: -------------------------------------------------------------------------------- 1 | export { main } from './main'; 2 | -------------------------------------------------------------------------------- /src/background/management/index.ts: -------------------------------------------------------------------------------- 1 | import browser from 'webextension-polyfill'; 2 | 3 | import Management from './Management'; 4 | 5 | export const management = new Management(browser); 6 | -------------------------------------------------------------------------------- /src/background/messaging/index.ts: -------------------------------------------------------------------------------- 1 | export { messaging } from './messaging'; 2 | -------------------------------------------------------------------------------- /src/background/messaging/messagingTypes.ts: -------------------------------------------------------------------------------- 1 | import { type SocialAuthProvider } from '../../common/constants'; 2 | 3 | export interface StartSocialAuthData { 4 | provider: SocialAuthProvider; 5 | marketingConsent: boolean; 6 | } 7 | 8 | export interface UserLookupData { 9 | email: string; 10 | } 11 | -------------------------------------------------------------------------------- /src/background/mobileEdgePromoService/index.ts: -------------------------------------------------------------------------------- 1 | export { mobileEdgePromoService } from './mobileEdgePromoService'; 2 | -------------------------------------------------------------------------------- /src/background/networkConnectionObserver/networkConnectionObserverAbstract.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * export './networkConnectionObserverAbstract' is replaced during webpack compilation 3 | * with NormalModuleReplacementPlugin to proper implementation 4 | * from './networkConnectionObserverMv2' or './networkConnectionObserverMv3' 5 | */ 6 | export class NetworkConnectionObserver { 7 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 8 | constructor(arg?: unknown) { 9 | throw new Error('Seems like webpack didn\'t inject proper NetworkConnectionObserver'); 10 | } 11 | 12 | public init() { 13 | throw new Error('Seems like webpack didn\'t inject proper NetworkConnectionObserver'); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/background/networkConnectionObserver/networkConnectionObserverMv2.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Class observes network state 3 | */ 4 | export class NetworkConnectionObserver { 5 | private readonly onlineHandler: () => Promise; 6 | 7 | constructor(callback: () => Promise) { 8 | this.onlineHandler = callback; 9 | } 10 | 11 | /** 12 | * Initializes the network connection observer. 13 | */ 14 | public init() { 15 | window.addEventListener('online', this.onlineHandler); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/background/permissionsChecker/index.ts: -------------------------------------------------------------------------------- 1 | import { credentials } from '../credentials'; 2 | 3 | import { PermissionsChecker } from './PermissionsChecker'; 4 | import { permissionsError } from './permissionsError'; 5 | 6 | export const permissionsChecker = new PermissionsChecker({ 7 | credentials, 8 | permissionsError, 9 | }); 10 | -------------------------------------------------------------------------------- /src/background/popupData/index.ts: -------------------------------------------------------------------------------- 1 | import { nonRoutable } from '../routability/nonRoutable'; 2 | import { permissionsError } from '../permissionsChecker/permissionsError'; 3 | import { endpoints } from '../endpoints'; 4 | import { permissionsChecker } from '../permissionsChecker'; 5 | import { credentials } from '../credentials'; 6 | 7 | import { PopupData } from './PopupData'; 8 | 9 | export const popupData = new PopupData({ 10 | endpoints, 11 | nonRoutable, 12 | permissionsChecker, 13 | permissionsError, 14 | credentials, 15 | }); 16 | -------------------------------------------------------------------------------- /src/background/proxy/chrome/proxyAuthTrigger/abstractProxyAuthTrigger.ts: -------------------------------------------------------------------------------- 1 | class AbstractProxyAuthTrigger { 2 | async run() { 3 | throw new Error('Not implemented'); 4 | } 5 | } 6 | 7 | export const proxyAuthTrigger = new AbstractProxyAuthTrigger(); 8 | -------------------------------------------------------------------------------- /src/background/proxy/chrome/proxyAuthTrigger/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This import will be replaced with proper implementation during bundling, with plugin NormalModuleReplacementPlugin 3 | * "./abstractProxyAuthTrigger" will be replaced by "./mv2" or "./mv3". 4 | */ 5 | export { proxyAuthTrigger } from './abstractProxyAuthTrigger'; 6 | -------------------------------------------------------------------------------- /src/background/proxy/chrome/proxyAuthTrigger/mv2/index.ts: -------------------------------------------------------------------------------- 1 | import { ProxyAuthTrigger } from './ProxyAuthTrigger'; 2 | 3 | const proxyAuthTrigger = new ProxyAuthTrigger(); 4 | export { proxyAuthTrigger }; 5 | -------------------------------------------------------------------------------- /src/background/proxy/chrome/proxyAuthTrigger/mv3/index.ts: -------------------------------------------------------------------------------- 1 | import { ProxyAuthTrigger } from './ProxyAuthTrigger'; 2 | 3 | const proxyAuthTrigger = new ProxyAuthTrigger(); 4 | export { proxyAuthTrigger }; 5 | -------------------------------------------------------------------------------- /src/background/proxy/index.ts: -------------------------------------------------------------------------------- 1 | export { proxy } from './proxy'; 2 | -------------------------------------------------------------------------------- /src/background/proxy/proxyConsts.ts: -------------------------------------------------------------------------------- 1 | export const LEVELS_OF_CONTROL = { 2 | NOT_CONTROLLABLE: 'not_controllable', 3 | CONTROLLED_BY_OTHER_EXTENSION: 'controlled_by_other_extensions', 4 | }; 5 | 6 | export const DEFAULT_EXCLUSIONS = [ 7 | // non routable 8 | 'localhost', 9 | '*.local', 10 | '127.0.0.1', 11 | ]; 12 | 13 | /** 14 | * This constant is used to send random request, which should be intercepted by proxy endpoint 15 | * and return empty response with status 200. 16 | * There is a known bug in Chrome: https://bugs.chromium.org/p/chromium/issues/detail?id=1009243 17 | * when onAuthRequired is not triggered when request is sent from service worker. 18 | * When this bug is fixed, this constant can be removed. 19 | */ 20 | export const PAC_SCRIPT_CHECK_URL = 'check-pac.adguard-vpn.online'; 21 | -------------------------------------------------------------------------------- /src/background/rateModal/index.ts: -------------------------------------------------------------------------------- 1 | import { notifier } from '../../common/notifier'; 2 | import { browserApi } from '../browserApi'; 3 | import { settings } from '../settings'; 4 | 5 | import { RateModal } from './RateModal'; 6 | 7 | export const rateModal = new RateModal({ 8 | storage: browserApi.storage, 9 | settings, 10 | notifier, 11 | }); 12 | -------------------------------------------------------------------------------- /src/background/routability/constants.ts: -------------------------------------------------------------------------------- 1 | export const NON_ROUTABLE_CIDR_NETS = [ 2 | '0.0.0.0/8', 3 | '10.0.0.0/8', 4 | '14.0.0.0/8', 5 | '24.0.0.0/8', 6 | '39.0.0.0/8', 7 | '127.0.0.0/8', 8 | '128.0.0.0/16', 9 | '169.254.0.0/16', 10 | '172.16.0.0/12', 11 | '191.255.0.0/16', 12 | '192.0.0.0/24', 13 | '192.0.2.0/24', 14 | '192.88.99.0/24', 15 | '192.168.0.0/16', 16 | '198.18.0.0/15', 17 | '223.255.255.0/24', 18 | '224.0.0.0/4', 19 | '240.0.0.0/4', 20 | ]; 21 | 22 | export const IPV4_REGEX = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/; 23 | -------------------------------------------------------------------------------- /src/background/routability/nonRoutable.ts: -------------------------------------------------------------------------------- 1 | import { browserApi } from '../browserApi'; 2 | 3 | import { NonRoutableService } from './NonRoutableService'; 4 | 5 | const nonRoutableService = new NonRoutableService(browserApi.storage); 6 | 7 | export const nonRoutable = { 8 | isUrlRoutable: nonRoutableService.isUrlRoutable.bind(nonRoutableService), 9 | init: nonRoutableService.init.bind(nonRoutableService), 10 | getNonRoutableList: nonRoutableService.getNonRoutableList.bind(nonRoutableService), 11 | }; 12 | -------------------------------------------------------------------------------- /src/background/routability/utils.ts: -------------------------------------------------------------------------------- 1 | import ipaddr from 'ipaddr.js'; 2 | 3 | /** 4 | * Checks if host is in net 5 | * @param host host address of application presented with ip 6 | * @param pattern 7 | * @param mask 8 | */ 9 | export const isInNet = (host: string, pattern: string, mask: string) => { 10 | const addr = ipaddr.IPv4.parse(host); 11 | 12 | return addr.match([ 13 | ipaddr.IPv4.parse(pattern), 14 | ipaddr.IPv4.parse(mask).prefixLengthFromSubnetMask(), 15 | ]); 16 | }; 17 | 18 | export const convertCidrToNet = (cidr: string): [string, string] => { 19 | const [ipAddress, subnetPrefix] = ipaddr.parseCIDR(cidr); 20 | return [ 21 | ipAddress.toString(), 22 | ipaddr.IPv4.subnetMaskFromPrefixLength(subnetPrefix).toString(), 23 | ]; 24 | }; 25 | -------------------------------------------------------------------------------- /src/background/savedLocations/index.ts: -------------------------------------------------------------------------------- 1 | import { browserApi } from '../browserApi'; 2 | 3 | import { SavedLocations } from './SavedLocations'; 4 | 5 | export const savedLocations = new SavedLocations({ 6 | storage: browserApi.storage, 7 | }); 8 | -------------------------------------------------------------------------------- /src/background/schema/auth/authAccessToken.ts: -------------------------------------------------------------------------------- 1 | import zod from 'zod'; 2 | 3 | export const authAccessTokenScheme = zod.object({ 4 | accessToken: zod.string(), 5 | expiresIn: zod.number(), 6 | tokenType: zod.string(), 7 | scope: zod.string().optional(), 8 | }); 9 | 10 | /** 11 | * Auth access token example: 12 | * { 13 | * "accessToken":"9f8duv8dfv", 14 | * "tokenType":"bearer", 15 | * "expiresIn":2627940, 16 | * "scope":"trust" 17 | * } 18 | */ 19 | export type AuthAccessToken = zod.infer; 20 | -------------------------------------------------------------------------------- /src/background/schema/auth/authState.ts: -------------------------------------------------------------------------------- 1 | import zod from 'zod'; 2 | 3 | import { authAccessTokenScheme } from './authAccessToken'; 4 | 5 | export const authStateScheme = zod.object({ 6 | socialAuthState: zod.string().or(zod.null()), 7 | accessTokenData: authAccessTokenScheme.or(zod.null()), 8 | }); 9 | 10 | export type AuthState = zod.infer; 11 | 12 | export const AUTH_STATE_DEFAULTS: AuthState = { 13 | socialAuthState: null, 14 | accessTokenData: null, 15 | }; 16 | -------------------------------------------------------------------------------- /src/background/schema/auth/index.ts: -------------------------------------------------------------------------------- 1 | export * from './authState'; 2 | export * from './authAccessToken'; 3 | -------------------------------------------------------------------------------- /src/background/schema/connectivity/data.ts: -------------------------------------------------------------------------------- 1 | import zod from 'zod'; 2 | 3 | import { connectivityStateScheme, CONNECTIVITY_STATE_DEFAULT } from './state'; 4 | import { connectivityContextScheme, CONNECTIVITY_CONTEXT_DEFAULTS } from './context'; 5 | 6 | /** 7 | * Connectivity FSM persisted data schema. 8 | */ 9 | export const connectivityDataScheme = zod.object({ 10 | state: connectivityStateScheme, 11 | context: connectivityContextScheme, 12 | }).strict(); 13 | 14 | /** 15 | * {@link connectivityDataScheme} type. 16 | */ 17 | export type ConnectivityData = zod.infer; 18 | 19 | /** 20 | * Default values for {@link ConnectivityData}. 21 | */ 22 | export const CONNECTIVITY_DATA_DEFAULTS: ConnectivityData = { 23 | state: CONNECTIVITY_STATE_DEFAULT, 24 | context: CONNECTIVITY_CONTEXT_DEFAULTS, 25 | }; 26 | -------------------------------------------------------------------------------- /src/background/schema/connectivity/index.ts: -------------------------------------------------------------------------------- 1 | // Connectivity FSM context schema 2 | export * from './context'; 3 | // Connectivity FSM state schema 4 | export * from './state'; 5 | // All persisted FSM data 6 | export * from './data'; 7 | -------------------------------------------------------------------------------- /src/background/schema/credentials/index.ts: -------------------------------------------------------------------------------- 1 | export * from './credentialsState'; 2 | export * from './vpnTokenData'; 3 | -------------------------------------------------------------------------------- /src/background/schema/credentials/trackInstallResponse.ts: -------------------------------------------------------------------------------- 1 | import zod from 'zod'; 2 | 3 | export const versionsSchema = zod.array(zod.string()).optional(); 4 | export const experimentsSchema = zod.object({ 5 | versions: versionsSchema, 6 | }).optional(); 7 | 8 | export const trackInstallResponseSchema = zod.object({ 9 | experiments: experimentsSchema, 10 | }); 11 | 12 | export type VersionsResponse = zod.infer; 13 | export type ExperimentsResponse = zod.infer; 14 | export type TrackInstallResponse = zod.infer; 15 | -------------------------------------------------------------------------------- /src/background/schema/credentials/vpnTokenData.ts: -------------------------------------------------------------------------------- 1 | import zod from 'zod'; 2 | 3 | import { SubscriptionType } from '../../../common/constants'; 4 | 5 | const vpnSubscriptionScheme = zod.object({ 6 | next_bill_date_iso: zod.string(), 7 | duration_v2: zod.nativeEnum(SubscriptionType), 8 | }); 9 | 10 | export const vpnTokenDataScheme = zod.object({ 11 | token: zod.string(), 12 | licenseStatus: zod.string(), 13 | timeExpiresSec: zod.number(), 14 | timeExpiresIso: zod.string(), 15 | licenseKey: zod.string().nullable(), 16 | vpnSubscription: vpnSubscriptionScheme.or(zod.null()), 17 | }); 18 | 19 | export type VpnTokenData = zod.infer; 20 | -------------------------------------------------------------------------------- /src/background/schema/dns/dnsServerData.ts: -------------------------------------------------------------------------------- 1 | import zod from 'zod'; 2 | 3 | export const dnsServerDataScheme = zod.object({ 4 | id: zod.string(), 5 | title: zod.string(), 6 | address: zod.string(), 7 | desc: zod.string().optional(), 8 | }); 9 | 10 | export type DnsServerData = zod.infer; 11 | -------------------------------------------------------------------------------- /src/background/schema/dns/dnsState.ts: -------------------------------------------------------------------------------- 1 | import zod from 'zod'; 2 | 3 | import { dnsServerDataScheme } from './dnsServerData'; 4 | 5 | export const dnsStateScheme = zod.object({ 6 | selectedDnsServer: zod.string().or(zod.null()), 7 | customDnsServers: dnsServerDataScheme.array(), 8 | backupDnsServersData: dnsServerDataScheme.array(), 9 | }); 10 | 11 | export type DnsState = zod.infer; 12 | 13 | export const DNS_STATE_DEFAULTS: DnsState = { 14 | customDnsServers: [], 15 | backupDnsServersData: [], 16 | selectedDnsServer: null, 17 | }; 18 | -------------------------------------------------------------------------------- /src/background/schema/dns/index.ts: -------------------------------------------------------------------------------- 1 | export * from './dnsState'; 2 | export * from './dnsServerData'; 3 | -------------------------------------------------------------------------------- /src/background/schema/endpoints/endpointInterface.ts: -------------------------------------------------------------------------------- 1 | import zod from 'zod'; 2 | 3 | // TODO: advanced validators for ip and domains 4 | export const endpointInterfaceScheme = zod.object({ 5 | id: zod.string(), 6 | ipv4Address: zod.string(), 7 | ipv6Address: zod.string(), 8 | domainName: zod.string(), 9 | publicKey: zod.string(), 10 | }).strict(); 11 | 12 | export type EndpointInterface = zod.infer; 13 | -------------------------------------------------------------------------------- /src/background/schema/endpoints/endpointsState.ts: -------------------------------------------------------------------------------- 1 | import zod from 'zod'; 2 | 3 | import { VpnInfoScheme } from '../../../common/schema/endpoints/vpnInfo'; 4 | 5 | export const endpointsStateScheme = zod.object({ 6 | vpnInfo: VpnInfoScheme.or(zod.null()), 7 | }); 8 | 9 | export type EndpointsState = zod.infer; 10 | 11 | export const ENDPOINTS_STATE_DEFAULTS = { 12 | vpnInfo: null, 13 | }; 14 | -------------------------------------------------------------------------------- /src/background/schema/endpoints/index.ts: -------------------------------------------------------------------------------- 1 | export * from './endpointInterface'; 2 | export * from './location'; 3 | export * from './locationsService'; 4 | export * from './endpointsState'; 5 | export * from './pingsCache'; 6 | -------------------------------------------------------------------------------- /src/background/schema/endpoints/locationsService.ts: -------------------------------------------------------------------------------- 1 | import zod from 'zod'; 2 | 3 | import { locationScheme } from './location'; 4 | import { pingsCacheScheme } from './pingsCache'; 5 | 6 | export const locationsServiceStateScheme = zod.object({ 7 | pingsCache: pingsCacheScheme, 8 | locations: locationScheme.array(), 9 | selectedLocation: locationScheme.or(zod.null()), 10 | }); 11 | 12 | export type LocationsServiceState = zod.infer; 13 | 14 | export const LOCATIONS_SERVICE_STATE_DEFAULTS = { 15 | pingsCache: {}, 16 | locations: [], 17 | selectedLocation: null, 18 | }; 19 | -------------------------------------------------------------------------------- /src/background/schema/endpoints/pingsCache.ts: -------------------------------------------------------------------------------- 1 | import zod from 'zod'; 2 | 3 | import { endpointInterfaceScheme } from './endpointInterface'; 4 | 5 | export const pingDataScheme = zod.object({ 6 | ping: zod.number().or(zod.null()), 7 | available: zod.boolean(), 8 | lastMeasurementTime: zod.number(), 9 | endpoint: endpointInterfaceScheme.or(zod.null()), 10 | isMeasuring: zod.boolean(), 11 | }); 12 | 13 | export const pingsCacheScheme = zod.record(zod.string(), pingDataScheme); 14 | 15 | export type PingsCacheInterface = zod.infer; 16 | -------------------------------------------------------------------------------- /src/background/schema/exclusions/exclusions/exclusion.ts: -------------------------------------------------------------------------------- 1 | import zod from 'zod'; 2 | 3 | import { ExclusionState } from '../../../../common/exclusionsConstants'; 4 | 5 | const exclusionState = zod.enum([ExclusionState.Enabled, ExclusionState.Disabled]); 6 | 7 | export const exclusionScheme = zod.object({ 8 | id: zod.string(), 9 | hostname: zod.string(), 10 | state: exclusionState, 11 | }); 12 | 13 | export type ExclusionInterface = zod.infer; 14 | -------------------------------------------------------------------------------- /src/background/schema/exclusions/exclusions/exclusionsHandler.ts: -------------------------------------------------------------------------------- 1 | import zod from 'zod'; 2 | 3 | import { exclusionScheme } from './exclusion'; 4 | 5 | const exclusionsIndexScheme = zod.record(zod.string(), zod.string().array()); 6 | 7 | export type IndexedExclusionsInterface = zod.infer; 8 | 9 | export const exclusionsHandlerStateScheme = zod.object({ 10 | exclusions: exclusionScheme.array(), 11 | exclusionsIndex: exclusionsIndexScheme, 12 | }).strict(); 13 | 14 | export type ExclusionsHandlerState = zod.infer; 15 | -------------------------------------------------------------------------------- /src/background/schema/exclusions/exclusions/index.ts: -------------------------------------------------------------------------------- 1 | export * from './exclusion'; 2 | export * from './exclusionsManagerState'; 3 | export * from './exclusionsHandler'; 4 | -------------------------------------------------------------------------------- /src/background/schema/exclusions/exclusionsServices/index.ts: -------------------------------------------------------------------------------- 1 | export * from './service'; 2 | export * from './servicesManagerState'; 3 | -------------------------------------------------------------------------------- /src/background/schema/exclusions/exclusionsServices/service.ts: -------------------------------------------------------------------------------- 1 | import zod from 'zod'; 2 | 3 | const serviceCategoryScheme = zod.object({ 4 | id: zod.string(), 5 | name: zod.string(), 6 | }).strict(); 7 | 8 | export type ServiceCategory = zod.infer; 9 | 10 | export const serviceScheme = zod.object({ 11 | serviceId: zod.string(), 12 | serviceName: zod.string(), 13 | iconUrl: zod.string(), 14 | modifiedTime: zod.string(), 15 | categories: serviceCategoryScheme.array(), 16 | domains: zod.string().array(), 17 | }).strict(); 18 | 19 | export type ServiceInterface = zod.infer; 20 | -------------------------------------------------------------------------------- /src/background/schema/exclusions/exclusionsState.ts: -------------------------------------------------------------------------------- 1 | import zod from 'zod'; 2 | 3 | import { persistedExclusionsScheme } from './exclusions'; 4 | 5 | export const exclusionsStateScheme = zod.object({ 6 | previousExclusions: persistedExclusionsScheme.omit({ inverted: true }).or(zod.null()), 7 | }).strict(); 8 | 9 | export type ExclusionsState = zod.infer; 10 | 11 | export const EXCLUSIONS_STATE_DEFAULTS: ExclusionsState = { 12 | previousExclusions: null, 13 | }; 14 | -------------------------------------------------------------------------------- /src/background/schema/exclusions/index.ts: -------------------------------------------------------------------------------- 1 | export * from './exclusions'; 2 | export * from './exclusionsServices'; 3 | export * from './exclusionsState'; 4 | -------------------------------------------------------------------------------- /src/background/schema/fallbackApi/fallbackInfo.ts: -------------------------------------------------------------------------------- 1 | import zod from 'zod'; 2 | 3 | export const fallbackInfoScheme = zod.object({ 4 | vpnApiUrl: zod.string(), 5 | authApiUrl: zod.string(), 6 | forwarderApiUrl: zod.string(), 7 | expiresInMs: zod.number(), 8 | }).strict(); 9 | 10 | export type FallbackInfo = zod.infer; 11 | -------------------------------------------------------------------------------- /src/background/schema/fallbackApi/index.ts: -------------------------------------------------------------------------------- 1 | export * from './fallbackInfo'; 2 | -------------------------------------------------------------------------------- /src/background/schema/index.ts: -------------------------------------------------------------------------------- 1 | export * from './proxy'; 2 | export * from './fallbackApi'; 3 | export * from './updateService'; 4 | export * from './exclusions'; 5 | export * from './credentials'; 6 | export * from './endpoints'; 7 | export * from './sessionStorage'; 8 | export * from './auth'; 9 | export * from './dns'; 10 | export * from './permissionsChecker'; 11 | export * from './popupData'; 12 | export * from './connectivity'; 13 | -------------------------------------------------------------------------------- /src/background/schema/limitedOffer/index.ts: -------------------------------------------------------------------------------- 1 | export { limitedOfferStorageDataScheme } from './limitedOfferService'; 2 | export type { LimitedOfferStorageData } from './limitedOfferService'; 3 | -------------------------------------------------------------------------------- /src/background/schema/limitedOffer/limitedOfferService.ts: -------------------------------------------------------------------------------- 1 | import zod from 'zod'; 2 | 3 | export const limitedOfferStorageDataScheme = zod.record(zod.number().or(zod.null())).or(zod.null()); 4 | 5 | export type LimitedOfferStorageData = zod.infer; 6 | -------------------------------------------------------------------------------- /src/background/schema/permissionsChecker/index.ts: -------------------------------------------------------------------------------- 1 | export * from './permissionsCheckerState'; 2 | -------------------------------------------------------------------------------- /src/background/schema/permissionsChecker/permissionsCheckerState.ts: -------------------------------------------------------------------------------- 1 | import zod from 'zod'; 2 | 3 | export const permissionsCheckerStateScheme = zod.object({ 4 | credentialsCheckTimerId: zod.number().or(zod.null()), 5 | vpnInfoCheckTimerId: zod.number().or(zod.null()), 6 | expiredCredentialsCheckTimeoutId: zod.number().or(zod.null()), 7 | }).strict(); 8 | 9 | export type PermissionsCheckerState = zod.infer; 10 | 11 | export const PERMISSIONS_CHECKER_DEFAULTS: PermissionsCheckerState = { 12 | credentialsCheckTimerId: null, 13 | vpnInfoCheckTimerId: null, 14 | expiredCredentialsCheckTimeoutId: null, 15 | }; 16 | -------------------------------------------------------------------------------- /src/background/schema/popupData/index.ts: -------------------------------------------------------------------------------- 1 | export * from './popupOpenedCounter'; 2 | -------------------------------------------------------------------------------- /src/background/schema/popupData/popupOpenedCounter.ts: -------------------------------------------------------------------------------- 1 | import zod from 'zod'; 2 | 3 | export const popupOpenedCounterStateScheme = zod.object({ 4 | count: zod.number(), 5 | }).strict(); 6 | 7 | export type PopupOpenedCounterState = zod.infer; 8 | 9 | export const POPUP_OPENED_COUNTER_DEFAULTS: PopupOpenedCounterState = { 10 | count: 0, 11 | }; 12 | -------------------------------------------------------------------------------- /src/background/schema/proxy/accessCredentials.ts: -------------------------------------------------------------------------------- 1 | import zod from 'zod'; 2 | 3 | export const accessCredentialsScheme = zod.object({ 4 | username: zod.string(), 5 | password: zod.string(), 6 | }).strict(); 7 | 8 | export type AccessCredentials = zod.infer; 9 | 10 | export const ACCESS_CREDENTIALS_DEFAULTS: AccessCredentials = { 11 | username: '', 12 | password: '', 13 | }; 14 | -------------------------------------------------------------------------------- /src/background/schema/proxy/canControlProxy.ts: -------------------------------------------------------------------------------- 1 | import zod from 'zod'; 2 | 3 | export const canControlProxyScheme = zod.object({ 4 | canControlProxy: zod.boolean(), 5 | cause: zod.string().optional(), 6 | }).strict(); 7 | 8 | export type CanControlProxy = zod.infer; 9 | -------------------------------------------------------------------------------- /src/background/schema/proxy/endpointsTldExclusions.ts: -------------------------------------------------------------------------------- 1 | import zod from 'zod'; 2 | 3 | export const endpointsTldExclusionsScheme = zod.object({ 4 | endpointsTldExclusionsList: zod.string().array(), 5 | }).strict(); 6 | 7 | export type EndpointsTldExclusionsState = zod.infer; 8 | 9 | export const ENDPOINTS_TLD_EXCLUSIONS_DEFAULTS: EndpointsTldExclusionsState = { 10 | endpointsTldExclusionsList: [], 11 | }; 12 | -------------------------------------------------------------------------------- /src/background/schema/proxy/index.ts: -------------------------------------------------------------------------------- 1 | export * from './proxyState'; 2 | export * from './accessCredentials'; 3 | export * from './canControlProxy'; 4 | export * from './proxyConfigInterface'; 5 | -------------------------------------------------------------------------------- /src/background/schema/proxy/proxyConfigInterface.ts: -------------------------------------------------------------------------------- 1 | import zod from 'zod'; 2 | 3 | import { accessCredentialsScheme } from './accessCredentials'; 4 | 5 | export const proxyConfigInterfaceScheme = zod.object({ 6 | bypassList: zod.string().array(), 7 | defaultExclusions: zod.string().array(), 8 | nonRoutableCidrNets: zod.string().array(), 9 | host: zod.string(), 10 | port: zod.number(), 11 | scheme: zod.string(), 12 | inverted: zod.boolean(), 13 | credentials: accessCredentialsScheme, 14 | }).strict(); 15 | 16 | export type ProxyConfigInterface = zod.infer; 17 | -------------------------------------------------------------------------------- /src/background/schema/sessionStorage/index.ts: -------------------------------------------------------------------------------- 1 | export * from './StorageData'; 2 | -------------------------------------------------------------------------------- /src/background/schema/updateService/index.ts: -------------------------------------------------------------------------------- 1 | export * from './updateServiceState'; 2 | -------------------------------------------------------------------------------- /src/background/schema/updateService/updateServiceState.ts: -------------------------------------------------------------------------------- 1 | import zod from 'zod'; 2 | 3 | export const updateServiceStateScheme = zod.object({ 4 | prevVersion: zod.string().optional(), 5 | currentVersion: zod.string().optional(), 6 | }).strict(); 7 | 8 | export type UpdateServiceState = zod.infer; 9 | -------------------------------------------------------------------------------- /src/background/settings/index.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/no-cycle 2 | export { settings } from './settings'; 3 | -------------------------------------------------------------------------------- /src/background/stateStorage/helper.ts: -------------------------------------------------------------------------------- 1 | import browser from 'webextension-polyfill'; 2 | 3 | import { MessageType } from '../../common/constants'; 4 | 5 | /** 6 | * Sends the message to the option's page to update event listeners 7 | */ 8 | export const updateOptionsPageListeners = async () => { 9 | try { 10 | await browser.runtime.sendMessage({ type: MessageType.UPDATE_LISTENERS }); 11 | } catch (e) { 12 | // ignore error of there is no options page 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /src/background/stateStorage/index.ts: -------------------------------------------------------------------------------- 1 | // !IMPORTANT! 2 | // export './stateStorage.abstract' is replaced during webpack compilation 3 | // with NormalModuleReplacementPlugin to proper implementation for the manifest version 4 | // from './mv2' or './mv3' 5 | export { stateStorage } from './stateStorage.abstract'; 6 | -------------------------------------------------------------------------------- /src/background/stateStorage/mv2/index.ts: -------------------------------------------------------------------------------- 1 | export { stateStorage } from './stateStorage'; 2 | -------------------------------------------------------------------------------- /src/background/stateStorage/mv3/index.ts: -------------------------------------------------------------------------------- 1 | export { stateStorage } from './stateStorage'; 2 | -------------------------------------------------------------------------------- /src/background/stateStorage/stateStorage.abstract.ts: -------------------------------------------------------------------------------- 1 | import type { StorageKey } from '../schema'; 2 | 3 | export interface StateStorageInterface { 4 | getItem(key: StorageKey): T; 5 | 6 | setItem(key: StorageKey, value: T): void; 7 | 8 | init(): Promise; 9 | 10 | waitInit(): Promise; 11 | } 12 | 13 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 14 | const errorFunction = (arg?: unknown) => { 15 | throw new Error('Seems like webpack didn\'t inject proper State Storage implementation'); 16 | }; 17 | 18 | export const stateStorage: StateStorageInterface = (() => { 19 | return { 20 | init: errorFunction, 21 | getItem: errorFunction, 22 | setItem: errorFunction, 23 | waitInit: errorFunction, 24 | }; 25 | })(); 26 | -------------------------------------------------------------------------------- /src/background/timers/Mv2Timers.ts: -------------------------------------------------------------------------------- 1 | import { type TimersInterface } from './AbstractTimers'; 2 | 3 | /** 4 | * Implements timers interface for MV2. 5 | */ 6 | class Mv2Timers implements TimersInterface { 7 | setTimeout = (callback: () => void, timeout: number): number => { 8 | return setTimeout(callback, timeout) as any; // TODO setup tsconfig to fix types 9 | }; 10 | 11 | clearTimeout = (timerId: number): void => { 12 | clearTimeout(timerId); 13 | }; 14 | 15 | setInterval = (callback: () => void, interval: number): number => { 16 | return setInterval(callback, interval) as any; // TODO setup tsconfig to fix types 17 | }; 18 | 19 | clearInterval = (intervalId: number): void => { 20 | clearInterval(intervalId); 21 | }; 22 | } 23 | 24 | export const timers = new Mv2Timers(); 25 | -------------------------------------------------------------------------------- /src/background/timers/index.ts: -------------------------------------------------------------------------------- 1 | // !IMPORTANT! 2 | // export './AbstractTimers' will be replaced during webpack compilation 3 | // with NormalModuleReplacementPlugin to proper implementation 4 | // from './Mv2Timers' or './Mv3Timers' 5 | export { timers } from './AbstractTimers'; 6 | -------------------------------------------------------------------------------- /src/background/uninstall.ts: -------------------------------------------------------------------------------- 1 | import { forwarder } from './forwarder'; 2 | import { setExtensionUninstallUrl } from './helpers'; 3 | 4 | /** 5 | * Sets uninstall url for the extension. 6 | */ 7 | export const setUninstallUrl = async () => { 8 | const forwarderDomain = await forwarder.updateAndGetDomain(); 9 | await setExtensionUninstallUrl(forwarderDomain); 10 | }; 11 | -------------------------------------------------------------------------------- /src/common/components/DotsLoader/DotsLoader.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import './dots-loader.pcss'; 4 | 5 | export const DotsLoader = () => ( 6 |
7 | 8 | 9 | 10 |
11 | ); 12 | -------------------------------------------------------------------------------- /src/common/components/DotsLoader/index.ts: -------------------------------------------------------------------------------- 1 | export { DotsLoader } from './DotsLoader'; 2 | -------------------------------------------------------------------------------- /src/common/components/SearchHighlighter/highlighter.pcss: -------------------------------------------------------------------------------- 1 | .highlighter { 2 | /* set letter spacing for more accurate highlighting */ 3 | letter-spacing: 0.38px; 4 | 5 | &__search { 6 | background-color: rgba(213, 133, 0, 0.4); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/common/components/SearchHighlighter/index.ts: -------------------------------------------------------------------------------- 1 | export { SearchHighlighter } from './SearchHighlighter'; 2 | -------------------------------------------------------------------------------- /src/common/fetch-config.ts: -------------------------------------------------------------------------------- 1 | // TODO: use internal axios fetch adapter after they release it instead of @vespaiach/axios-fetch-adapter 2 | // https://github.com/axios/axios/pull/5146 3 | import fetchAdapter from '@vespaiach/axios-fetch-adapter'; 4 | 5 | // this variable is located in separate file to avoid side effects 6 | // which are caused by importing it to different places 7 | export const fetchConfig = { 8 | adapter: fetchAdapter, 9 | }; 10 | -------------------------------------------------------------------------------- /src/common/forwarderHelpers.ts: -------------------------------------------------------------------------------- 1 | import { AMO_EULA_URL, AMO_PRIVACY_URL, FORWARDER_URL_QUERIES } from '../background/config'; 2 | 3 | import { getForwarderUrl } from './helpers'; 4 | import { Prefs } from './prefs'; 5 | 6 | /** 7 | * Returns URLs for Privacy Policy and EULA based on the browser type. 8 | * 9 | * @param forwarderDomain Forwarder domain to use for generating URLs. 10 | * 11 | * @returns Privacy and EULA URLs. 12 | */ 13 | export const getPrivacyAndEulaUrls = (forwarderDomain: string) => { 14 | if (Prefs.isFirefox()) { 15 | return { 16 | privacyUrl: AMO_PRIVACY_URL, 17 | eulaUrl: AMO_EULA_URL, 18 | }; 19 | } 20 | 21 | return { 22 | privacyUrl: getForwarderUrl(forwarderDomain, FORWARDER_URL_QUERIES.PRIVACY), 23 | eulaUrl: getForwarderUrl(forwarderDomain, FORWARDER_URL_QUERIES.EULA), 24 | }; 25 | }; 26 | -------------------------------------------------------------------------------- /src/common/log-storage/index.ts: -------------------------------------------------------------------------------- 1 | export { logStorage } from './log-storage'; 2 | -------------------------------------------------------------------------------- /src/common/log-storage/storageProvider/LogStorageProvider.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines the contract for log storage providers. 3 | * 4 | * Implementations of this interface should provide mechanisms to set, retrieve, and clear logs. 5 | */ 6 | export interface LogStorageProvider { 7 | /** 8 | * Stores the provided logs. 9 | * 10 | * @param logs - The logs to store. 11 | * @returns A promise that resolves once the logs have been stored. 12 | */ 13 | set(logs: string[]): Promise; 14 | 15 | /** 16 | * Retrieves the stored logs. 17 | * 18 | * @returns A promise that resolves with the retrieved logs. 19 | */ 20 | get(): Promise; 21 | 22 | /** 23 | * Clears the stored logs. 24 | * 25 | * @returns A promise that resolves once the logs have been cleared. 26 | */ 27 | clear(): Promise; 28 | } 29 | -------------------------------------------------------------------------------- /src/common/log-storage/storageProvider/browserProvider/index.ts: -------------------------------------------------------------------------------- 1 | export { BrowserLogStorageProvider } from './BrowserLogStorageProvider'; 2 | -------------------------------------------------------------------------------- /src/common/log-storage/storageProvider/browserProvider/logsSchema.ts: -------------------------------------------------------------------------------- 1 | import zod from 'zod'; 2 | 3 | export const logsValidator = zod.array(zod.string()).default([]); 4 | 5 | /** 6 | * Describes logs format 7 | */ 8 | export type Logs = zod.infer; 9 | -------------------------------------------------------------------------------- /src/common/log-storage/storageProvider/memoryProvider/index.ts: -------------------------------------------------------------------------------- 1 | export { MemoryLogStorageProvider } from './MemoryLogStorageProvider'; 2 | -------------------------------------------------------------------------------- /src/common/reactTranslator.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { translate, type I18nInterface } from '@adguard/translate'; 4 | 5 | import { i18n } from './i18n'; 6 | 7 | /** 8 | * Retrieves localized messages by key, formats and converts into react components or string 9 | */ 10 | export const reactTranslator = translate.createReactTranslator(i18n, React); 11 | -------------------------------------------------------------------------------- /src/common/schema/endpoints/vpnInfo.ts: -------------------------------------------------------------------------------- 1 | import zod from 'zod'; 2 | 3 | export const VpnInfoScheme = zod.object({ 4 | bandwidthFreeMbits: zod.number().optional(), 5 | premiumPromoPage: zod.string().optional(), 6 | premiumPromoEnabled: zod.boolean().optional(), 7 | refreshTokens: zod.boolean().optional(), 8 | vpnFailurePage: zod.string().optional(), 9 | usedDownloadedBytes: zod.number().optional(), 10 | usedUploadedBytes: zod.number().optional(), 11 | maxDownloadedBytes: zod.number().optional(), 12 | maxUploadedBytes: zod.number().optional(), 13 | renewalTrafficDate: zod.string().optional(), 14 | maxDevicesCount: zod.number().optional(), 15 | emailConfirmationRequired: zod.boolean().optional(), 16 | }).strict(); 17 | 18 | export type VpnExtensionInfoInterface = zod.infer; 19 | -------------------------------------------------------------------------------- /src/common/styles/media.pcss: -------------------------------------------------------------------------------- 1 | /* 2 | WARNING: This file will be imported on every pcss file, do not 3 | include any styles here, only pcss utility should be written here. 4 | */ 5 | 6 | @custom-media --tablet (max-width: 875px); 7 | @custom-media --tablet-sm (max-width: 600px); 8 | @custom-media --mobile (max-width: 400px); 9 | 10 | @custom-media --touch-screen (hover: none); -------------------------------------------------------------------------------- /src/common/telemetry/index.ts: -------------------------------------------------------------------------------- 1 | export { TelemetryStore } from './TelemetryStore'; 2 | export { useTelemetryPageViewEvent } from './useTelemetryPageViewEvent'; 3 | -------------------------------------------------------------------------------- /src/common/translator.ts: -------------------------------------------------------------------------------- 1 | import { translate, type I18nInterface } from '@adguard/translate'; 2 | 3 | import { i18n } from './i18n'; 4 | 5 | /** 6 | * Retrieves localised message by key, formats it and converts into string 7 | */ 8 | export const translator = translate.createTranslator(i18n); 9 | -------------------------------------------------------------------------------- /src/common/utils/date.ts: -------------------------------------------------------------------------------- 1 | import { differenceInDays, parseISO } from 'date-fns'; 2 | 3 | /** 4 | * Returns the number of days between the current date and the renewal date. If the renewal date is in the past, 5 | * it returns 0. 6 | * 7 | * @param curDate - The current date. 8 | * @param renewalDate - The renewal date in ISO 8601 format (yyyy-MM-dd'T'HH:mm:ssZ). 9 | * @returns The number of days until the renewal date or 0 if the renewal date is in the past. 10 | */ 11 | export const daysToRenewal = (curDate: Date, renewalDate: string) => { 12 | const parsed = parseISO(renewalDate); 13 | const daysToRenewal = differenceInDays(parsed, curDate); 14 | return daysToRenewal >= 0 ? daysToRenewal : 0; 15 | }; 16 | -------------------------------------------------------------------------------- /src/consent/ConsentPage/index.ts: -------------------------------------------------------------------------------- 1 | export { ConsentPage } from './ConsentPage'; 2 | -------------------------------------------------------------------------------- /src/consent/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /src/consent/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Provider } from 'mobx-react'; 3 | 4 | import { createRoot } from 'react-dom/client'; 5 | 6 | import { translator } from '../common/translator'; 7 | 8 | import { ConsentPage } from './ConsentPage'; 9 | 10 | document.title = translator.getMessage('name'); 11 | 12 | const rootNode = document.getElementById('root')!; 13 | const root = createRoot(rootNode); 14 | 15 | root.render( 16 | 17 | 18 | , 19 | ); 20 | -------------------------------------------------------------------------------- /src/custom-protocol-handler/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | Custom protocol handler page 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/declaration.d.ts: -------------------------------------------------------------------------------- 1 | // Declare other types of files here if you want to import them in project as URL 2 | 3 | declare module '*.svg' { 4 | const content: string; 5 | export default content; 6 | } 7 | 8 | declare module '*.png' { 9 | const content: string; 10 | export default content; 11 | } 12 | 13 | declare module '*.webm' { 14 | const content: string; 15 | export default content; 16 | } 17 | -------------------------------------------------------------------------------- /src/export/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | AdGuard Export logs 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/export/index.ts: -------------------------------------------------------------------------------- 1 | import { exportLogs } from './export-logs'; 2 | 3 | (async () => { 4 | await exportLogs(); 5 | })(); 6 | -------------------------------------------------------------------------------- /src/offscreen/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | Offscreen document 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/offscreen/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | const myWorker = new Worker('worker.js'); 3 | 4 | /** 5 | * For more details on this worker see ProxyAuthTrigger. 6 | * We don't use standard chrome.runtime.onMessage event in order to avoid conflicts with other messages handlers. 7 | * When we receive a message from the service worker, we send it to the worker. 8 | */ 9 | navigator.serviceWorker.addEventListener('message', (e) => { 10 | // cant use logger since local storage is not available here 11 | console.log(`offscreen document received message: ${e.data}`); 12 | myWorker.postMessage(e.data); 13 | }); 14 | -------------------------------------------------------------------------------- /src/options/components/About/about.pcss: -------------------------------------------------------------------------------- 1 | .about { 2 | &__info { 3 | padding: 16px 16px 0; 4 | 5 | &-name { 6 | color: var(--text-main-text-main-default); 7 | font-size: 16px; 8 | font-weight: 600; 9 | line-height: 1.3; 10 | } 11 | 12 | &-version { 13 | color: var(--text-description-description-default); 14 | font-size: 14px; 15 | line-height: 1.3; 16 | } 17 | } 18 | 19 | &__copyright { 20 | padding: 8px 16px 24px; 21 | color: var(--text-description-description-default); 22 | font-size: 14px; 23 | line-height: 1.3; 24 | } 25 | 26 | &__link { 27 | text-decoration: none; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/options/components/About/index.ts: -------------------------------------------------------------------------------- 1 | export { About } from './About'; 2 | -------------------------------------------------------------------------------- /src/options/components/Account/Features/index.ts: -------------------------------------------------------------------------------- 1 | export { Features } from './Features'; 2 | -------------------------------------------------------------------------------- /src/options/components/Account/account.pcss: -------------------------------------------------------------------------------- 1 | .account { 2 | &__description { 3 | color: var(--text-main-text-main-default); 4 | } 5 | 6 | &__info { 7 | & + & { 8 | margin-top: 24px; 9 | } 10 | } 11 | 12 | &__actions { 13 | display: flex; 14 | flex-direction: column; 15 | margin-top: 24px; 16 | padding: 0 16px; 17 | row-gap: 16px; 18 | 19 | &--features { 20 | margin-top: 0; 21 | } 22 | } 23 | 24 | &__action { 25 | text-decoration: none; 26 | width: 260px; 27 | /* Overriding button's default font style */ 28 | font-size: 16px; 29 | font-weight: 600; 30 | 31 | @media (--mobile) { 32 | width: 100%; 33 | } 34 | } 35 | 36 | &__features { 37 | padding-top: 40px; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/options/components/Account/index.ts: -------------------------------------------------------------------------------- 1 | export { Account } from './Account'; 2 | -------------------------------------------------------------------------------- /src/options/components/App/index.ts: -------------------------------------------------------------------------------- 1 | export { App } from './App'; 2 | -------------------------------------------------------------------------------- /src/options/components/Exclusions/Actions/RemoveAllModal/index.ts: -------------------------------------------------------------------------------- 1 | export { RemoveAllModal } from './RemoveAllModal'; 2 | -------------------------------------------------------------------------------- /src/options/components/Exclusions/Actions/SelectListModal/index.ts: -------------------------------------------------------------------------------- 1 | export { SelectListModal } from './SelectListModal'; 2 | -------------------------------------------------------------------------------- /src/options/components/Exclusions/Actions/actions.pcss: -------------------------------------------------------------------------------- 1 | .actions { 2 | .select__btn { 3 | column-gap: 4px; 4 | color: var(--text-placeholder-placeholder-default); 5 | } 6 | 7 | .select__list { 8 | /* 9px is design specified value */ 9 | top: calc(100% + 9px); 10 | } 11 | 12 | .select__item { 13 | padding: 16px; 14 | } 15 | } -------------------------------------------------------------------------------- /src/options/components/Exclusions/Actions/index.ts: -------------------------------------------------------------------------------- 1 | export { Actions } from './Actions'; 2 | -------------------------------------------------------------------------------- /src/options/components/Exclusions/ChildrenList/ResetServiceModal/index.ts: -------------------------------------------------------------------------------- 1 | export { ResetServiceModal } from './ResetServiceModal'; 2 | -------------------------------------------------------------------------------- /src/options/components/Exclusions/ChildrenList/SubdomainModal/index.ts: -------------------------------------------------------------------------------- 1 | export { SubdomainModal } from './SubdomainModal'; 2 | -------------------------------------------------------------------------------- /src/options/components/Exclusions/ChildrenList/index.ts: -------------------------------------------------------------------------------- 1 | export { ChildrenList } from './ChildrenList'; 2 | -------------------------------------------------------------------------------- /src/options/components/Exclusions/Exclusion/index.ts: -------------------------------------------------------------------------------- 1 | export { Exclusion } from './Exclusion'; 2 | -------------------------------------------------------------------------------- /src/options/components/Exclusions/ExclusionsModal/AddExclusionsModal/ManualMode/index.ts: -------------------------------------------------------------------------------- 1 | export { ManualMode, MANUAL_FORM_ID } from './ManualMode'; 2 | -------------------------------------------------------------------------------- /src/options/components/Exclusions/ExclusionsModal/AddExclusionsModal/ManualMode/manual-mode.pcss: -------------------------------------------------------------------------------- 1 | .manual-mode { 2 | /* To match height of service mode */ 3 | height: 320px; 4 | } -------------------------------------------------------------------------------- /src/options/components/Exclusions/ExclusionsModal/AddExclusionsModal/ServiceMode/index.ts: -------------------------------------------------------------------------------- 1 | export { ServiceMode, SERVICE_FORM_ID } from './ServiceMode'; 2 | -------------------------------------------------------------------------------- /src/options/components/Exclusions/ExclusionsModal/AddExclusionsModal/index.ts: -------------------------------------------------------------------------------- 1 | export { AddExclusionModal } from './AddExclusionModal'; 2 | -------------------------------------------------------------------------------- /src/options/components/Exclusions/ExclusionsModal/ConfirmAddModal/index.ts: -------------------------------------------------------------------------------- 1 | export { ConfirmAddModal } from './ConfirmAddModal'; 2 | -------------------------------------------------------------------------------- /src/options/components/Exclusions/ExclusionsModal/index.ts: -------------------------------------------------------------------------------- 1 | export { AddExclusionModal } from './AddExclusionsModal'; 2 | export { ConfirmAddModal } from './ConfirmAddModal'; 3 | -------------------------------------------------------------------------------- /src/options/components/Exclusions/List/index.ts: -------------------------------------------------------------------------------- 1 | export { List } from './List'; 2 | -------------------------------------------------------------------------------- /src/options/components/Exclusions/Loader/Loader.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import { observer } from 'mobx-react'; 3 | 4 | import classnames from 'classnames'; 5 | 6 | import { rootStore } from '../../../stores'; 7 | import { Icon } from '../../ui/Icon'; 8 | 9 | import './loader.pcss'; 10 | 11 | export const Loader = observer(() => { 12 | const { exclusionsStore } = useContext(rootStore); 13 | 14 | const loaderClass = classnames('loader', { 15 | loader__visible: exclusionsStore.importingExclusions, 16 | }); 17 | 18 | return ( 19 |
20 |
21 | 22 |
23 | ); 24 | }); 25 | -------------------------------------------------------------------------------- /src/options/components/Exclusions/Loader/index.ts: -------------------------------------------------------------------------------- 1 | export { Loader } from './Loader'; 2 | -------------------------------------------------------------------------------- /src/options/components/Exclusions/ModeSelectorModal/index.ts: -------------------------------------------------------------------------------- 1 | export { ModeSelectorModal } from './ModeSelectorModal'; 2 | -------------------------------------------------------------------------------- /src/options/components/Exclusions/Search/ExclusionsSearch/index.ts: -------------------------------------------------------------------------------- 1 | export { ExclusionsSearch } from './ExclusionsSearch'; 2 | -------------------------------------------------------------------------------- /src/options/components/Exclusions/Search/ServicesSearch/index.ts: -------------------------------------------------------------------------------- 1 | export { ServicesSearch } from './ServicesSearch'; 2 | -------------------------------------------------------------------------------- /src/options/components/Exclusions/Search/index.ts: -------------------------------------------------------------------------------- 1 | export { ExclusionsSearch } from './ExclusionsSearch'; 2 | export { ServicesSearch } from './ServicesSearch'; 3 | -------------------------------------------------------------------------------- /src/options/components/Exclusions/index.ts: -------------------------------------------------------------------------------- 1 | export { Exclusions } from './Exclusions'; 2 | -------------------------------------------------------------------------------- /src/options/components/FreeGbs/index.ts: -------------------------------------------------------------------------------- 1 | export { FreeGbs } from './FreeGbs'; 2 | -------------------------------------------------------------------------------- /src/options/components/General/DnsSettings/index.ts: -------------------------------------------------------------------------------- 1 | export { DnsSettings } from './DnsSettings'; 2 | export { DnsSettingsButton } from './DnsSettingsButton'; 3 | -------------------------------------------------------------------------------- /src/options/components/General/WebRTC.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import { observer } from 'mobx-react'; 3 | 4 | import { rootStore } from '../../stores'; 5 | import { translator } from '../../../common/translator'; 6 | import { ControlsSwitch } from '../ui/Controls'; 7 | 8 | export const WebRTC = observer(() => { 9 | const { settingsStore } = useContext(rootStore); 10 | const { webRTCEnabled } = settingsStore; 11 | 12 | const handleToggle = async (): Promise => { 13 | await settingsStore.setWebRTCValue(!webRTCEnabled); 14 | }; 15 | 16 | return ( 17 | 23 | ); 24 | }); 25 | -------------------------------------------------------------------------------- /src/options/components/General/general.pcss: -------------------------------------------------------------------------------- 1 | .help-us-modal { 2 | .modal__header { 3 | padding-bottom: 0; 4 | } 5 | 6 | .modal__description { 7 | color: var(--text-main-text-main-default); 8 | } 9 | 10 | &__text { 11 | margin-top: 0; 12 | margin-bottom: 1em; 13 | } 14 | 15 | &__list { 16 | margin-bottom: 1em; 17 | padding-left: 1.5em; 18 | } 19 | 20 | /* To increase selector specificity we add .link */ 21 | &__link.link { 22 | text-decoration: none; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/options/components/General/index.ts: -------------------------------------------------------------------------------- 1 | export { General } from './General'; 2 | -------------------------------------------------------------------------------- /src/options/components/Preloader/Preloader.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import { observer } from 'mobx-react'; 3 | 4 | import { rootStore } from '../../stores'; 5 | import { RequestStatus } from '../../stores/consts'; 6 | 7 | import './preloader.pcss'; 8 | 9 | export const Preloader = observer(() => { 10 | const { authStore, globalStore } = useContext(rootStore); 11 | 12 | const isOpen = globalStore.status === RequestStatus.Pending 13 | || authStore.requestProcessState === RequestStatus.Pending; 14 | 15 | if (!isOpen) { 16 | return null; 17 | } 18 | 19 | return ( 20 |
21 |
22 |
23 | ); 24 | }); 25 | -------------------------------------------------------------------------------- /src/options/components/Preloader/index.ts: -------------------------------------------------------------------------------- 1 | export { Preloader } from './Preloader'; 2 | -------------------------------------------------------------------------------- /src/options/components/Preloader/preloader.pcss: -------------------------------------------------------------------------------- 1 | @keyframes preloader { 2 | 0% { 3 | transform: rotate(0deg); 4 | } 5 | 6 | 100% { 7 | transform: rotate(360deg); 8 | } 9 | } 10 | 11 | .preloader { 12 | z-index: var(--preloader-z); 13 | position: fixed; 14 | display: flex; 15 | align-items: center; 16 | justify-content: center; 17 | outline: none; 18 | width: 100%; 19 | height: 100%; 20 | top: 0; 21 | left: 0; 22 | background-color: var(--fills-backgrounds-page-background-default); 23 | 24 | &__in { 25 | width: 40px; 26 | height: 40px; 27 | background-repeat: no-repeat; 28 | background-position: 50%; 29 | background-image: url("../../../assets/images/preloader.svg"); 30 | animation: preloader 2s linear infinite; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/options/components/Sidebar/Rate/index.ts: -------------------------------------------------------------------------------- 1 | export { Rate } from './Rate'; 2 | -------------------------------------------------------------------------------- /src/options/components/Sidebar/index.ts: -------------------------------------------------------------------------------- 1 | export { Sidebar } from './Sidebar'; 2 | -------------------------------------------------------------------------------- /src/options/components/SignedOut/index.ts: -------------------------------------------------------------------------------- 1 | export { SignedOut } from './SignedOut'; 2 | -------------------------------------------------------------------------------- /src/options/components/Support/BugReporter/index.ts: -------------------------------------------------------------------------------- 1 | export { BugReporter } from './BugReporter'; 2 | -------------------------------------------------------------------------------- /src/options/components/Support/index.ts: -------------------------------------------------------------------------------- 1 | export { Support } from './Support'; 2 | -------------------------------------------------------------------------------- /src/options/components/Support/support.pcss: -------------------------------------------------------------------------------- 1 | .support { 2 | &__icon { 3 | color: var(--stroke-icons-product-icon-default); 4 | } 5 | 6 | &__btn-icon { 7 | transform: rotate(-90deg); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/options/components/ui/Button/index.ts: -------------------------------------------------------------------------------- 1 | export { Button } from './Button'; 2 | -------------------------------------------------------------------------------- /src/options/components/ui/Checkbox/checkbox.pcss: -------------------------------------------------------------------------------- 1 | .checkbox { 2 | display: flex; 3 | align-items: flex-start; 4 | justify-content: flex-start; 5 | cursor: pointer; 6 | 7 | &__icon { 8 | flex-shrink: 0; 9 | 10 | .checkbox--active & { 11 | color: var(--stroke-icons-product-icon-default); 12 | } 13 | } 14 | 15 | &__input { 16 | appearance: none; 17 | margin: 0; 18 | } 19 | 20 | &__label { 21 | flex: 1 1 auto; 22 | color: var(--text-main-text-main-default); 23 | font-size: 16px; 24 | line-height: 1.5; 25 | margin-left: 16px; 26 | } 27 | } -------------------------------------------------------------------------------- /src/options/components/ui/Checkbox/index.ts: -------------------------------------------------------------------------------- 1 | export { Checkbox } from './Checkbox'; 2 | -------------------------------------------------------------------------------- /src/options/components/ui/Controls/index.ts: -------------------------------------------------------------------------------- 1 | export { Controls } from './Controls'; 2 | export { ControlsSelect } from './ControlsSelect'; 3 | export { ControlsSwitch } from './ControlsSwitch'; 4 | -------------------------------------------------------------------------------- /src/options/components/ui/Icon/Icon.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import classNames from 'classnames'; 4 | 5 | /** 6 | * Icon component props. 7 | */ 8 | export interface IconProps { 9 | /** 10 | * The name of the icon to display. 11 | * 12 | * Full list of available icons can be found in the `Icons.tsx` file. 13 | */ 14 | name: string; 15 | 16 | /** 17 | * Additional class name. 18 | */ 19 | className?: string; 20 | } 21 | 22 | export function Icon({ name, className }: IconProps) { 23 | const classes = classNames( 24 | 'icon', 25 | className, 26 | ); 27 | 28 | return ( 29 | 30 | 31 | 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/options/components/ui/Icon/index.ts: -------------------------------------------------------------------------------- 1 | export { Icons } from './Icons'; 2 | export { Icon } from './Icon'; 3 | export { IconButton, type IconButtonProps } from './IconButton'; 4 | -------------------------------------------------------------------------------- /src/options/components/ui/Input/index.ts: -------------------------------------------------------------------------------- 1 | export { Input } from './Input'; 2 | export { TextArea } from './TextArea'; 3 | -------------------------------------------------------------------------------- /src/options/components/ui/Modal/index.ts: -------------------------------------------------------------------------------- 1 | export { Modal } from './Modal'; 2 | -------------------------------------------------------------------------------- /src/options/components/ui/Notifications/index.ts: -------------------------------------------------------------------------------- 1 | export { Notifications } from './Notifications'; 2 | -------------------------------------------------------------------------------- /src/options/components/ui/Radio/index.ts: -------------------------------------------------------------------------------- 1 | export { Radio } from './Radio'; 2 | -------------------------------------------------------------------------------- /src/options/components/ui/ReactPortal.tsx: -------------------------------------------------------------------------------- 1 | import { type PropsWithChildren } from 'react'; 2 | import { createPortal } from 'react-dom'; 3 | 4 | export function ReactPortal({ children }: PropsWithChildren) { 5 | return createPortal(children, document.body); 6 | } 7 | -------------------------------------------------------------------------------- /src/options/components/ui/Select/index.ts: -------------------------------------------------------------------------------- 1 | export { Select, type SelectProps } from './Select'; 2 | -------------------------------------------------------------------------------- /src/options/components/ui/Switch/index.ts: -------------------------------------------------------------------------------- 1 | export { Switch, type SwitchProps } from './Switch'; 2 | -------------------------------------------------------------------------------- /src/options/components/ui/Title/index.ts: -------------------------------------------------------------------------------- 1 | export { Title } from './Title'; 2 | -------------------------------------------------------------------------------- /src/options/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /src/options/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Provider } from 'mobx-react'; 3 | 4 | import { createRoot } from 'react-dom/client'; 5 | 6 | import { translator } from '../common/translator'; 7 | 8 | import { App } from './components/App'; 9 | 10 | document.title = translator.getMessage('options_title'); 11 | 12 | const rootNode = document.getElementById('root')!; 13 | const root = createRoot(rootNode); 14 | 15 | // TODO add fallback screen with possibilities to export logs 16 | root.render( 17 | 18 | 19 | , 20 | ); 21 | -------------------------------------------------------------------------------- /src/options/react.d.ts: -------------------------------------------------------------------------------- 1 | import { type AriaAttributes, type DOMAttributes } from 'react'; 2 | 3 | declare module 'react' { 4 | interface HTMLAttributes extends AriaAttributes, DOMAttributes { 5 | /** 6 | * Indicates that the browser will ignore this element and its descendants, 7 | * preventing some interactions and hiding it from assistive technology. 8 | * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/inert 9 | * @see https://github.com/facebook/react/pull/24730 10 | * 11 | * TODO: Remove this stub declaration after updating react to newer version. 12 | */ 13 | inert?: ''; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/options/stores/NotificationsStore/ErrorNotification.ts: -------------------------------------------------------------------------------- 1 | import { Notification } from './Notification'; 2 | 3 | export class ErrorNotification extends Notification { 4 | isError() { 5 | return true; 6 | } 7 | 8 | isSuccess() { 9 | return false; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/options/stores/NotificationsStore/Notification.ts: -------------------------------------------------------------------------------- 1 | import { type ReactNode } from 'react'; 2 | 3 | import { nanoid } from 'nanoid'; 4 | 5 | export interface Action { 6 | action: ReactNode | string, 7 | handler: () => void, 8 | } 9 | 10 | export class Notification { 11 | id: string; 12 | 13 | message: ReactNode | string; 14 | 15 | action?: Action; 16 | 17 | constructor(message: ReactNode | string, action?: Action) { 18 | this.id = nanoid(); 19 | this.message = message; 20 | this.action = action; 21 | } 22 | 23 | isError(): boolean { 24 | throw new Error('Not defined yet'); 25 | } 26 | 27 | isSuccess(): boolean { 28 | throw new Error('Not defined yet'); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/options/stores/NotificationsStore/SuccessNotification.ts: -------------------------------------------------------------------------------- 1 | import { Notification } from './Notification'; 2 | 3 | export class SuccessNotification extends Notification { 4 | isError() { 5 | return false; 6 | } 7 | 8 | isSuccess() { 9 | return true; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/options/stores/NotificationsStore/index.ts: -------------------------------------------------------------------------------- 1 | export { NotificationsStore } from './NotificationsStore'; 2 | -------------------------------------------------------------------------------- /src/options/stores/consts.ts: -------------------------------------------------------------------------------- 1 | // TODO: rename file to constants.ts 2 | export enum RequestStatus { 3 | Done = 'done', 4 | Pending = 'pending', 5 | Error = 'error', 6 | } 7 | 8 | export const COMPLETE_TASK_BONUS_GB = 1; 9 | -------------------------------------------------------------------------------- /src/options/stores/index.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | import { configure } from 'mobx'; 4 | 5 | import { RootStore } from './RootStore'; 6 | 7 | // Do not allow property change outside of store actions 8 | configure({ enforceActions: 'observed' }); 9 | 10 | export const rootStore = createContext(new RootStore()); 11 | -------------------------------------------------------------------------------- /src/options/styles/fonts.pcss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "Roboto"; 3 | src: url("../../assets/fonts/roboto/regular.woff2") format("woff2"), url("../../assets/fonts/roboto/regular.woff") format("woff"); 4 | font-weight: 400; 5 | font-style: normal; 6 | } 7 | 8 | @font-face { 9 | font-family: "Roboto"; 10 | src: url("../../assets/fonts/roboto/medium.woff2") format("woff2"), url("../../assets/fonts/roboto/medium.woff") format("woff"); 11 | font-weight: 500; 12 | font-style: normal; 13 | } 14 | 15 | @font-face { 16 | font-family: "Roboto"; 17 | src: url("../../assets/fonts/roboto/bold.woff2") format("woff2"), url("../../assets/fonts/roboto/bold.woff") format("woff"); 18 | font-weight: 700; 19 | font-style: normal; 20 | } 21 | -------------------------------------------------------------------------------- /src/options/styles/main.pcss: -------------------------------------------------------------------------------- 1 | @import "vars.pcss"; 2 | @import "fonts.pcss"; 3 | @import "common.pcss"; 4 | -------------------------------------------------------------------------------- /src/popup/actions/popupActions/index.ts: -------------------------------------------------------------------------------- 1 | import { tabs } from '../../../background/tabs'; 2 | import { messenger } from '../../../common/messenger'; 3 | 4 | class PopupActions { 5 | openTab = async (url: string): Promise => { 6 | await tabs.openTab(url); 7 | window.close(); 8 | }; 9 | 10 | openVpnFailurePage = async (): Promise => { 11 | const vpnFailurePage = await messenger.getVpnFailurePage(); 12 | await this.openTab(vpnFailurePage); 13 | }; 14 | 15 | openFreeGbsPage = async (): Promise => { 16 | await messenger.openFreeGbsPage(); 17 | window.close(); 18 | }; 19 | } 20 | 21 | export const popupActions = new PopupActions(); 22 | -------------------------------------------------------------------------------- /src/popup/components/App/index.ts: -------------------------------------------------------------------------------- 1 | export { App } from './App'; 2 | -------------------------------------------------------------------------------- /src/popup/components/Authentication/Authorization/index.ts: -------------------------------------------------------------------------------- 1 | export { Authorization } from './Authorization'; 2 | -------------------------------------------------------------------------------- /src/popup/components/Authentication/Checkbox/index.ts: -------------------------------------------------------------------------------- 1 | export { Checkbox } from './Checkbox'; 2 | -------------------------------------------------------------------------------- /src/popup/components/Authentication/ConfirmEmail/index.ts: -------------------------------------------------------------------------------- 1 | export { ConfirmEmail } from './ConfirmEmail'; 2 | -------------------------------------------------------------------------------- /src/popup/components/Authentication/EmailAuth/index.ts: -------------------------------------------------------------------------------- 1 | export { EmailAuth } from './EmailAuth'; 2 | -------------------------------------------------------------------------------- /src/popup/components/Authentication/Newsletter/index.ts: -------------------------------------------------------------------------------- 1 | export { Newsletter } from './Newsletter'; 2 | -------------------------------------------------------------------------------- /src/popup/components/Authentication/Onboarding/index.ts: -------------------------------------------------------------------------------- 1 | export { Onboarding } from './Onboarding'; 2 | -------------------------------------------------------------------------------- /src/popup/components/Authentication/Onboarding/onboarding.pcss: -------------------------------------------------------------------------------- 1 | .onboarding { 2 | flex: 1 1 auto; 3 | text-align: center; 4 | padding: 40px 0; 5 | height: 100%; 6 | } 7 | -------------------------------------------------------------------------------- /src/popup/components/Authentication/PolicyAgreement/index.ts: -------------------------------------------------------------------------------- 1 | export { PolicyAgreement } from './PolicyAgreement'; 2 | -------------------------------------------------------------------------------- /src/popup/components/Authentication/RegistrationForm/index.ts: -------------------------------------------------------------------------------- 1 | export { RegistrationForm } from './RegistrationForm'; 2 | -------------------------------------------------------------------------------- /src/popup/components/Authentication/SignInForm/index.ts: -------------------------------------------------------------------------------- 1 | export { SignInForm } from './SignInForm'; 2 | -------------------------------------------------------------------------------- /src/popup/components/Authentication/Submit.tsx: -------------------------------------------------------------------------------- 1 | import React, { type ReactNode } from 'react'; 2 | 3 | import { DotsLoader } from '../../../common/components/DotsLoader'; 4 | 5 | type SubmitProps = { 6 | processing: boolean, 7 | disabled?: boolean, 8 | text: string | ReactNode, 9 | }; 10 | 11 | export const Submit = ({ 12 | processing, disabled, text, 13 | }: SubmitProps) => { 14 | if (processing) { 15 | return ; 16 | } 17 | 18 | return ( 19 | 26 | ); 27 | }; 28 | -------------------------------------------------------------------------------- /src/popup/components/Authentication/TwoFactorForm/index.ts: -------------------------------------------------------------------------------- 1 | export { TwoFactorForm } from './TwoFactorForm'; 2 | -------------------------------------------------------------------------------- /src/popup/components/Authentication/UpgradeScreen/index.ts: -------------------------------------------------------------------------------- 1 | export { UpgradeScreen } from './UpgradeScreen'; 2 | -------------------------------------------------------------------------------- /src/popup/components/Authentication/index.ts: -------------------------------------------------------------------------------- 1 | export { Authentication } from './Authentication'; 2 | -------------------------------------------------------------------------------- /src/popup/components/ConnectionsLimitError/index.ts: -------------------------------------------------------------------------------- 1 | export { ConnectionsLimitError } from './ConnectionsLimitError'; 2 | -------------------------------------------------------------------------------- /src/popup/components/ExtraOptions/Option.tsx: -------------------------------------------------------------------------------- 1 | import React, { type ReactNode } from 'react'; 2 | 3 | import classnames from 'classnames'; 4 | 5 | type OptionProps = { 6 | handler: () => void, 7 | text: string | ReactNode, 8 | addClassName: string | null, 9 | }; 10 | 11 | export const Option = ({ handler, text, addClassName }: OptionProps) => { 12 | const optionClasses = classnames( 13 | 'button button--inline extra-options__item', 14 | { [`${addClassName}`]: !!addClassName }, 15 | ); 16 | 17 | return ( 18 | 25 | ); 26 | }; 27 | -------------------------------------------------------------------------------- /src/popup/components/ExtraOptions/index.ts: -------------------------------------------------------------------------------- 1 | export { ExtraOptions } from './ExtraOptions'; 2 | -------------------------------------------------------------------------------- /src/popup/components/GlobalError/index.ts: -------------------------------------------------------------------------------- 1 | export { GlobalError } from './GlobalError'; 2 | -------------------------------------------------------------------------------- /src/popup/components/Header/index.ts: -------------------------------------------------------------------------------- 1 | export { Header, HeaderScreenShot } from './Header'; 2 | -------------------------------------------------------------------------------- /src/popup/components/HostPermissionsError/index.ts: -------------------------------------------------------------------------------- 1 | export { HostPermissionsError } from './HostPermissionsError'; 2 | -------------------------------------------------------------------------------- /src/popup/components/InfoMessage/index.ts: -------------------------------------------------------------------------------- 1 | export { InfoMessage } from './InfoMessage'; 2 | export { FeedbackMessage } from './FeedbackMessage'; 3 | -------------------------------------------------------------------------------- /src/popup/components/LimitedOfferModal/LimitedOfferModal.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import { observer } from 'mobx-react'; 3 | 4 | import { rootStore } from '../../stores'; 5 | 6 | import { LimitedOfferNotice } from './LimitedOfferNotice'; 7 | import { LimitedOfferDetails } from './LimitedOfferDetails'; 8 | 9 | export const LimitedOfferModal = observer(() => { 10 | const { uiStore } = useContext(rootStore); 11 | 12 | const { shouldShowLimitedOfferNotice, shouldShowLimitedOfferDetails } = uiStore; 13 | 14 | return ( 15 | <> 16 | {shouldShowLimitedOfferNotice && } 17 | {shouldShowLimitedOfferDetails && } 18 | 19 | ); 20 | }); 21 | -------------------------------------------------------------------------------- /src/popup/components/LimitedOfferModal/index.ts: -------------------------------------------------------------------------------- 1 | export { LimitedOfferModal } from './LimitedOfferModal'; 2 | -------------------------------------------------------------------------------- /src/popup/components/Locations/FastestSkeleton.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { SkeletonEndpoint } from '../ui/SkeletonEndpoint'; 4 | 5 | /** 6 | * Component for the fastest locations skeleton, used when the fastest locations are loading. 7 | * 8 | * @returns Fastest locations skeleton component. 9 | */ 10 | export const FastestSkeleton = () => { 11 | return ( 12 |
13 | 14 | 15 | 16 |
17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /src/popup/components/Locations/index.ts: -------------------------------------------------------------------------------- 1 | export { Locations } from './Locations'; 2 | export { getFlagIconStyle } from './Location'; 3 | -------------------------------------------------------------------------------- /src/popup/components/MobileEdgePromo/index.ts: -------------------------------------------------------------------------------- 1 | export { MobileEdgePromo } from './MobileEdgePromo'; 2 | -------------------------------------------------------------------------------- /src/popup/components/NoLocationsError/index.ts: -------------------------------------------------------------------------------- 1 | export { NoLocationsError } from './NoLocationsError'; 2 | -------------------------------------------------------------------------------- /src/popup/components/Ping/index.ts: -------------------------------------------------------------------------------- 1 | export { Ping } from './Ping'; 2 | -------------------------------------------------------------------------------- /src/popup/components/Ping/ping.pcss: -------------------------------------------------------------------------------- 1 | .ping { 2 | margin-left: auto; 3 | margin-right: 0; 4 | font-size: 16px; 5 | line-height: 20px; 6 | text-align: right; 7 | 8 | &--success { 9 | color: var(--text-links-link-default); 10 | } 11 | 12 | &--warning { 13 | color: var(--text-links-attention-link-default); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/popup/components/PingDotsLoader/PingDotsLoader.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import './ping-dots-loader.pcss'; 4 | 5 | export const PingDotsLoader = () => ( 6 |
7 | 8 | 9 | 10 |
11 | ); 12 | -------------------------------------------------------------------------------- /src/popup/components/PingDotsLoader/index.ts: -------------------------------------------------------------------------------- 1 | export { PingDotsLoader } from './PingDotsLoader'; 2 | -------------------------------------------------------------------------------- /src/popup/components/PromoNotificationModal/index.ts: -------------------------------------------------------------------------------- 1 | export { PromoNotificationModal } from './PromoNotificationModal'; 2 | -------------------------------------------------------------------------------- /src/popup/components/RatePopup/index.ts: -------------------------------------------------------------------------------- 1 | export { RatePopup } from './RatePopup'; 2 | -------------------------------------------------------------------------------- /src/popup/components/ReviewPopup/ReviewPopup.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { RateModal } from './RateModal'; 4 | import { ConfirmRateModal } from './ConfirmRateModal'; 5 | 6 | export const ReviewPopup = () => { 7 | return ( 8 | <> 9 | 10 | 11 | 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /src/popup/components/ReviewPopup/constants.ts: -------------------------------------------------------------------------------- 1 | import ninjaImageUrl1 from '../../../assets/images/rate/ninja1.svg'; 2 | import ninjaImageUrl2 from '../../../assets/images/rate/ninja2.svg'; 3 | import ninjaImageUrl3 from '../../../assets/images/rate/ninja3.svg'; 4 | import ninjaImageUrl4 from '../../../assets/images/rate/ninja4.svg'; 5 | import ninjaImageUrl5 from '../../../assets/images/rate/ninja5.svg'; 6 | 7 | export const RATING_IMAGES_MAP: { [key:number]: string } = { 8 | 1: ninjaImageUrl1, 9 | 2: ninjaImageUrl2, 10 | 3: ninjaImageUrl3, 11 | 4: ninjaImageUrl4, 12 | 5: ninjaImageUrl5, 13 | }; 14 | -------------------------------------------------------------------------------- /src/popup/components/ReviewPopup/index.ts: -------------------------------------------------------------------------------- 1 | export { ReviewPopup } from './ReviewPopup'; 2 | -------------------------------------------------------------------------------- /src/popup/components/ServerErrorPopup/index.ts: -------------------------------------------------------------------------------- 1 | export { ServerErrorPopup } from './ServerErrorPopup'; 2 | -------------------------------------------------------------------------------- /src/popup/components/Settings/BackgroundAnimation/index.ts: -------------------------------------------------------------------------------- 1 | export { BackgroundAnimation } from './BackgroundAnimation'; 2 | -------------------------------------------------------------------------------- /src/popup/components/Settings/CurrentEndpoint/index.ts: -------------------------------------------------------------------------------- 1 | export { CurrentEndpoint, CurrentEndpointScreenShot } from './CurrentEndpoint'; 2 | -------------------------------------------------------------------------------- /src/popup/components/Settings/ExclusionsScreen/index.ts: -------------------------------------------------------------------------------- 1 | export { ExclusionsScreen } from './ExclusionsScreen'; 2 | -------------------------------------------------------------------------------- /src/popup/components/Settings/GlobalControl/ExcludeSite/exclude-site.pcss: -------------------------------------------------------------------------------- 1 | .exclude-site-wrapper { 2 | position: relative; 3 | 4 | .button--highlighted-hint { 5 | position: relative; 6 | z-index: var(--exclude-site-hint-overlay-z-index); 7 | 8 | &:after { 9 | content: ''; 10 | position: absolute; 11 | top: 0; 12 | left: 0; 13 | width: 100%; 14 | height: 100%; 15 | border-radius: 8px; 16 | box-shadow: 0px 0px 0px 9999px var(--exclude-site-hint-overlay); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/popup/components/Settings/GlobalControl/ExcludeSite/index.ts: -------------------------------------------------------------------------------- 1 | export { ExcludeSite } from './ExcludeSite'; 2 | -------------------------------------------------------------------------------- /src/popup/components/Settings/GlobalControl/HintPopup/hint-popup.pcss: -------------------------------------------------------------------------------- 1 | .hint-popup { 2 | width: 220px; 3 | border-radius: 8px; 4 | margin: auto; 5 | padding: 15px; 6 | background-color: var(--white); 7 | text-align: left; 8 | position: absolute; 9 | left: 0; 10 | right: 0; 11 | top: 0; 12 | transform: translateY(-100%); 13 | z-index: var(--exclude-site-hint-overlay-z-index); 14 | 15 | &:after { 16 | content: "▼"; 17 | color: var(--white); 18 | display: block; 19 | position: absolute; 20 | bottom: -17px; 21 | right: 47%; 22 | font-size: 23px; 23 | } 24 | 25 | &__content { 26 | font-size: 14px; 27 | } 28 | 29 | &__button { 30 | margin-top: 15px; 31 | 32 | &:hover { 33 | text-decoration: underline; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/popup/components/Settings/GlobalControl/HintPopup/index.ts: -------------------------------------------------------------------------------- 1 | export { HintPopup } from './HintPopup'; 2 | -------------------------------------------------------------------------------- /src/popup/components/Settings/GlobalControl/index.ts: -------------------------------------------------------------------------------- 1 | export { GlobalControl } from './GlobalControl'; 2 | -------------------------------------------------------------------------------- /src/popup/components/Settings/SiteInfo/Info.tsx: -------------------------------------------------------------------------------- 1 | import React, { type ReactNode } from 'react'; 2 | 3 | type InfoProps = { 4 | title: string, 5 | status: string | ReactNode, 6 | children?: ReactNode, 7 | }; 8 | 9 | export const Info = ({ title, status, children }: InfoProps) => ( 10 |
11 |
12 | {title} 13 |
14 |
15 | {status} 16 |
17 | {children} 18 |
19 | ); 20 | -------------------------------------------------------------------------------- /src/popup/components/Settings/SiteInfo/index.ts: -------------------------------------------------------------------------------- 1 | export { SiteInfo } from './SiteInfo'; 2 | -------------------------------------------------------------------------------- /src/popup/components/Settings/SiteInfo/site-info.pcss: -------------------------------------------------------------------------------- 1 | .site-info { 2 | padding: 0; 3 | max-width: 100%; 4 | height: auto; 5 | margin-top: 100px; 6 | 7 | &__title { 8 | margin: 0 auto 8px; 9 | font-size: 2rem; 10 | font-weight: 700; 11 | line-height: 1.1; 12 | color: var(--green76); 13 | max-width: 85%; 14 | overflow: hidden; 15 | white-space: nowrap; 16 | text-overflow: ellipsis; 17 | } 18 | 19 | &__status { 20 | font-size: 1.4rem; 21 | line-height: 1.3; 22 | color: #d8d8d8; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/popup/components/Settings/Status/index.ts: -------------------------------------------------------------------------------- 1 | export { Status } from './Status'; 2 | -------------------------------------------------------------------------------- /src/popup/components/Settings/Status/status.pcss: -------------------------------------------------------------------------------- 1 | .status { 2 | color: #fff; 3 | margin-bottom: 24px; 4 | font-size: 24px; 5 | font-weight: 700; 6 | line-height: 29px; 7 | 8 | &--skeleton { 9 | cursor: default; 10 | padding: 0 12px; 11 | } 12 | 13 | &--no-locations-error { 14 | color: var(--gray-base); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/popup/components/Settings/TrafficLimitExceeded/index.ts: -------------------------------------------------------------------------------- 1 | export { TrafficLimitExceeded } from './TrafficLimitExceeded'; 2 | -------------------------------------------------------------------------------- /src/popup/components/Settings/Warning/Warning.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import './warning.pcss'; 4 | 5 | type WarningProps = { 6 | desc: string | React.ReactNode, 7 | mod: string, 8 | }; 9 | 10 | export const Warning = ({ desc, mod }: WarningProps) => { 11 | return ( 12 |
13 | {desc} 14 |
15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /src/popup/components/Settings/Warning/index.ts: -------------------------------------------------------------------------------- 1 | export { Warning } from './Warning'; 2 | -------------------------------------------------------------------------------- /src/popup/components/Settings/Warning/warning.pcss: -------------------------------------------------------------------------------- 1 | .warning { 2 | padding: 25px 25px 25px 70px; 3 | position: relative; 4 | color: var(--gray-base); 5 | font-size: 16px; 6 | line-height: 22px; 7 | border-top: 1px solid var(--grayf3); 8 | 9 | &:after { 10 | content: ''; 11 | position: absolute; 12 | top: 27px; 13 | left: 25px; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/popup/components/Settings/index.ts: -------------------------------------------------------------------------------- 1 | export { Settings, SettingsScreenShot } from './Settings'; 2 | -------------------------------------------------------------------------------- /src/popup/components/SkeletonLoading/index.ts: -------------------------------------------------------------------------------- 1 | export { SkeletonLoading } from './SkeletonLoading'; 2 | -------------------------------------------------------------------------------- /src/popup/components/VpnBlockedError/VpnBlockedError.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import { observer } from 'mobx-react'; 3 | 4 | import { rootStore } from '../../stores'; 5 | 6 | import { VpnBlockedNotice } from './VpnBlockedNotice'; 7 | import { VpnBlockedDetails } from './VpnBlockedDetails'; 8 | 9 | export const VpnBlockedError = observer(() => { 10 | const { uiStore } = useContext(rootStore); 11 | 12 | const { isShownVpnBlockedErrorNotice, isShownVpnBlockedErrorDetails } = uiStore; 13 | 14 | return ( 15 | <> 16 | {isShownVpnBlockedErrorNotice && } 17 | {isShownVpnBlockedErrorDetails && } 18 | 19 | ); 20 | }); 21 | -------------------------------------------------------------------------------- /src/popup/components/VpnBlockedError/index.ts: -------------------------------------------------------------------------------- 1 | export { VpnBlockedError } from './VpnBlockedError'; 2 | -------------------------------------------------------------------------------- /src/popup/components/ui/CloseButton/CloseButton.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Icon } from '../Icon'; 4 | 5 | import './close-button.pcss'; 6 | 7 | type CloseButtonProps = { 8 | handler: () => void, 9 | }; 10 | 11 | export const CloseButton = ({ handler }: CloseButtonProps) => { 12 | const clickHandler = (): void => { 13 | handler(); 14 | }; 15 | 16 | return ( 17 |
18 | 25 |
26 | ); 27 | }; 28 | -------------------------------------------------------------------------------- /src/popup/components/ui/CloseButton/close-button.pcss: -------------------------------------------------------------------------------- 1 | .close-button { 2 | position: absolute; 3 | top: 17px; 4 | right: 13px; 5 | 6 | & button { 7 | background-color: transparent; 8 | border: none; 9 | cursor: pointer; 10 | } 11 | 12 | &__icon { 13 | width: 24px; 14 | height: 24px; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/popup/components/ui/CloseButton/index.ts: -------------------------------------------------------------------------------- 1 | export { CloseButton } from './CloseButton'; 2 | -------------------------------------------------------------------------------- /src/popup/components/ui/DotsIndicator/dots-indicator.pcss: -------------------------------------------------------------------------------- 1 | .dots-indicator { 2 | display: flex; 3 | flex-direction: row; 4 | align-items: center; 5 | justify-content: center; 6 | height: 30px; 7 | 8 | & button { 9 | display: inline-block; 10 | vertical-align: middle; 11 | background: none; 12 | border: none; 13 | height: 20px; 14 | cursor: pointer; 15 | padding: 5px; 16 | } 17 | 18 | &__dot { 19 | height: 7px; 20 | width: 7px; 21 | background-color: var(--grayd8); 22 | border-radius: 50%; 23 | border: none; 24 | padding: 0; 25 | cursor: pointer; 26 | 27 | &--active { 28 | background-color: var(--green76); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/popup/components/ui/DotsIndicator/index.ts: -------------------------------------------------------------------------------- 1 | export { DotsIndicator } from './DotsIndicator'; 2 | -------------------------------------------------------------------------------- /src/popup/components/ui/Icon.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import cn from 'classnames'; 4 | 5 | type IconProps = { 6 | icon: string, 7 | className: string, 8 | }; 9 | 10 | export const Icon = ({ icon, className }: IconProps) => { 11 | return ( 12 | 13 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /src/popup/components/ui/SkeletonEndpoint/SkeletonEndpoint.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import './skeleton-endpoint.pcss'; 4 | 5 | export const SkeletonEndpoint = () => { 6 | return ( 7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | ); 15 | }; 16 | -------------------------------------------------------------------------------- /src/popup/components/ui/SkeletonEndpoint/index.ts: -------------------------------------------------------------------------------- 1 | export { SkeletonEndpoint } from './SkeletonEndpoint'; 2 | -------------------------------------------------------------------------------- /src/popup/components/ui/SkeletonHeader/SkeletonHeader.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import '../../Header/header.pcss'; 4 | 5 | export const SkeletonHeader = () => { 6 | return ( 7 |
8 |
9 |
10 |
11 | 20 |
21 | ); 22 | }; 23 | -------------------------------------------------------------------------------- /src/popup/components/ui/SkeletonHeader/index.ts: -------------------------------------------------------------------------------- 1 | export { SkeletonHeader } from './SkeletonHeader'; 2 | -------------------------------------------------------------------------------- /src/popup/components/ui/Slider/index.ts: -------------------------------------------------------------------------------- 1 | export { Slider } from './Slider'; 2 | -------------------------------------------------------------------------------- /src/popup/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | Popup page 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/popup/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Provider } from 'mobx-react'; 3 | 4 | import { createRoot } from 'react-dom/client'; 5 | 6 | import { App } from './components/App'; 7 | 8 | import './styles/main.pcss'; 9 | 10 | const rootNode = document.getElementById('root')!; 11 | const root = createRoot(rootNode); 12 | 13 | root.render( 14 | 15 | 16 | , 17 | ); 18 | -------------------------------------------------------------------------------- /src/popup/stores/constants.ts: -------------------------------------------------------------------------------- 1 | export enum RequestStatus { 2 | Done = 'done', 3 | Pending = 'pending', 4 | Error = 'error', 5 | } 6 | 7 | export const MAX_GET_POPUP_DATA_ATTEMPTS = 5; 8 | 9 | export enum InputType { 10 | Text = 'text', 11 | Password = 'password', 12 | } 13 | 14 | export const RECALCULATE_PINGS_BTN_INACTIVITY_DELAY_MS = 2000; 15 | -------------------------------------------------------------------------------- /src/popup/stores/index.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | import { configure } from 'mobx'; 4 | 5 | import { RootStore } from './RootStore'; 6 | 7 | // Do not allow property change outside of store actions 8 | configure({ enforceActions: 'observed' }); 9 | 10 | export const rootStore = createContext(new RootStore()); 11 | -------------------------------------------------------------------------------- /src/popup/styles/animation.pcss: -------------------------------------------------------------------------------- 1 | .fade { 2 | &-enter { 3 | opacity: 0; 4 | } 5 | 6 | &-enter-active { 7 | opacity: 1; 8 | } 9 | 10 | &-exit { 11 | opacity: 1; 12 | } 13 | 14 | &-exit-active { 15 | opacity: 0; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/popup/styles/fonts.pcss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "Roboto"; 3 | src: url("../../assets/fonts/roboto/regular.woff2") format("woff2"), url("../../assets/fonts/roboto/regular.woff") format("woff"); 4 | font-weight: 400; 5 | font-style: normal; 6 | } 7 | 8 | @font-face { 9 | font-family: "Roboto"; 10 | src: url("../../assets/fonts/roboto/medium.woff2") format("woff2"), url("../../assets/fonts/roboto/medium.woff") format("woff"); 11 | font-weight: 500; 12 | font-style: normal; 13 | } 14 | 15 | @font-face { 16 | font-family: "Roboto"; 17 | src: url("../../assets/fonts/roboto/bold.woff2") format("woff2"), url("../../assets/fonts/roboto/bold.woff") format("woff"); 18 | font-weight: 700; 19 | font-style: normal; 20 | } 21 | -------------------------------------------------------------------------------- /src/popup/styles/main.pcss: -------------------------------------------------------------------------------- 1 | @import "vars.pcss"; 2 | @import "fonts.pcss"; 3 | @import "button.pcss"; 4 | @import "modal.pcss"; 5 | @import "common.pcss"; 6 | @import "animation.pcss"; 7 | @import "form.pcss"; 8 | -------------------------------------------------------------------------------- /src/worker/worker.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | /** 3 | * Worker receives url from the offscreen document and fetches it. 4 | * For more details on this worker read ProxyAuthTrigger. 5 | * @param e Message event 6 | */ 7 | onmessage = (e) => { 8 | // cant use logger since local storage is not available here 9 | console.log(`worker created by offscreen document received: ${e.data}`); 10 | fetch(e.data) 11 | .then(() => { 12 | console.log('url fetched successfully'); 13 | }) 14 | .catch((ex) => { 15 | console.log(`during fetching: ${e.data}, error occurred: ${ex}`); 16 | }); 17 | }; 18 | -------------------------------------------------------------------------------- /tasks/chrome/manifest.chrome.ts: -------------------------------------------------------------------------------- 1 | export const chromeManifestDiff = { 2 | manifest_version: 3, 3 | minimum_chrome_version: '109.0', 4 | options_page: 'options.html', 5 | background: { 6 | service_worker: 'background.js', 7 | }, 8 | action: { 9 | default_icon: { 10 | 19: 'assets/images/icons/disabled-19.png', 11 | 38: 'assets/images/icons/disabled-38.png', 12 | }, 13 | default_title: '__MSG_name__', 14 | default_popup: 'popup.html', 15 | }, 16 | content_security_policy: { 17 | extension_pages: "script-src 'self'; object-src 'self'", 18 | }, 19 | permissions: [ 20 | 'webRequestAuthProvider', 21 | 'offscreen', 22 | ], 23 | host_permissions: [ 24 | '', 25 | ], 26 | }; 27 | -------------------------------------------------------------------------------- /tasks/compile-protobuf.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | BANNER="/** 3 | * This file was automatically generated with protobufjs cli tool, see \"yarn compile-proto\" 4 | */" 5 | echo "$BANNER" > src/background/connectivity/protobufCompiled.js 6 | pbjs -t static-module -w es6 -l " eslint-disable " src/background/connectivity/connectivity.proto >> src/background/connectivity/protobufCompiled.js 7 | -------------------------------------------------------------------------------- /tasks/edge/manifest.edge.ts: -------------------------------------------------------------------------------- 1 | import { chromeManifestDiff } from '../chrome/manifest.chrome'; 2 | 3 | /** 4 | * Edge manifest diff. 5 | * 6 | * Basically the same as Chrome MV3. 7 | */ 8 | export const edgeManifestDiff = { 9 | ...chromeManifestDiff, 10 | }; 11 | -------------------------------------------------------------------------------- /tasks/opera/manifest.opera.ts: -------------------------------------------------------------------------------- 1 | export const operaManifestDiff = { 2 | minimum_opera_version: '66', 3 | manifest_version: 2, 4 | minimum_chrome_version: '66.0', 5 | options_page: 'options.html', 6 | browser_action: { 7 | default_icon: { 8 | 19: 'assets/images/icons/disabled-19.png', 9 | 38: 'assets/images/icons/disabled-38.png', 10 | }, 11 | default_title: '__MSG_name__', 12 | default_popup: 'popup.html', 13 | }, 14 | background: { 15 | page: 'background.html', 16 | persistent: true, 17 | }, 18 | permissions: [ 19 | '', 20 | 'webRequestBlocking', 21 | ], 22 | }; 23 | -------------------------------------------------------------------------------- /tasks/translations/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "twosky_config_path": "../../.twosky.json", 3 | "api_url": "https://twosky.int.agrd.dev/api/v1", 4 | "source_relative_path": "../../src", 5 | "supported_source_filename_extensions": [ 6 | ".js", 7 | ".jsx", 8 | ".tsx", 9 | ".ts" 10 | ], 11 | "persistent_messages": [ 12 | "name", 13 | "short_name", 14 | "description" 15 | ], 16 | "locales_relative_path": "../../src/_locales", 17 | "locales_data_format": "chrome", 18 | "locales_data_filename": "messages.json", 19 | "required_locales": [ 20 | "de", 21 | "fr", 22 | "it", 23 | "es", 24 | "pt_BR", 25 | "pt_PT", 26 | "ko", 27 | "ja", 28 | "ru", 29 | "zh_CN", 30 | "zh_TW" 31 | ], 32 | "threshold_percentage": 100 33 | } 34 | -------------------------------------------------------------------------------- /tests/__mocks__/browserApiMock.ts: -------------------------------------------------------------------------------- 1 | interface StorageInterface { 2 | [key: string]: any; 3 | set: jest.Mock, [key: string, data: any]>; 4 | get: jest.Mock, [key: string]>; 5 | remove: jest.Mock, [key: string]>; 6 | } 7 | 8 | const storage: StorageInterface = { 9 | set: jest.fn(async (key: string, data: any): Promise => { 10 | storage[key] = data; 11 | }), 12 | get: jest.fn(async (key: string): Promise => { 13 | return storage[key]; 14 | }), 15 | remove: jest.fn(async (key: string): Promise => { 16 | return delete storage[key]; 17 | }), 18 | }; 19 | 20 | const runtime = { 21 | // TODO: test mv3 after official switch to mv3 22 | isManifestVersion2: () => true, 23 | }; 24 | 25 | export const browserApi = { 26 | storage, 27 | runtime, 28 | }; 29 | -------------------------------------------------------------------------------- /tests/__mocks__/fetchMock.ts: -------------------------------------------------------------------------------- 1 | type ReturnData = { 2 | [key: string]: string | number | boolean | null; 3 | }; 4 | 5 | /** 6 | * Mocks fetch function to return a rejected Promise. 7 | */ 8 | export const fetchRejectMock = () => { 9 | global.fetch = jest.fn(() => Promise.reject()); 10 | }; 11 | 12 | /** 13 | * Mocks fetch function to return a resolved Promise with the provided JSON data. 14 | * @param data 15 | * @param ok 16 | */ 17 | export const fetchResolveMock = (data: ReturnData | Promise, ok = true) => { 18 | // @ts-ignore 19 | global.fetch = jest.fn(() => Promise.resolve({ 20 | ok, 21 | json: () => { 22 | return data; 23 | }, 24 | })); 25 | }; 26 | -------------------------------------------------------------------------------- /tests/__mocks__/fileMock.ts: -------------------------------------------------------------------------------- 1 | module.exports = 'test-file-stub'; 2 | -------------------------------------------------------------------------------- /tests/__mocks__/index.ts: -------------------------------------------------------------------------------- 1 | export * from './sessionMock'; 2 | -------------------------------------------------------------------------------- /tests/__mocks__/sessionMock.ts: -------------------------------------------------------------------------------- 1 | export const session: { [key: string]: any } = { 2 | set: jest.fn(async (key: string, data: any): Promise => { 3 | session[key] = data; 4 | }), 5 | get: jest.fn(async (key: string): Promise => { 6 | return session[key]; 7 | }), 8 | }; 9 | -------------------------------------------------------------------------------- /tests/__mocks__/styleMock.ts: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /tests/__setups__/chrome.ts: -------------------------------------------------------------------------------- 1 | const chrome = require('sinon-chrome'); 2 | 3 | if (!chrome.runtime.id) { 4 | chrome.runtime.id = 'test'; 5 | } 6 | 7 | declare global { 8 | namespace NodeJS { 9 | interface Global { 10 | chrome: any; 11 | } 12 | } 13 | } 14 | 15 | global.chrome = chrome; 16 | 17 | export {}; 18 | -------------------------------------------------------------------------------- /tests/__setups__/jest-setup.ts: -------------------------------------------------------------------------------- 1 | import '@testing-library/jest-dom'; 2 | -------------------------------------------------------------------------------- /tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": [ 4 | ".eslintrc.js", 5 | "**/*" 6 | ] 7 | } 8 | --------------------------------------------------------------------------------