diff --git a/.eslintrc.json b/.eslintrc.json index 5ec44a743..649e30e75 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -27,10 +27,10 @@ "version": "detect" }, "import/resolver": { - "@ps-analysis-tool/eslint-import-resolver": { + "@google-psat/eslint-import-resolver": { "mapping": { - "^@ps-analysis-tool\\/(.*)\\/(.*)": "./packages/$1/src/$2", - "^@ps-analysis-tool\\/(.*)": "./packages/$1/src/" + "^@google-psat\\/(.*)\\/(.*)": "./packages/$1/src/$2", + "^@google-psat\\/(.*)": "./packages/$1/src/" }, "extensions": [".js", ".jsx", ".ts", ".tsx"] }, diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 000000000..9ec2ddd55 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,26 @@ +name: Mark issues as stale. + +on: + schedule: + - cron: '44 20 * * *' + +jobs: + stale: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + + steps: + - uses: actions/stale@v5 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + days-before-stale: 30 + days-before-issue-close: 14 + exempt-all-milestones: true + only-labels: 'support' + stale-issue-message: 'This issue has been marked as stale because there has been no activity in the past 30 days.' + close-issue-message: 'This issue has been closed since there was no activity since it was marked as stale.' + stale-issue-label: 'stale' + remove-issue-stale-when-updated: true + labels-to-remove-when-unstale: 'stale' diff --git a/CHANGELOG.md b/CHANGELOG.md index 6748a3e0d..620e61674 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -356,4 +356,44 @@ * Miscellaneous CLI dashboard improvements https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/660 ## Others -* Feature: Update babel-loader webpack config to have caching support for faster builds https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/452 \ No newline at end of file +* Feature: Update babel-loader webpack config to have caching support for faster builds https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/452 + + +#v0.9.0 +## Extension +* Feature: Complete migration to CDP https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/539 +* Feature: Add and implement i18n translation locales https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/713 +* Feature: Navigate from PSAT to network panel https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/685 +* Feature: Add “Protected Audience” landing page https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/714 +* Feature: Add blocking direction icons in CLI https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/715 +* Fix: Persist filters after web page reload https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/692 +* Fix: Exemption section disappearing https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/691 +* Fix: GSI messaging on correct urls. https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/684 +* Fix: Inconsistent highlighting on livemint.com https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/683 +* Fix: Update menu bar scrolling logic https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/699 +* Add known breakages section in CLI https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/708 +* Fix: v1.0 QA issues https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/717 +* Fix: Conditionally use `I18n.getMessage` function inside `InfoCard` https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/720 +* Fix: Update UI/UX of table components https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/679 +* Fix: Update UX of design system components https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/693 + + +## CLI +* Upgrade to the latest Puppeteer version and fix cookie mapping issues https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/704 +* Update CLI messaging to be smaller and more concise https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/721 +* CLI: Refactor CLI package into 2 different packages https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/687 +* Feature: Publish packages on the NPM registry https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/698 +* Make -u argument optional in CLI. https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/711 +* Fix: Create separate CSV generation utils for CLI dashboard and extension https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/619 +* Add website hostname as prefix to cookie table CSV https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/700 +* Fix: Report throwing error when downloaded from CLI. https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/709 +* CLI: Add ability to catch exempted cookies https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/712 +* Fix: `Samesite` value in cookie table CSV https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/726 +* CLI: Fix missing frame key in the page frames hash map https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/728 +* Fix: Paths for CLI to work on global as well as local installation https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/727 + +## Others +* Add `chrome-pat` and `chrome-pat-ps` commands to test Private Advertising https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/706 +* Close support issues using stale actions. https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/571 +* Temporarily remove storybook package https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/702 +* Refactor: Update package scope name to `@google-psat` https://github.com/GoogleChromeLabs/ps-analysis-tool/pull/725 \ No newline at end of file diff --git a/assets/data/open-cookie-database.json b/assets/data/open-cookie-database.json index a8d3e1f7e..bccb9eab6 100644 --- a/assets/data/open-cookie-database.json +++ b/assets/data/open-cookie-database.json @@ -5,10 +5,10 @@ "category": "Functional", "name": "cookiePreferences", "domain": "", - "description": "Registers cookie preferences of a user", - "retention": "2 years", + "description": "OCD_cookiePreferences_description", + "retention": "OCD_retention_2_years", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -18,10 +18,10 @@ "category": "Analytics", "name": "td", "domain": "www.googletagmanager.com", - "description": "Registers statistical data on users' behaviour on the website. Used for internal analytics by the website operator.", - "retention": "session", + "description": "OCD_td_description", + "retention": "OCD_retention_session", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -31,8 +31,8 @@ "category": "Functional", "name": "CookieConsent*", "domain": "cookiebot.com (3rd party) or", - "description": "Registers cookie preferences of a user", - "retention": "1 year", + "description": "OCD_CookieConsent__description", + "retention": "OCD_retention_1_year", "dataController": "Cookiebot", "gdprUrl": "https://www.cookiebot.com/en/cookie-declaration/", "wildcard": "1" @@ -44,8 +44,8 @@ "category": "Functional", "name": "CookieConsentBulkTicket", "domain": "cookiebot.com (3rd party)", - "description": "Enables sharing cookie preferences across domains / websites", - "retention": "1 year", + "description": "OCD_CookieConsentBulkTicket_description", + "retention": "OCD_retention_1_year", "dataController": "Cookiebot", "gdprUrl": "https://www.cookiebot.com/en/cookie-declaration/", "wildcard": "0" @@ -57,8 +57,8 @@ "category": "Functional", "name": "userlang*", "domain": "cookiebot.com (3rd party)", - "description": "Saves language preferences of user for a website", - "retention": "1 year", + "description": "OCD_userlang__description", + "retention": "OCD_retention_1_year", "dataController": "Cookiebot", "gdprUrl": "https://www.cookiebot.com/en/cookie-declaration/", "wildcard": "1" @@ -70,8 +70,8 @@ "category": "Functional", "name": "consentUUID", "domain": "", - "description": "This cookie is used as a unique identification for the users who has accepted the cookie consent box.", - "retention": "1 year", + "description": "OCD_consentUUID_description", + "retention": "OCD_retention_1_year", "dataController": "Cookiebot", "gdprUrl": "https://www.cookiebot.com/en/cookie-declaration/", "wildcard": "0" @@ -83,8 +83,8 @@ "category": "Functional", "name": "cookieconsent_variant", "domain": "", - "description": "Stores the variant of shown cookie banner", - "retention": "1 year", + "description": "OCD_cookieconsent_variant_description", + "retention": "OCD_retention_1_year", "dataController": "Maxlead", "gdprUrl": "https://maxlead.com/privacy-statement/", "wildcard": "0" @@ -96,8 +96,8 @@ "category": "Functional", "name": "cookieconsent_system", "domain": "", - "description": "Cookie consent system cookie for saving user's cookie opt-in/out choices.", - "retention": "1 year", + "description": "OCD_cookieconsent_system_description", + "retention": "OCD_retention_1_year", "dataController": "Maxlead", "gdprUrl": "https://maxlead.com/privacy-statement/", "wildcard": "0" @@ -109,8 +109,8 @@ "category": "Functional", "name": "cookieconsent_level", "domain": "", - "description": "Cookie consent system cookie for storing the level of cookie consent.", - "retention": "1 year", + "description": "OCD_cookieconsent_level_description", + "retention": "OCD_retention_1_year", "dataController": "Maxlead", "gdprUrl": "https://maxlead.com/privacy-statement/", "wildcard": "0" @@ -122,8 +122,8 @@ "category": "Functional", "name": "cookieconsent_seen", "domain": "", - "description": "Used to support the GDPR / AVG compliant cookie consent system", - "retention": "1 year", + "description": "OCD_cookieconsent_seen_description", + "retention": "OCD_retention_1_year", "dataController": "Maxlead", "gdprUrl": "https://maxlead.com/privacy-statement/", "wildcard": "0" @@ -135,8 +135,8 @@ "category": "Functional", "name": "CookieConsent", "domain": "", - "description": "Stores the user's cookie consent state for the current domain", - "retention": "1 year", + "description": "OCD_CookieConsent_description", + "retention": "OCD_retention_1_year", "dataController": "Cookiebot", "gdprUrl": "https://www.cookiebot.com/en/cookie-declaration/", "wildcard": "0" @@ -147,11 +147,11 @@ "platform": "Google Analytics", "category": "Analytics", "name": "_ga", - "domain": "google-analytics.com (3rd party) or", - "description": "ID used to identify users", - "retention": "2 years", + "domain": "", + "description": "OCD__ga_description", + "retention": "OCD_retention_2_years", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -160,11 +160,11 @@ "platform": "Google Analytics", "category": "Analytics", "name": "_ga_*", - "domain": "google-analytics.com (3rd party) or ", - "description": "ID used to identify users", - "retention": "2 years", + "domain": "", + "description": "OCD__ga___description", + "retention": "OCD_retention_2_years", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "1" } ], @@ -173,11 +173,11 @@ "platform": "Google Analytics", "category": "Analytics", "name": "_gid", - "domain": "google-analytics.com (3rd party) or", - "description": "ID used to identify users for 24 hours after last activity", - "retention": "24 hours", + "domain": "", + "description": "OCD__gid_description", + "retention": "OCD_retention_24_hours", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -186,11 +186,11 @@ "platform": "Google Analytics", "category": "Analytics", "name": "_gat*", - "domain": "google-analytics.com (3rd party) or", - "description": "Used to monitor number of Google Analytics server requests when using Google Tag Manager", - "retention": "1 minute", + "domain": "", + "description": "OCD__gat__description", + "retention": "OCD_retention_1_minute", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "1" } ], @@ -199,11 +199,11 @@ "platform": "Google Analytics", "category": "Analytics", "name": "_dc_gtm_*", - "domain": "google-analytics.com (3rd party) or", - "description": "Used to monitor number of Google Analytics server requests", - "retention": "1 minute", + "domain": "", + "description": "OCD__dc_gtm___description", + "retention": "OCD_retention_1_minute", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "1" } ], @@ -212,11 +212,11 @@ "platform": "Google Analytics", "category": "Analytics", "name": "AMP_TOKEN", - "domain": "google-analytics.com (3rd party) or", - "description": "Contains a token code that is used to read out a Client ID from the AMP Client ID Service. By matching this ID with that of Google Analytics, users can be matched when switching between AMP content and non-AMP content.\n\nReference: https://support.google.com/analytics/answer/7486764?hl=en", - "retention": "30 seconds till 1 year", + "domain": "", + "description": "OCD_AMP_TOKEN_description", + "retention": "OCD_retention_30_seconds_till_1_year", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -225,11 +225,11 @@ "platform": "Google Analytics", "category": "Analytics", "name": "_gat_gtag_*", - "domain": "google-analytics.com (3rd party) or", - "description": "Used to set and get tracking data", - "retention": "1 hour", + "domain": "", + "description": "OCD__gat_gtag___description", + "retention": "OCD_retention_1_hour", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "1" } ], @@ -238,11 +238,11 @@ "platform": "Google Analytics", "category": "Marketing", "name": "_gac_*", - "domain": "google-analytics.com (3rd party) or", - "description": "Contains information related to marketing campaigns of the user. These are shared with Google AdWords / Google Ads when the Google Ads and Google Analytics accounts are linked together.", - "retention": "90 days", + "domain": "", + "description": "OCD__gac___description", + "retention": "OCD_retention_90_days", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "1" } ], @@ -251,11 +251,11 @@ "platform": "Google Analytics", "category": "Analytics", "name": "__utma", - "domain": "google-analytics.com (3rd party) or", - "description": "ID used to identify users and sessions", - "retention": "2 years after last activity", + "domain": "", + "description": "OCD___utma_description", + "retention": "OCD_retention_2_years_after_last_activity", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -264,11 +264,11 @@ "platform": "Google Analytics", "category": "Analytics", "name": "__utmt", - "domain": "google-analytics.com (3rd party) or", - "description": "Used to monitor number of Google Analytics server requests", - "retention": "10 minutes", + "domain": "", + "description": "OCD___utmt_description", + "retention": "OCD_retention_10_minutes", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -277,11 +277,11 @@ "platform": "Google Analytics", "category": "Analytics", "name": "__utmb", - "domain": "google-analytics.com (3rd party) or", - "description": "Used to distinguish new sessions and visits. This cookie is set when the GA.js javascript library is loaded and there is no existing __utmb cookie. The cookie is updated every time data is sent to the Google Analytics server.", - "retention": "30 minutes after last activity", + "domain": "", + "description": "OCD___utmb_description", + "retention": "OCD_retention_30_minutes_after_last_activity", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -290,11 +290,11 @@ "platform": "Google Analytics", "category": "Analytics", "name": "__utmc", - "domain": "google-analytics.com (3rd party) or", - "description": "Used only with old Urchin versions of Google Analytics and not with GA.js. Was used to distinguish between new sessions and visits at the end of a session.", - "retention": "End of session (browser)", + "domain": "", + "description": "OCD___utmc_description", + "retention": "OCD_retention_End_of_session__browser_", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -303,11 +303,11 @@ "platform": "Google Analytics", "category": "Analytics", "name": "__utmz", - "domain": "google-analytics.com (3rd party) or", - "description": "Contains information about the traffic source or campaign that directed user to the website. The cookie is set when the GA.js javascript is loaded and updated when data is sent to the Google Anaytics server", - "retention": "6 months after last activity", + "domain": "", + "description": "OCD___utmz_description", + "retention": "OCD_retention_6_months_after_last_activity", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -317,8 +317,8 @@ "category": "Analytics", "name": "__utmv", "domain": "google-analytics.com (3rd party) or", - "description": "Bevat custom informatie die door de webdeveloper is ingesteld via de _setCustomVar methode in Google Analytics. Deze cookie wordt iedere keer geupdate als er nieuwe gegevens naar de Google Analytics server worden gestuurd.", - "retention": "2 years after last activity", + "description": "OCD___utmv_description", + "retention": "OCD_retention_2_years_after_last_activity", "dataController": "Google", "gdprUrl": "https://privacy.google.com/take-control.html", "wildcard": "0" @@ -329,11 +329,11 @@ "platform": "Google Analytics", "category": "Analytics", "name": "__utmx", - "domain": "google-analytics.com (3rd party) or", - "description": "Used to determine whether a user is included in an A / B or Multivariate test.", - "retention": "18 months", + "domain": "", + "description": "OCD___utmx_description", + "retention": "OCD_retention_18_months", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -342,11 +342,11 @@ "platform": "Google Analytics", "category": "Analytics", "name": "__utmxx", - "domain": "google-analytics.com (3rd party) or", - "description": "Used to determine when the A / B or Multivariate test in which the user participates ends", - "retention": "18 months", + "domain": "", + "description": "OCD___utmxx_description", + "retention": "OCD_retention_18_months", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -356,10 +356,10 @@ "category": "Marketing", "name": "FPAU", "domain": "", - "description": "Assigns a specific ID to the visitor. This allows the website to determine the number of specific user-visits for analysis and statistics.", - "retention": "session", + "description": "OCD_FPAU_description", + "retention": "OCD_retention_session", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -369,10 +369,10 @@ "category": "Analytics", "name": "FPID", "domain": "", - "description": "Registers statistical data on users' behaviour on the website. Used for internal analytics by the website operator.", - "retention": "session", + "description": "OCD_FPID_description", + "retention": "OCD_retention_session", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -382,10 +382,10 @@ "category": "Analytics", "name": "FPLC", "domain": "", - "description": "This FPLC cookie is the cross-domain linker cookie hashed from the FPID cookie. It’s not HttpOnly, which means it can be read with JavaScript. It has a relatively short lifetime, just 20 hours.", - "retention": "session", + "description": "OCD_FPLC_description", + "retention": "OCD_retention_session", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -395,75 +395,348 @@ "category": "Functional", "name": "_GRECAPTCHA", "domain": "google.com", - "description": "Google reCAPTCHA sets a necessary cookie (_GRECAPTCHA) when executed for the purpose of providing its risk analysis.", - "retention": "179 days", + "description": "OCD__GRECAPTCHA_description", + "retention": "OCD_retention_179_days", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", + "wildcard": "0" + } + ], + "__eoi": [ + { + "platform": "Google AdSense", + "category": "Functional", + "name": "__eoi", + "domain": "", + "description": "OCD___eoi_description", + "retention": "OCD_retention_3_Months", + "dataController": "Google", + "gdprUrl": "https://policies.google.com/privacy", + "wildcard": "0" + } + ], + "pm_sess": [ + { + "platform": "Google AdSense", + "category": "Functional", + "name": "pm_sess", + "domain": "", + "description": "OCD_pm_sess_description", + "retention": "OCD_retention_30_minutes", + "dataController": "Google", + "gdprUrl": "https://policies.google.com/privacy", + "wildcard": "0" + } + ], + "pm_sess_NNN": [ + { + "platform": "Google AdSense", + "category": "Functional", + "name": "pm_sess_NNN", + "domain": "", + "description": "OCD_pm_sess_NNN_description", + "retention": "OCD_retention_30_minutes", + "dataController": "Google", + "gdprUrl": "https://policies.google.com/privacy", + "wildcard": "0" + } + ], + "aboutads_sessNNN": [ + { + "platform": "Google AdSense", + "category": "Functional", + "name": "aboutads_sessNNN", + "domain": "", + "description": "OCD_aboutads_sessNNN_description", + "retention": "OCD_retention_30_minutes", + "dataController": "Google", + "gdprUrl": "https://policies.google.com/privacy", + "wildcard": "0" + } + ], + "ANID": [ + { + "platform": "Google AdSense", + "category": "Functional", + "name": "ANID", + "domain": "", + "description": "OCD_ANID_description", + "retention": "OCD_retention_13_months", + "dataController": "Google", + "gdprUrl": "https://policies.google.com/privacy", + "wildcard": "0" + } + ], + "GA_OPT_OUT": [ + { + "platform": "Google Analytics", + "category": "Functional", + "name": "GA_OPT_OUT", + "domain": "google-analytics.com", + "description": "OCD_GA_OPT_OUT_description", + "retention": "OCD_retention_10_Nov_2030", + "dataController": "Google", + "gdprUrl": "https://policies.google.com/privacy", + "wildcard": "0" + } + ], + "Conversion": [ + { + "platform": "Google Ads", + "category": "Marketing", + "name": "Conversion", + "domain": "www.googleadservices.com", + "description": "OCD_Conversion_description", + "retention": "OCD_retention_90_days", + "dataController": "Google", + "gdprUrl": "https://policies.google.com/privacy", + "wildcard": "0" + } + ], + "_opt_awkid": [ + { + "platform": "Google Optimize", + "category": "Analytics", + "name": "_opt_awkid", + "domain": "", + "description": "OCD__opt_awkid_description", + "retention": "OCD_retention_24_hours", + "dataController": "Google", + "gdprUrl": "https://policies.google.com/privacy", + "wildcard": "0" + } + ], + "_opt_awgid": [ + { + "platform": "Google Optimize", + "category": "Analytics", + "name": "_opt_awgid", + "domain": "", + "description": "OCD__opt_awgid_description", + "retention": "OCD_retention_24_hours", + "dataController": "Google", + "gdprUrl": "https://policies.google.com/privacy", + "wildcard": "0" + } + ], + "_opt_awmid": [ + { + "platform": "Google Optimize", + "category": "Analytics", + "name": "_opt_awmid", + "domain": "", + "description": "OCD__opt_awmid_description", + "retention": "OCD_retention_24_hours", + "dataController": "Google", + "gdprUrl": "https://policies.google.com/privacy", + "wildcard": "0" + } + ], + "_gaexp_rc": [ + { + "platform": "Google Optimize", + "category": "Analytics", + "name": "_gaexp_rc", + "domain": "", + "description": "OCD__gaexp_rc_description", + "retention": "OCD_retention_24_hours", + "dataController": "Google", + "gdprUrl": "https://policies.google.com/privacy", + "wildcard": "0" + } + ], + "_opt_awcid": [ + { + "platform": "Google Optimize", + "category": "Analytics", + "name": "_opt_awcid", + "domain": "", + "description": "OCD__opt_awcid_description", + "retention": "OCD_retention_24_hours", + "dataController": "Google", + "gdprUrl": "https://policies.google.com/privacy", + "wildcard": "0" + } + ], + "PAIDCONTENT": [ + { + "platform": "Google Surveys", + "category": "Marketing", + "name": "PAIDCONTENT", + "domain": "doubleclick.net", + "description": "OCD_PAIDCONTENT_description", + "retention": "OCD_retention_30_Days", + "dataController": "Google", + "gdprUrl": "https://policies.google.com/privacy", + "wildcard": "0" + } + ], + "_opt_expid": [ + { + "platform": "Google Optimize", + "category": "Analytics", + "name": "_opt_expid", + "domain": "", + "description": "OCD__opt_expid_description", + "retention": "OCD_retention_10_seconds", + "dataController": "Google", + "gdprUrl": "https://policies.google.com/privacy", + "wildcard": "0" + } + ], + "_gcl_ha": [ + { + "platform": "Google Hotel Ads", + "category": "Marketing", + "name": "_gcl_ha", + "domain": "", + "description": "OCD__gcl_ha_description", + "retention": "OCD_retention_90_Days", + "dataController": "Google", + "gdprUrl": "https://policies.google.com/privacy", + "wildcard": "0" + } + ], + "_gcl_gf": [ + { + "platform": "Google Flights", + "category": "Marketing", + "name": "_gcl_gf", + "domain": "", + "description": "OCD__gcl_gf_description", + "retention": "OCD_retention_90_Days", + "dataController": "Google", + "gdprUrl": "https://policies.google.com/privacy", + "wildcard": "0" + } + ], + "_gcl_aw": [ + { + "platform": "Google Ads", + "category": "Marketing", + "name": "_gcl_aw", + "domain": "", + "description": "OCD__gcl_aw_description", + "retention": "OCD_retention_90_Days", + "dataController": "Google", + "gdprUrl": "https://policies.google.com/privacy", + "wildcard": "0" + } + ], + "_gcl_gb": [ + { + "platform": "Google Ads", + "category": "Marketing", + "name": "_gcl_gb", + "domain": "", + "description": "OCD__gcl_gb_description", + "retention": "OCD_retention_90_Days", + "dataController": "Google", + "gdprUrl": "https://policies.google.com/privacy", + "wildcard": "0" + } + ], + "_gac_gb_*": [ + { + "platform": "Google Ads", + "category": "Marketing", + "name": "_gac_gb_*", + "domain": "", + "description": "OCD__gac_gb___description", + "retention": "OCD_retention_90_Days", + "dataController": "Google", + "gdprUrl": "https://policies.google.com/privacy", + "wildcard": "1" + } + ], + "FPGCLGB": [ + { + "platform": "Google Ads", + "category": "Marketing", + "name": "FPGCLGB", + "domain": "", + "description": "OCD_FPGCLGB_description", + "retention": "OCD_retention_90_Days", + "dataController": "Google", + "gdprUrl": "https://policies.google.com/privacy", + "wildcard": "0" + } + ], + "FPGCLAW": [ + { + "platform": "Google Ads", + "category": "Marketing", + "name": "FPGCLAW", + "domain": "", + "description": "OCD_FPGCLAW_description", + "retention": "OCD_retention_90_Days", + "dataController": "Google", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], "__gsas": [ { - "platform": "Google Adsense", + "platform": "Google AdSense", "category": "Marketing", "name": "__gsas", "domain": "", - "description": "Provides ad delivery or retargeting.", - "retention": "3 months", + "description": "OCD___gsas_description", + "retention": "OCD_retention_3_months", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], "__gpi": [ { - "platform": "Google Adsense", + "platform": "Google AdSense", "category": "Marketing", "name": "__gpi", "domain": "", - "description": "Collects information on user behaviour on multiple websites. This information is used in order to optimize the relevance of advertisement on the website.", - "retention": "13 months", + "description": "OCD___gpi_description", + "retention": "OCD_retention_13_months", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], "__gpi_optout": [ { - "platform": "Google Adsense", + "platform": "Google AdSense", "category": "Marketing", "name": "__gpi_optout", "domain": "", - "description": "Collects information on user behaviour on multiple websites. This information is used in order to optimize the relevance of advertisement on the website.", - "retention": "13 months", + "description": "OCD___gpi_optout_description", + "retention": "OCD_retention_13_months", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], "GED_PLAYLIST_ACTIVITY": [ { - "platform": "Google Adsense", + "platform": "Google AdSense", "category": "Marketing", "name": "GED_PLAYLIST_ACTIVITY", "domain": "", - "description": "Improves targeting/advertising within the website", - "retention": "session", + "description": "OCD_GED_PLAYLIST_ACTIVITY_description", + "retention": "OCD_retention_session", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], "ACLK_DATA": [ { - "platform": "Google Adsense", + "platform": "Google AdSense", "category": "Marketing", "name": "ACLK_DATA", "domain": "", - "description": "This cookie is used to help improve advertising. This targets advertising based on what's relevant to a user, to improve reporting on campaign performance.", - "retention": "5 minutes", + "description": "OCD_ACLK_DATA_description", + "retention": "OCD_retention_5_minutes", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -473,10 +746,10 @@ "category": "Functional", "name": "AEC", "domain": "google.com", - "description": "AEC cookies ensure that requests within a browsing session are made by the user, and not by other sites. These cookies prevent malicious sites from acting on behalf of a user without that user’s knowledge.", - "retention": "6 months", + "description": "OCD_AEC_description", + "retention": "OCD_retention_6_months", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -486,10 +759,10 @@ "category": "Marketing", "name": "ADS_VISITOR_ID", "domain": "google.com", - "description": "Cookie required to use the options and on-site web services", - "retention": "2 months", + "description": "OCD_ADS_VISITOR_ID_description", + "retention": "OCD_retention_2_months", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -499,10 +772,10 @@ "category": "Marketing", "name": "__Secure-3PSIDCC", "domain": "google.com", - "description": "Targeting cookie. Used to create a user profile and display relevant and personalised Google Ads to the user.", - "retention": "2 years", + "description": "OCD___Secure_3PSIDCC_description", + "retention": "OCD_retention_2_years", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -512,10 +785,10 @@ "category": "Marketing", "name": "__Secure-3PSIDTS", "domain": "google.com", - "description": "Targeting cookie. Used to create a user profile and display relevant and personalised Google Ads to the user.", - "retention": "2 years", + "description": "OCD___Secure_3PSIDTS_description", + "retention": "OCD_retention_2_years", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -525,10 +798,10 @@ "category": "Marketing", "name": "__Secure-1PSIDTS", "domain": "google.com", - "description": "Targeting cookie. Used to create a user profile and display relevant and personalised Google Ads to the user.", - "retention": "2 years", + "description": "OCD___Secure_1PSIDTS_description", + "retention": "OCD_retention_2_years", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -538,10 +811,10 @@ "category": "Marketing", "name": "__Secure-1PAPISID", "domain": "google.com", - "description": "Targeting cookie. Used to create a user profile and display relevant and personalised Google Ads to the user.", - "retention": "2 years", + "description": "OCD___Secure_1PAPISID_description", + "retention": "OCD_retention_2_years", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -551,10 +824,10 @@ "category": "Marketing", "name": "__Secure-3PSID", "domain": "google.com", - "description": "Targeting cookie. Used to profile the interests of website visitors and display relevant and personalised Google ads.", - "retention": "2 years", + "description": "OCD___Secure_3PSID_description", + "retention": "OCD_retention_2_years", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -564,10 +837,10 @@ "category": "Marketing", "name": "__Secure-1PSID", "domain": "google.com", - "description": "Targeting cookie. Used to create a user profile and display relevant and personalised Google Ads to the user.", - "retention": "2 years", + "description": "OCD___Secure_1PSID_description", + "retention": "OCD_retention_2_years", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -577,10 +850,10 @@ "category": "Marketing", "name": "__Secure-1PSIDCC", "domain": "google.com", - "description": "Targeting cookie. Used to create a user profile and display relevant and personalised Google Ads to the user.", - "retention": "2 years", + "description": "OCD___Secure_1PSIDCC_description", + "retention": "OCD_retention_2_years", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -590,10 +863,10 @@ "category": "Marketing", "name": "__Secure-3PAPISID", "domain": "google.com", - "description": "Profiles the interests of website visitors to serve relevant and personalised ads through retargeting.", - "retention": "2 years", + "description": "OCD___Secure_3PAPISID_description", + "retention": "OCD_retention_2_years", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -603,10 +876,10 @@ "category": "Marketing", "name": "OGPC", "domain": "google.com", - "description": "These cookies are used by Google to store user preferences and information while viewing Google mapped pages.", - "retention": "1 month", + "description": "OCD_OGPC_description", + "retention": "OCD_retention_1_month", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -616,10 +889,10 @@ "category": "Marketing", "name": "OGP", "domain": "google.com", - "description": "This cookie is used by Google to activate and track the Google Maps functionality.", - "retention": "2 months", + "description": "OCD_OGP_description", + "retention": "OCD_retention_2_months", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -629,10 +902,10 @@ "category": "Marketing", "name": "1P_JAR", "domain": ".gstatic.com", - "description": "These cookies are set via embedded youtube-videos. They register anonymous statistical data on for example how many times the video is displayed and what settings are used for playback.", - "retention": "1 month", + "description": "OCD_1P_JAR_description", + "retention": "OCD_retention_1_month", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -642,10 +915,10 @@ "category": "Functional", "name": "CONSENT", "domain": ".gstatic.com", - "description": "Google cookie consent tracker", - "retention": "20 years", + "description": "OCD_CONSENT_description", + "retention": "OCD_retention_20_years", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -655,10 +928,10 @@ "category": "Functional", "name": "SOCS", "domain": "google.com", - "description": "Stores a user's state regarding their cookies choices", - "retention": "13 months", + "description": "OCD_SOCS_description", + "retention": "OCD_retention_13_months", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -668,10 +941,10 @@ "category": "Functional", "name": "ACCOUNT_CHOOSER", "domain": "accounts.google.com", - "description": "Used to sign in with Google account.", - "retention": "session", + "description": "OCD_ACCOUNT_CHOOSER_description", + "retention": "OCD_retention_session", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -681,10 +954,10 @@ "category": "Functional", "name": "SMSV", "domain": "accounts.google.com", - "description": "Used to sign in with Google account.", - "retention": "session", + "description": "OCD_SMSV_description", + "retention": "OCD_retention_session", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -694,10 +967,10 @@ "category": "Functional", "name": "__Host-1PLSID", "domain": "accounts.google.com", - "description": "Used to sign in with Google account.", - "retention": "1 year", + "description": "OCD___Host_1PLSID_description", + "retention": "OCD_retention_1_year", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -707,10 +980,10 @@ "category": "Functional", "name": "__Host-3PLSID", "domain": "accounts.google.com", - "description": "Used to sign in with Google account.", - "retention": "1 year", + "description": "OCD___Host_3PLSID_description", + "retention": "OCD_retention_1_year", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -720,10 +993,10 @@ "category": "Functional", "name": "__Host-GAPS", "domain": "accounts.google.com", - "description": "Used to sign in with Google account.", - "retention": "1 year", + "description": "OCD___Host_GAPS_description", + "retention": "OCD_retention_1_year", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -733,10 +1006,10 @@ "category": "Functional", "name": "LSOLH", "domain": "accounts.google.com", - "description": "This cookie is for authentication with your Google account", - "retention": "1 year", + "description": "OCD_LSOLH_description", + "retention": "OCD_retention_1_year", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -746,10 +1019,10 @@ "category": "Functional", "name": "g_enabled_idps", "domain": "accounts.google.com", - "description": "Used for Google Single Sign On", - "retention": "1 year", + "description": "OCD_g_enabled_idps_description", + "retention": "OCD_retention_1_year", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -759,10 +1032,10 @@ "category": "Functional", "name": "G_AUTHUSER_H", "domain": "accounts.google.com", - "description": "Google Authentication", - "retention": "session", + "description": "OCD_G_AUTHUSER_H_description", + "retention": "OCD_retention_session", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -772,10 +1045,10 @@ "category": "Functional", "name": "SEARCH_SAMESITE", "domain": "google.com", - "description": "SameSite prevents the browser from sending this cookie along with cross-site requests. The main goal is mitigate the risk of cross-origin information leakage. It also provides some protection against cross-site request forgery attacks.", - "retention": "session", + "description": "OCD_SEARCH_SAMESITE_description", + "retention": "OCD_retention_session", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -785,10 +1058,10 @@ "category": "Marketing", "name": "AID", "domain": "google.com", - "description": "Download certain Google Tools and save certain preferences, for example the number of search results per page or activation of the SafeSearch Filter. Adjusts the ads that appear in Google Search.", - "retention": "1 year", + "description": "OCD_AID_description", + "retention": "OCD_retention_1_year", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -798,10 +1071,10 @@ "category": "Marketing", "name": "SID", "domain": "google.com", - "description": "Download certain Google Tools and save certain preferences, for example the number of search results per page or activation of the SafeSearch Filter. Adjusts the ads that appear in Google Search.", - "retention": "2 years", + "description": "OCD_SID_description", + "retention": "OCD_retention_2_years", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -811,10 +1084,10 @@ "category": "Marketing", "name": "HSID", "domain": "google.com", - "description": "Download certain Google Tools and save certain preferences, for example the number of search results per page or activation of the SafeSearch Filter. Adjusts the ads that appear in Google Search.", - "retention": "2 years", + "description": "OCD_HSID_description", + "retention": "OCD_retention_2_years", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -824,10 +1097,10 @@ "category": "Marketing", "name": "APISID", "domain": "google.com", - "description": "Download certain Google Tools and save certain preferences, for example the number of search results per page or activation of the SafeSearch Filter. Adjusts the ads that appear in Google Search.", - "retention": "2 years", + "description": "OCD_APISID_description", + "retention": "OCD_retention_2_years", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -837,10 +1110,10 @@ "category": "Marketing", "name": "SAPISID", "domain": "google.com", - "description": "Download certain Google Tools and save certain preferences, for example the number of search results per page or activation of the SafeSearch Filter. Adjusts the ads that appear in Google Search.", - "retention": "2 years", + "description": "OCD_SAPISID_description", + "retention": "OCD_retention_2_years", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -850,10 +1123,10 @@ "category": "Marketing", "name": "SSID", "domain": "google.com", - "description": "Download certain Google Tools and save certain preferences, for example the number of search results per page or activation of the SafeSearch Filter. Adjusts the ads that appear in Google Search.", - "retention": "2 years", + "description": "OCD_SSID_description", + "retention": "OCD_retention_2_years", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -863,10 +1136,10 @@ "category": "Marketing", "name": "SIDCC", "domain": "google.com", - "description": "Download certain Google Tools and save certain preferences, for example the number of search results per page or activation of the SafeSearch Filter. Adjusts the ads that appear in Google Search.", - "retention": "2 years", + "description": "OCD_SIDCC_description", + "retention": "OCD_retention_2_years", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -876,10 +1149,10 @@ "category": "Marketing", "name": "OTZ", "domain": "google.com", - "description": "Aggregate analysis of website visitors", - "retention": "17 days", + "description": "OCD_OTZ_description", + "retention": "OCD_retention_17_days", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -889,10 +1162,10 @@ "category": "Marketing", "name": "A", "domain": "google.com", - "description": "Google uses this cookies to make advertising more engaging to users and more valuable to publishers and advertisers", - "retention": "17 days", + "description": "OCD_A_description", + "retention": "OCD_retention_17_days", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -902,10 +1175,10 @@ "category": "Marketing", "name": "DV", "domain": "google.com", - "description": "This cookies is used to collect website statistics and track conversion rates and Google ad personalisation", - "retention": "1 year", + "description": "OCD_DV_description", + "retention": "OCD_retention_1_year", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -915,10 +1188,10 @@ "category": "Marketing", "name": "NID", "domain": "google.com", - "description": "This cookies is used to collect website statistics and track conversion rates and Google ad personalisation", - "retention": "1 year", + "description": "OCD_NID_description", + "retention": "OCD_retention_1_year", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -928,10 +1201,10 @@ "category": "Marketing", "name": "TAID", "domain": "google.com", - "description": "This cookie is used to link your activity across devices if you’ve previously signed in to your Google Account on another device. We do this to coordinate that the ads you see across devices and measure conversion events.", - "retention": "14 days", + "description": "OCD_TAID_description", + "retention": "OCD_retention_14_days", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -941,10 +1214,10 @@ "category": "Marketing", "name": "FPGCLDC", "domain": "google.com", - "description": "Used to help advertisers determine how many times users who click on their ads end up taking an action on their site", - "retention": "90 days", + "description": "OCD_FPGCLDC_description", + "retention": "OCD_retention_90_days", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -954,10 +1227,10 @@ "category": "Marketing", "name": "_gcl_au", "domain": "", - "description": "Used by Google AdSense for experimenting with advertisement efficiency across websites using their services.", - "retention": "3 months", + "description": "OCD__gcl_au_description", + "retention": "OCD_retention_3_months", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -967,10 +1240,10 @@ "category": "Marketing", "name": "_gcl_dc", "domain": "", - "description": "Used by Google AdSense for experimenting with advertisement efficiency across websites using their services.", - "retention": "3 months", + "description": "OCD__gcl_dc_description", + "retention": "OCD_retention_3_months", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -980,8 +1253,8 @@ "category": "Functional", "name": "_gaexp", "domain": "", - "description": "Used to determine a user's inclusion in an experiment and the expiry of experiments a user has been included in.", - "retention": "90 days", + "description": "OCD__gaexp_description", + "retention": "OCD_retention_90_days", "dataController": "Google", "gdprUrl": "", "wildcard": "0" @@ -993,10 +1266,10 @@ "category": "Functional", "name": "GCLB", "domain": "", - "description": "This cookie is used in context with load balancing - This optimizes the response rate between the visitor and the site, by distributing the traffic load on multiple network links or servers.", - "retention": "Session", + "description": "OCD_GCLB_description", + "retention": "OCD_retention_Session", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -1006,10 +1279,10 @@ "category": "Analytics", "name": "FCCDCF", "domain": "", - "description": "Cookie for Google Funding Choices API which allows for functionality specific to consent gathering for things like GDPR consent and CCPA opt-out.", - "retention": "13 months", + "description": "OCD_FCCDCF_description", + "retention": "OCD_retention_13_months", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -1019,10 +1292,10 @@ "category": "Analytics", "name": "FCNEC", "domain": "", - "description": "Cookie for Google Funding Choices API which allows for functionality specific to consent gathering for things like GDPR consent and CCPA opt-out.", - "retention": "13 months", + "description": "OCD_FCNEC_description", + "retention": "OCD_retention_13_months", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -1032,10 +1305,10 @@ "category": "Functional", "name": "receive-cookie-deprecation", "domain": "", - "description": "This cookie ensures browers in an experiment group of the Chrome-facilitated testing period include the Sec-Cookie-Deprecation request header as soon as it becomes available.", - "retention": "180 days", + "description": "OCD_receive_cookie_deprecation_description", + "retention": "OCD_retention_180_days", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -1045,10 +1318,127 @@ "category": "Marketing", "name": "_dcid", "domain": "", - "description": "Collects information on user behaviour on multiple websites. This information is used in order to optimize the relevance of advertisement on the website.", - "retention": "400 days", + "description": "OCD__dcid_description", + "retention": "OCD_retention_400_days", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", + "wildcard": "0" + } + ], + "SNID": [ + { + "platform": "Google", + "category": "Marketing", + "name": "SNID", + "domain": ".google.com", + "description": "OCD_SNID_description", + "retention": "OCD_retention_6_months", + "dataController": "Google", + "gdprUrl": "https://policies.google.com/privacy", + "wildcard": "0" + } + ], + "cookies_accepted": [ + { + "platform": "Google", + "category": "Marketing", + "name": "cookies_accepted", + "domain": ".developers.google.com", + "description": "OCD_cookies_accepted_description", + "retention": "OCD_retention_1_year", + "dataController": "Google", + "gdprUrl": "https://policies.google.com/privacy", + "wildcard": "0" + } + ], + "django_language": [ + { + "platform": "Google", + "category": "Functional", + "name": "django_language", + "domain": "developers.google.com", + "description": "OCD_django_language_description", + "retention": "OCD_retention_3_month", + "dataController": "Google", + "gdprUrl": "https://policies.google.com/privacy", + "wildcard": "0" + } + ], + "GN_PREF": [ + { + "platform": "Google", + "category": "Marketing", + "name": "GN_PREF", + "domain": "news.google.com", + "description": "OCD_GN_PREF_description", + "retention": "OCD_retention_1_year", + "dataController": "Google", + "gdprUrl": "https://policies.google.com/privacy", + "wildcard": "0" + } + ], + "OSID": [ + { + "platform": "Google", + "category": "Marketing", + "name": "OSID", + "domain": ".google.com", + "description": "OCD_OSID_description", + "retention": "OCD_retention_6_months", + "dataController": "Google", + "gdprUrl": "https://policies.google.com/privacy", + "wildcard": "0" + } + ], + "__Secure-OSID": [ + { + "platform": "Google", + "category": "Marketing", + "name": "__Secure-OSID", + "domain": ".google.com", + "description": "OCD___Secure_OSID_description", + "retention": "OCD_retention_6_months", + "dataController": "Google", + "gdprUrl": "https://policies.google.com/privacy", + "wildcard": "0" + } + ], + "LSID": [ + { + "platform": "Google", + "category": "Marketing", + "name": "LSID", + "domain": ".google.com", + "description": "OCD_LSID_description", + "retention": "OCD_retention_6_months", + "dataController": "Google", + "gdprUrl": "https://policies.google.com/privacy", + "wildcard": "0" + } + ], + "COMPASS": [ + { + "platform": "Google", + "category": "Marketing", + "name": "COMPASS", + "domain": ".google.com", + "description": "OCD_COMPASS_description", + "retention": "OCD_retention_2_days", + "dataController": "Google", + "gdprUrl": "https://policies.google.com/privacy", + "wildcard": "0" + } + ], + "UULE": [ + { + "platform": "Google", + "category": "Marketing", + "name": "UULE", + "domain": ".google.com", + "description": "OCD_UULE_description", + "retention": "OCD_retention_6_hours", + "dataController": "Google", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -1058,10 +1448,10 @@ "category": "Marketing", "name": "IDE", "domain": "doubleclick.net (3rd party)", - "description": "This cookie is used for targeting, analyzing and optimisation of ad campaigns in DoubleClick/Google Marketing Suite", - "retention": "2 years", + "description": "OCD_IDE_description", + "retention": "OCD_retention_2_years", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -1071,10 +1461,10 @@ "category": "Marketing", "name": "DSID", "domain": "doubleclick.net (3rd party)", - "description": "This cookie is used for targeting, analyzing and optimisation of ad campaigns in DoubleClick/Google Marketing Suite", - "retention": "2 weeks", + "description": "OCD_DSID_description", + "retention": "OCD_retention_2_weeks", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -1084,10 +1474,10 @@ "category": "Marketing", "name": "ID", "domain": "doubleclick.net (3rd party)", - "description": "This cookie is used for targeting, analyzing and optimisation of ad campaigns in DoubleClick/Google Marketing Suite", - "retention": "2 months", + "description": "OCD_ID_description", + "retention": "OCD_retention_2_months", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -1097,10 +1487,10 @@ "category": "Marketing", "name": "RUL", "domain": "doubleclick.net (3rd party)", - "description": "Used by DoubleClick to determine if the website ad was properly displayed. This is done to make their marketing efforts more efficient.", - "retention": "1 year", + "description": "OCD_RUL_description", + "retention": "OCD_retention_1_year", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -1110,10 +1500,10 @@ "category": "Marketing", "name": "FLC", "domain": "doubleclick.net (3rd party)", - "description": "This cookie is used to link your activity across devices if you’ve previously signed in to your Google Account on another device. We do this to coordinate that the ads you see across devices and measure conversion events.", - "retention": "10 seconds", + "description": "OCD_FLC_description", + "retention": "OCD_retention_10_seconds", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -1123,10 +1513,10 @@ "category": "Marketing", "name": "__gads", "domain": "", - "description": "This cookie is used by Google for a variety of purposes (e.g., ensuring Frequency Caps work correctly). It includes AdSense if you have AdSense enabled. This cookie is associated with the DoubleClick for Publishers service from Google. Its purpose is to monitor the showing of advertisements on the site, for which the owner may earn some revenue. The main purpose of this cookie is targeting/advertising.", - "retention": "various", + "description": "OCD___gads_description", + "retention": "OCD_retention_various", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -1136,10 +1526,10 @@ "category": "Marketing", "name": "GoogleAdServingTest", "domain": "", - "description": "Used to register what ads have been displayed to the user.", - "retention": "session", + "description": "OCD_GoogleAdServingTest_description", + "retention": "OCD_retention_session", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -1149,10 +1539,10 @@ "category": "Marketing", "name": "ar_debug", "domain": "doubleclick.net", - "description": "Store and track conversions", - "retention": "Persistent", + "description": "OCD_ar_debug_description", + "retention": "OCD_retention_Persistent", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -1162,10 +1552,10 @@ "category": "Functional", "name": "test_cookie", "domain": "doubleclick.net", - "description": "This cookie is set by DoubleClick (which is owned by Google) to determine if the website visitor's browser supports cookies.", - "retention": "1 year", + "description": "OCD_test_cookie_description", + "retention": "OCD_retention_1_year", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -1175,10 +1565,10 @@ "category": "Marketing", "name": "APC", "domain": "doubleclick.net", - "description": "This cookie is used for targeting, analyzing and optimisation of ad campaigns in DoubleClick/Google Marketing Suite", - "retention": "6 months", + "description": "OCD_APC_description", + "retention": "OCD_retention_6_months", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -1188,8 +1578,8 @@ "category": "Analytics", "name": "S1", "domain": "nl.sitestat.com (3rd party)", - "description": "Comscore: statistical and analytical data", - "retention": "5 years", + "description": "OCD_S1_description", + "retention": "OCD_retention_5_years", "dataController": "comScore", "gdprUrl": "https://www.comscore.com/About/Privacy-Policy", "wildcard": "0" @@ -1201,8 +1591,8 @@ "category": "Analytics", "name": "C1", "domain": "nl.sitestat.com (3rd party)", - "description": "Comscore: statistical and analytical data", - "retention": "5 years", + "description": "OCD_C1_description", + "retention": "OCD_retention_5_years", "dataController": "comScore", "gdprUrl": "https://www.comscore.com/About/Privacy-Policy", "wildcard": "0" @@ -1214,8 +1604,8 @@ "category": "Analytics", "name": "s_cc", "domain": "", - "description": "Used to determine if browser of user accepts cookies or not", - "retention": "End of session (browser)", + "description": "OCD_s_cc_description", + "retention": "OCD_retention_End_of_session__browser_", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/nl/privacy.html", "wildcard": "0" @@ -1227,8 +1617,8 @@ "category": "Analytics", "name": "s_sq", "domain": "", - "description": "Used to register the previous link clicked by the user", - "retention": "End of session (browser)", + "description": "OCD_s_sq_description", + "retention": "OCD_retention_End_of_session__browser_", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/nl/privacy.html", "wildcard": "0" @@ -1240,8 +1630,8 @@ "category": "Analytics", "name": "s_vi*", "domain": "or 207.net (3rd party)", - "description": "Contains a unique ID to identify a user", - "retention": "2 years", + "description": "OCD_s_vi__description", + "retention": "OCD_retention_2_years", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/nl/privacy.html", "wildcard": "1" @@ -1253,8 +1643,8 @@ "category": "Analytics", "name": "s_fid", "domain": "", - "description": "Alternative cookie with unique user ID / timestamp when the s_vi cookie can not be set for technical reasons", - "retention": "5 years", + "description": "OCD_s_fid_description", + "retention": "OCD_retention_5_years", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/nl/privacy.html", "wildcard": "0" @@ -1266,8 +1656,8 @@ "category": "Analytics", "name": "fid", "domain": "", - "description": "If other visitor ID methods fail, Adobe sets a fallback cookie or uses a combination of IP address and user agent to identify the visitor.", - "retention": "2 years", + "description": "OCD_fid_description", + "retention": "OCD_retention_2_years", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/nl/privacy.html", "wildcard": "0" @@ -1279,8 +1669,8 @@ "category": "Marketing", "name": "s_ecid", "domain": "", - "description": "This cookie is set by the customer's domain after the AMCV cookie is set by the client. The purpose of this cookie is to allow persistent ID tracking in the 1st-party state and is used as a reference ID if the AMCV cookie has expired.", - "retention": "2 years", + "description": "OCD_s_ecid_description", + "retention": "OCD_retention_2_years", "dataController": "Advertiser's website", "gdprUrl": "https://www.adobe.com/nl/privacy.html", "wildcard": "0" @@ -1292,8 +1682,8 @@ "category": "Analytics", "name": "s_ppv", "domain": "", - "description": "Stores information on the percentage of the page displayed", - "retention": "session", + "description": "OCD_s_ppv_description", + "retention": "OCD_retention_session", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/nl/privacy.html", "wildcard": "0" @@ -1305,8 +1695,8 @@ "category": "Analytics", "name": "s_tp", "domain": "", - "description": "This lets us know how much of the page you viewed.", - "retention": "session", + "description": "OCD_s_tp_description", + "retention": "OCD_retention_session", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/nl/privacy.html", "wildcard": "0" @@ -1318,8 +1708,8 @@ "category": "Functional", "name": "sat_track", "domain": "", - "description": "The sat_track cookie is a part of Adobe Analytics. It controls the enabling and disabling of cookies and whether they are loaded onto the site.", - "retention": "90 days", + "description": "OCD_sat_track_description", + "retention": "OCD_retention_90_days", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy", "wildcard": "0" @@ -1331,8 +1721,8 @@ "category": "Marketing", "name": "demdex", "domain": "or demdex.net (3rd party)", - "description": "Unique value with which Audience Manager can identify a user. Used, among others, for identification, segmentation, modeling and reporting purposes.", - "retention": "180 days after last activity or 10 years when opting out", + "description": "OCD_demdex_description", + "retention": "OCD_retention_180_days_after_last_activity_or_10_years_when_opting_out", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/nl/privacy.html", "wildcard": "0" @@ -1344,8 +1734,8 @@ "category": "Marketing", "name": "dextp", "domain": "", - "description": "Registers the date plus time (timestamp) on which a data synchronization was last performed by the Audience Manager.", - "retention": "180 days after last activity", + "description": "OCD_dextp_description", + "retention": "OCD_retention_180_days_after_last_activity", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/nl/privacy.html", "wildcard": "0" @@ -1357,8 +1747,8 @@ "category": "Marketing", "name": "dst", "domain": "", - "description": "Used to register a possible error message when sending data to a linked system.", - "retention": "180 days after last activity or 10 years when opting out", + "description": "OCD_dst_description", + "retention": "OCD_retention_180_days_after_last_activity_or_10_years_when_opting_out", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/nl/privacy.html", "wildcard": "0" @@ -1370,8 +1760,8 @@ "category": "Marketing", "name": "_dp", "domain": "or demdex.net (3rd party)", - "description": "Used to determine if browser of user accepts cookies or not", - "retention": "30 seconds", + "description": "OCD__dp_description", + "retention": "OCD_retention_30_seconds", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/nl/privacy.html", "wildcard": "0" @@ -1383,8 +1773,8 @@ "category": "Marketing", "name": "aam_uuid", "domain": "", - "description": "Adobe Audience Manager - data management platform uses these cookies to assign a unique ID when users visit a website.", - "retention": "1 month", + "description": "OCD_aam_uuid_description", + "retention": "OCD_retention_1_month", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/nl/privacy.html", "wildcard": "0" @@ -1396,8 +1786,8 @@ "category": "Marketing", "name": "AMCV_*", "domain": "", - "description": "Adobe Experience Cloud uses a cookie to store a unique visitor ID that is used across Experience Cloud Solutions.", - "retention": "2 years", + "description": "OCD_AMCV___description", + "retention": "OCD_retention_2_years", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/nl/privacy.html", "wildcard": "1" @@ -1409,8 +1799,8 @@ "category": "Marketing", "name": "AMCVS_*", "domain": "", - "description": "The AMCVS cookie serves as a flag indicating that the session has been initialized. Its value is always 1 and discontinues when the session has ended.", - "retention": "Session", + "description": "OCD_AMCVS___description", + "retention": "OCD_retention_Session", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/nl/privacy.html", "wildcard": "1" @@ -1422,8 +1812,8 @@ "category": "Marketing", "name": "mbox", "domain": "", - "description": "Adobe Target uses cookies to give website operators the ability to test which online content and offers are more relevant to visitors.", - "retention": "2 years", + "description": "OCD_mbox_description", + "retention": "OCD_retention_2_years", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/nl/privacy.html", "wildcard": "0" @@ -1435,8 +1825,8 @@ "category": "Functional", "name": "at_check", "domain": "", - "description": "A simple test value used to determine if a visitor supports cookies. Set each time a visitor requests a page.", - "retention": "session", + "description": "OCD_at_check_description", + "retention": "OCD_retention_session", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/nl/privacy.html", "wildcard": "0" @@ -1448,8 +1838,8 @@ "category": "Functional", "name": "renderid", "domain": "", - "description": "This cookie is needed by the dispatcher (webserver) to distinguish between the different publisher server.", - "retention": "session", + "description": "OCD_renderid_description", + "retention": "OCD_retention_session", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/nl/privacy.html", "wildcard": "0" @@ -1461,8 +1851,8 @@ "category": "Marketing", "name": "dpm", "domain": "", - "description": "DPM is an abbreviation for Data Provider Match. It tells internal, Adobe systems that a call from Audience Manager or the Adobe Experience Cloud ID Service is passing in customer data for synchronization or requesting an ID.", - "retention": "180 days", + "description": "OCD_dpm_description", + "retention": "OCD_retention_180_days", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/nl/privacy.html", "wildcard": "0" @@ -1474,8 +1864,8 @@ "category": "Marketing", "name": "TPC", "domain": "adform.net (3rd party)", - "description": "Used to determine if browser of user accepts third party cookies or not", - "retention": "14 days", + "description": "OCD_TPC_description", + "retention": "OCD_retention_14_days", "dataController": "Adform", "gdprUrl": "https://site.adform.com/privacy-center/overview", "wildcard": "0" @@ -1487,8 +1877,8 @@ "category": "Marketing", "name": "C", "domain": "adform.net (3rd party)", - "description": "Used to determine if browser of user accepts cookies or not", - "retention": "60 days till 3650 days", + "description": "OCD_C_description", + "retention": "OCD_retention_60_days_till_3650_days", "dataController": "Adform", "gdprUrl": "https://site.adform.com/privacy-center/overview", "wildcard": "0" @@ -1500,8 +1890,8 @@ "category": "Marketing", "name": "uid", "domain": "adform.net (3rd party)", - "description": "Contains a unique ID to identify a user", - "retention": "60 days", + "description": "OCD_uid_description", + "retention": "OCD_retention_60_days", "dataController": "Adform", "gdprUrl": "https://site.adform.com/privacy-center/overview", "wildcard": "0" @@ -1513,8 +1903,8 @@ "category": "Marketing", "name": "cid", "domain": "adform.net (3rd party)", - "description": "Unique value to be able to identify cookies from users (same as uid)", - "retention": "60 days", + "description": "OCD_cid_description", + "retention": "OCD_retention_60_days", "dataController": "Adform", "gdprUrl": "https://site.adform.com/privacy-center/overview", "wildcard": "0" @@ -1526,8 +1916,8 @@ "category": "Marketing", "name": "GCM", "domain": "adform.net (3rd party)", - "description": "Checks if a new partner cookie synchronization is required", - "retention": "1 day", + "description": "OCD_GCM_description", + "retention": "OCD_retention_1_day", "dataController": "Adform", "gdprUrl": "https://site.adform.com/privacy-center/overview", "wildcard": "0" @@ -1539,8 +1929,8 @@ "category": "Marketing", "name": "CM", "domain": "adform.net (3rd party)", - "description": "Checks if a new partner cookie synchronization is required (cookie set by ad server)", - "retention": "1 day", + "description": "OCD_CM_description", + "retention": "OCD_retention_1_day", "dataController": "Adform", "gdprUrl": "https://site.adform.com/privacy-center/overview", "wildcard": "0" @@ -1552,8 +1942,8 @@ "category": "Marketing", "name": "CM14", "domain": "adform.net (3rd party)", - "description": "Checks if a new partner cookie synchronization is required (cookie set during cookie synchronization )", - "retention": "1 day", + "description": "OCD_CM14_description", + "retention": "OCD_retention_1_day", "dataController": "Adform", "gdprUrl": "https://site.adform.com/privacy-center/overview", "wildcard": "0" @@ -1565,8 +1955,8 @@ "category": "Marketing", "name": "token", "domain": "adform.net (3rd party)", - "description": "Security token for opt out functionality", - "retention": "End of session (browser)", + "description": "OCD_token_description", + "retention": "OCD_retention_End_of_session__browser_", "dataController": "Adform", "gdprUrl": "https://site.adform.com/privacy-center/overview", "wildcard": "0" @@ -1589,8 +1979,8 @@ "category": "Marketing", "name": "otsid", "domain": "adform.net (3rd party)", - "description": "Opt out cookie for specific advertiser", - "retention": "365 days", + "description": "OCD_otsid_description", + "retention": "OCD_retention_365_days", "dataController": "Adform", "gdprUrl": "https://site.adform.com/privacy-center/overview", "wildcard": "0" @@ -1602,8 +1992,8 @@ "category": "Marketing", "name": "adtrc", "domain": "adform.net (3rd party)", - "description": "Used to determine if browser related information has been collected", - "retention": "7 days", + "description": "OCD_adtrc_description", + "retention": "OCD_retention_7_days", "dataController": "Adform", "gdprUrl": "https://site.adform.com/privacy-center/overview", "wildcard": "0" @@ -1615,8 +2005,8 @@ "category": "Marketing", "name": "SR*", "domain": "adform.net (3rd party)", - "description": "Unique value that records info about consecutive ads - includes: total impressions, daily impressions, total clicks, daily clicks, and last impression date", - "retention": "1 day", + "description": "OCD_SR__description", + "retention": "OCD_retention_1_day", "dataController": "Adform", "gdprUrl": "https://site.adform.com/privacy-center/overview", "wildcard": "1" @@ -1628,8 +2018,8 @@ "category": "Marketing", "name": "CT*", "domain": "adform.net (3rd party)", - "description": "Identifies the last click membership for third-party pixels on advertiser's pages", - "retention": "1 hour", + "description": "OCD_CT__description", + "retention": "OCD_retention_1_hour", "dataController": "Adform", "gdprUrl": "https://site.adform.com/privacy-center/overview", "wildcard": "1" @@ -1641,8 +2031,8 @@ "category": "Marketing", "name": "EBFCD*", "domain": "adform.net (3rd party)", - "description": "Registers daily max. number of impressions (frequency cap) for expanding advertisements (expandables)", - "retention": "7 days", + "description": "OCD_EBFCD__description", + "retention": "OCD_retention_7_days", "dataController": "Adform", "gdprUrl": "https://site.adform.com/privacy-center/overview", "wildcard": "1" @@ -1654,8 +2044,8 @@ "category": "Marketing", "name": "EBFC*", "domain": "adform.net (3rd party)", - "description": "Registers max. total number of impressions (frequency cap) for expanding advertisements (expandables)", - "retention": "7 days", + "description": "OCD_EBFC__description", + "retention": "OCD_retention_7_days", "dataController": "Adform", "gdprUrl": "https://site.adform.com/privacy-center/overview", "wildcard": "1" @@ -1667,8 +2057,8 @@ "category": "Marketing", "name": "CFFC*", "domain": "adform.net (3rd party)", - "description": "Registers max. number of impressions (frequency cap) for compound banners", - "retention": "7 days", + "description": "OCD_CFFC__description", + "retention": "OCD_retention_7_days", "dataController": "Adform", "gdprUrl": "https://site.adform.com/privacy-center/overview", "wildcard": "1" @@ -1680,8 +2070,8 @@ "category": "Marketing", "name": "DigiTrust.v1.identity", "domain": "adform.net (3rd party)", - "description": "Unique value with which the user is identified by DigiTrust, an independent industrial body", - "retention": "7 days", + "description": "OCD_DigiTrust_v1_identity_description", + "retention": "OCD_retention_7_days", "dataController": "Adform", "gdprUrl": "https://site.adform.com/privacy-center/overview", "wildcard": "0" @@ -1693,8 +2083,8 @@ "category": "Marketing", "name": "adformfrpid", "domain": "", - "description": "Collects data on the user across websites - This data is used to make advertisement more relevant.", - "retention": "30 days", + "description": "OCD_adformfrpid_description", + "retention": "OCD_retention_30_days", "dataController": "Adform", "gdprUrl": "https://site.adform.com/privacy-center/overview", "wildcard": "0" @@ -1706,8 +2096,8 @@ "category": "Marketing", "name": "lu", "domain": "facebook.com (3rd party)", - "description": "Used to record whether the person chose to remain logged in\n\nContents: User ID and miscellaneous log in information (e.g., number of logins per account, state of the \"remember me\" check box, etc.)", - "retention": "2 year", + "description": "OCD_lu_description", + "retention": "OCD_retention_2_year", "dataController": "Facebook", "gdprUrl": "https://www.facebook.com/about/privacy/", "wildcard": "0" @@ -1719,8 +2109,8 @@ "category": "Marketing", "name": "xs", "domain": "facebook.com (3rd party)", - "description": "Used in conjunction with the c_user cookie to authenticate your identity to Facebook.\n\nContents: Session ID, creation time, authentication value, secure session state, caching group ID", - "retention": "90 days", + "description": "OCD_xs_description", + "retention": "OCD_retention_90_days", "dataController": "Facebook", "gdprUrl": "https://www.facebook.com/about/privacy/", "wildcard": "0" @@ -1732,8 +2122,8 @@ "category": "Marketing", "name": "c_user", "domain": "facebook.com (3rd party)", - "description": "Used in conjunction with the xs cookie to authenticate your identity to Facebook.\n\nContents: User ID", - "retention": "90 days", + "description": "OCD_c_user_description", + "retention": "OCD_retention_90_days", "dataController": "Facebook", "gdprUrl": "https://www.facebook.com/about/privacy/", "wildcard": "0" @@ -1745,8 +2135,8 @@ "category": "Marketing", "name": "m_user", "domain": "facebook.com (3rd party)", - "description": "Used to authenticate your identity on Facebook's mobile website.\n\nContents: Email, User ID, authentication value, version, user agent capability, creation time, Facebook version indicator", - "retention": "90 days", + "description": "OCD_m_user_description", + "retention": "OCD_retention_90_days", "dataController": "Facebook", "gdprUrl": "https://www.facebook.com/about/privacy/", "wildcard": "0" @@ -1758,8 +2148,8 @@ "category": "Marketing", "name": "pl", "domain": "facebook.com (3rd party)", - "description": "Used to record that a device or browser logged in via Facebook platform.\n\nContents: Y/N", - "retention": "90 days", + "description": "OCD_pl_description", + "retention": "OCD_retention_90_days", "dataController": "Facebook", "gdprUrl": "https://www.facebook.com/about/privacy/", "wildcard": "0" @@ -1771,8 +2161,8 @@ "category": "Marketing", "name": "dbln", "domain": "facebook.com (3rd party)", - "description": "Used to enable device-based logins\n\nContents: Login authentication values", - "retention": "2 years", + "description": "OCD_dbln_description", + "retention": "OCD_retention_2_years", "dataController": "Facebook", "gdprUrl": "https://www.facebook.com/about/privacy/", "wildcard": "0" @@ -1784,8 +2174,8 @@ "category": "Marketing", "name": "aks", "domain": "facebook.com (3rd party)", - "description": "Determines the login state of a person visiting accountkit.com\n\nContents: Account kit access token", - "retention": "30 days", + "description": "OCD_aks_description", + "retention": "OCD_retention_30_days", "dataController": "Facebook", "gdprUrl": "https://www.facebook.com/about/privacy/", "wildcard": "0" @@ -1797,8 +2187,8 @@ "category": "Marketing", "name": "aksb", "domain": "facebook.com (3rd party)", - "description": "Authenticates logins using Account Kit\n\nContents: Request time value", - "retention": "30 minutes", + "description": "OCD_aksb_description", + "retention": "OCD_retention_30_minutes", "dataController": "Facebook", "gdprUrl": "https://www.facebook.com/about/privacy/", "wildcard": "0" @@ -1810,8 +2200,8 @@ "category": "Marketing", "name": "sfau", "domain": "facebook.com (3rd party)", - "description": "Optimizes recovery flow after failed login attempts\n\nContents: Encrypted user ID, contact point, time stamp, and other login information", - "retention": "1 day", + "description": "OCD_sfau_description", + "retention": "OCD_retention_1_day", "dataController": "Facebook", "gdprUrl": "https://www.facebook.com/about/privacy/", "wildcard": "0" @@ -1823,8 +2213,8 @@ "category": "Marketing", "name": "ick", "domain": "facebook.com (3rd party)", - "description": "Stores an encryption key used to encrypt cookies", - "retention": "2 years", + "description": "OCD_ick_description", + "retention": "OCD_retention_2_years", "dataController": "Facebook", "gdprUrl": "https://www.facebook.com/about/privacy/", "wildcard": "0" @@ -1836,8 +2226,8 @@ "category": "Marketing", "name": "csm", "domain": "facebook.com (3rd party)", - "description": "Insecure indicator", - "retention": "90 days", + "description": "OCD_csm_description", + "retention": "OCD_retention_90_days", "dataController": "Facebook", "gdprUrl": "https://www.facebook.com/about/privacy/", "wildcard": "0" @@ -1849,8 +2239,8 @@ "category": "Marketing", "name": "s", "domain": "facebook.com (3rd party)", - "description": "\nFacebook browser identification, authentication, marketing, and other Facebook-specific function cookies.", - "retention": "90 days", + "description": "OCD_s_description", + "retention": "OCD_retention_90_days", "dataController": "Facebook", "gdprUrl": "https://www.facebook.com/about/privacy/", "wildcard": "0" @@ -1862,8 +2252,8 @@ "category": "Marketing", "name": "datr", "domain": "facebook.com (3rd party)", - "description": "Used to prevent creation of fake / spammy accounts. Datr cookie is associated with a browser, not individual people.", - "retention": "2 years", + "description": "OCD_datr_description", + "retention": "OCD_retention_2_years", "dataController": "Facebook", "gdprUrl": "https://www.facebook.com/about/privacy/", "wildcard": "0" @@ -1875,8 +2265,8 @@ "category": "Marketing", "name": "sb", "domain": "facebook.com (3rd party)", - "description": "Facebook browser identification, authentication, marketing, and other Facebook-specific function cookies.", - "retention": "2 years", + "description": "OCD_sb_description", + "retention": "OCD_retention_2_years", "dataController": "Facebook", "gdprUrl": "https://www.facebook.com/about/privacy/", "wildcard": "0" @@ -1888,8 +2278,8 @@ "category": "Marketing", "name": "fr", "domain": "facebook.com (3rd party)", - "description": "Contains a unique browser and user ID, used for targeted advertising.", - "retention": "90 days", + "description": "OCD_fr_description", + "retention": "OCD_retention_90_days", "dataController": "Facebook", "gdprUrl": "https://www.facebook.com/about/privacy/", "wildcard": "0" @@ -1901,8 +2291,8 @@ "category": "Marketing", "name": "oo", "domain": "facebook.com (3rd party)", - "description": "Ad optout cookie", - "retention": "5 years", + "description": "OCD_oo_description", + "retention": "OCD_retention_5_years", "dataController": "Facebook", "gdprUrl": "https://www.facebook.com/about/privacy/", "wildcard": "0" @@ -1914,8 +2304,8 @@ "category": "Marketing", "name": "ddid", "domain": "facebook.com (3rd party)", - "description": "Used to open a specific location in an advertiser's app upon installation", - "retention": "28 days", + "description": "OCD_ddid_description", + "retention": "OCD_retention_28_days", "dataController": "Facebook", "gdprUrl": "https://www.facebook.com/about/privacy/", "wildcard": "0" @@ -1927,8 +2317,8 @@ "category": "Marketing", "name": "locale", "domain": "facebook.com (3rd party)", - "description": "This cookie contains the display locale of the last logged in user on this browser. This cookie\nappears to only be set after the user logs out.\nThe locale cookie has a lifetime of one week.", - "retention": "7 days", + "description": "OCD_locale_description", + "retention": "OCD_retention_7_days", "dataController": "Facebook", "gdprUrl": "https://www.facebook.com/about/privacy/", "wildcard": "0" @@ -1940,8 +2330,8 @@ "category": "Marketing", "name": "_fbp", "domain": "facebook.com (3rd party)", - "description": "Used by Facebook to deliver a series of advertisement products such as real time bidding from third party advertisers", - "retention": "4 months", + "description": "OCD__fbp_description", + "retention": "OCD_retention_4_months", "dataController": "Facebook", "gdprUrl": "https://www.facebook.com/about/privacy/", "wildcard": "0" @@ -1953,8 +2343,8 @@ "category": "Marketing", "name": "_fbc", "domain": "facebook.com (3rd party)", - "description": "Used by Facebook to deliver a series of advertisement products such as real time bidding from third party advertisers", - "retention": "2 years", + "description": "OCD__fbc_description", + "retention": "OCD_retention_2_years", "dataController": "Facebook", "gdprUrl": "https://www.facebook.com/about/privacy/", "wildcard": "0" @@ -1966,8 +2356,8 @@ "category": "Marketing", "name": "js_ver", "domain": "facebook.com (3rd party)", - "description": "Records the age of Facebook javascript files.", - "retention": "7 days", + "description": "OCD_js_ver_description", + "retention": "OCD_retention_7_days", "dataController": "Facebook", "gdprUrl": "https://www.facebook.com/about/privacy/", "wildcard": "0" @@ -1979,8 +2369,8 @@ "category": "Marketing", "name": "rc", "domain": "facebook.com (3rd party)", - "description": "Used to optimize site performance for advertisers", - "retention": "7 days", + "description": "OCD_rc_description", + "retention": "OCD_retention_7_days", "dataController": "Facebook", "gdprUrl": "https://www.facebook.com/about/privacy/", "wildcard": "0" @@ -1992,8 +2382,8 @@ "category": "Marketing", "name": "campaign_click_url", "domain": "facebook.com (3rd party)", - "description": "Records the Facebook URL that an individual landed on after clicking on an ad promoting Facebook", - "retention": "30 days", + "description": "OCD_campaign_click_url_description", + "retention": "OCD_retention_30_days", "dataController": "Facebook", "gdprUrl": "https://www.facebook.com/about/privacy/", "wildcard": "0" @@ -2005,8 +2395,8 @@ "category": "Functional", "name": "wd", "domain": "facebook.com (3rd party)", - "description": "This cookie stores the browser window dimensions and is used by Facebook to optimise the rendering of the page.", - "retention": "Session", + "description": "OCD_wd_description", + "retention": "OCD_retention_Session", "dataController": "Facebook", "gdprUrl": "https://www.facebook.com/about/privacy/", "wildcard": "0" @@ -2018,8 +2408,8 @@ "category": "Marketing", "name": "usida", "domain": "facebook.com (3rd party)", - "description": "Collects a combination of the user’s browser and unique identifier, used to tailor advertising to users.", - "retention": "Session", + "description": "OCD_usida_description", + "retention": "OCD_retention_Session", "dataController": "Facebook", "gdprUrl": "https://www.facebook.com/about/privacy/", "wildcard": "0" @@ -2031,8 +2421,8 @@ "category": "Functional", "name": "presence", "domain": "facebook.com (3rd party)", - "description": "The presence cookie is used to contain the user’s chat state.", - "retention": "Session", + "description": "OCD_presence_description", + "retention": "OCD_retention_Session", "dataController": "Facebook", "gdprUrl": "https://www.facebook.com/about/privacy/", "wildcard": "0" @@ -2044,8 +2434,8 @@ "category": "Marketing", "name": "fl_inst", "domain": "creative-serving.com (3rd party)", - "description": "Used to check if Flash plugin is enabled in browser of user.", - "retention": "7 days", + "description": "OCD_fl_inst_description", + "retention": "OCD_retention_7_days", "dataController": "Platform161", "gdprUrl": "https://platform161.com/cookie-and-privacy-policy/", "wildcard": "0" @@ -2057,8 +2447,8 @@ "category": "Marketing", "name": "pvc2", "domain": "creative-serving.com (3rd party)", - "description": "Contains information related to ad impressions.", - "retention": "13 months", + "description": "OCD_pvc2_description", + "retention": "OCD_retention_13_months", "dataController": "Platform161", "gdprUrl": "https://platform161.com/cookie-and-privacy-policy/", "wildcard": "0" @@ -2070,8 +2460,8 @@ "category": "Marketing", "name": "pcc2", "domain": "creative-serving.com (3rd party)", - "description": "Contains information related to ad impressions.", - "retention": "13 months", + "description": "OCD_pcc2_description", + "retention": "OCD_retention_13_months", "dataController": "Platform161", "gdprUrl": "https://platform161.com/cookie-and-privacy-policy/", "wildcard": "0" @@ -2083,8 +2473,8 @@ "category": "Marketing", "name": "trc", "domain": "creative-serving.com (3rd party)", - "description": "Contains information related to ad impressions.", - "retention": "13 months", + "description": "OCD_trc_description", + "retention": "OCD_retention_13_months", "dataController": "Platform161", "gdprUrl": "https://platform161.com/cookie-and-privacy-policy/", "wildcard": "0" @@ -2096,8 +2486,8 @@ "category": "Marketing", "name": "tuuid", "domain": "creative-serving.com (3rd party)", - "description": "Unique value to identify individual users.", - "retention": "13 months", + "description": "OCD_tuuid_description", + "retention": "OCD_retention_13_months", "dataController": "Platform161", "gdprUrl": "https://platform161.com/cookie-and-privacy-policy/", "wildcard": "0" @@ -2109,8 +2499,8 @@ "category": "Marketing", "name": "ad2", "domain": "creative-serving.com (3rd party)", - "description": "Contains information related to ad impressions.", - "retention": "13 months", + "description": "OCD_ad2_description", + "retention": "OCD_retention_13_months", "dataController": "Platform161", "gdprUrl": "https://platform161.com/cookie-and-privacy-policy/", "wildcard": "0" @@ -2122,8 +2512,8 @@ "category": "Marketing", "name": "MR", "domain": ".bing.com (3rd party) or .microsoft.com (3rd party)", - "description": "Used to collect information for analytics purposes.", - "retention": "6 months", + "description": "OCD_MR_description", + "retention": "OCD_retention_6_months", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2135,8 +2525,8 @@ "category": "Marketing", "name": "MUID", "domain": ".bing.com (3rd party) or .microsoft.com (3rd party)", - "description": "Identifies unique web browsers visiting Microsoft sites. These cookies are used for advertising, site analytics, and other operational purposes.", - "retention": "1 year", + "description": "OCD_MUID_description", + "retention": "OCD_retention_1_year", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2148,8 +2538,8 @@ "category": "Marketing", "name": "MUIDB", "domain": ".bing.com (3rd party) or .microsoft.com (3rd party)", - "description": "Identifies unique web browsers visiting Microsoft sites. These cookies are used for advertising, site analytics, and other operational purposes.", - "retention": "1 year", + "description": "OCD_MUIDB_description", + "retention": "OCD_retention_1_year", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2161,8 +2551,8 @@ "category": "Marketing", "name": "MC1", "domain": ".bing.com (3rd party) or .microsoft.com (3rd party)", - "description": "Identifies unique web browsers visiting Microsoft sites. These cookies are used for advertising, site analytics, and other operational purposes.", - "retention": "1 year", + "description": "OCD_MC1_description", + "retention": "OCD_retention_1_year", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2174,8 +2564,8 @@ "category": "Marketing", "name": "MSFPC", "domain": ".bing.com (3rd party) or .microsoft.com (3rd party)", - "description": "Identifies unique web browsers visiting Microsoft sites. These cookies are used for advertising, site analytics, and other operational purposes.", - "retention": "1 year", + "description": "OCD_MSFPC_description", + "retention": "OCD_retention_1_year", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2187,8 +2577,8 @@ "category": "Marketing", "name": "_uetsid", "domain": ".bing.com (3rd party) or .microsoft.com (3rd party)", - "description": "This cookie is used by Bing to determine what ads should be shown that may be relevant to the end user perusing the site.", - "retention": "30 minutes", + "description": "OCD__uetsid_description", + "retention": "OCD_retention_30_minutes", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2200,8 +2590,8 @@ "category": "Marketing", "name": "_uetvid", "domain": ".bing.com (3rd party) or .microsoft.com (3rd party)", - "description": "This is a cookie utilised by Microsoft Bing Ads and is a tracking cookie. It allows us to engage with a user that has previously visited our website.", - "retention": "16 days", + "description": "OCD__uetvid_description", + "retention": "OCD_retention_16_days", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2213,8 +2603,8 @@ "category": "Marketing", "name": "ANON", "domain": "microsoft.com (3rd party)", - "description": "Contains the A, a unique identifier derived from your Microsoft account, which is used for advertising, personalization, and operational purposes. It is also used to preserve your choice to opt out of interest-based advertising from Microsoft if you have chosen to associate the opt-out with your Microsoft account.", - "retention": "1 year", + "description": "OCD_ANON_description", + "retention": "OCD_retention_1_year", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2226,8 +2616,8 @@ "category": "Marketing", "name": "ANONCHK", "domain": "microsoft.com (3rd party)", - "description": "Used to store session ID for a users session to ensure that clicks from adverts on the Bing search engine are verified for reporting purposes and for personalisation", - "retention": "10 minutes", + "description": "OCD_ANONCHK_description", + "retention": "OCD_retention_10_minutes", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2239,8 +2629,8 @@ "category": "Marketing", "name": "CC", "domain": "microsoft.com (3rd party)", - "description": "Contains a country code as determined from your IP address.", - "retention": "1 year", + "description": "OCD_CC_description", + "retention": "OCD_retention_1_year", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2252,8 +2642,8 @@ "category": "Functional", "name": "PPAuth", "domain": "microsoft.com (3rd party)", - "description": "Helps to authenticate you when you sign in with your Microsoft account.", - "retention": "5 years", + "description": "OCD_PPAuth_description", + "retention": "OCD_retention_5_years", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2265,8 +2655,8 @@ "category": "Functional", "name": "MSPAuth", "domain": "microsoft.com (3rd party)", - "description": "Helps to authenticate you when you sign in with your Microsoft account.", - "retention": "5 years", + "description": "OCD_MSPAuth_description", + "retention": "OCD_retention_5_years", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2278,8 +2668,8 @@ "category": "Functional", "name": "MSNRPSAuth", "domain": "microsoft.com (3rd party)", - "description": "Helps to authenticate you when you sign in with your Microsoft account.", - "retention": "5 years", + "description": "OCD_MSNRPSAuth_description", + "retention": "OCD_retention_5_years", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2291,8 +2681,8 @@ "category": "Functional", "name": "KievRPSAuth", "domain": "microsoft.com (3rd party)", - "description": "Helps to authenticate you when you sign in with your Microsoft account.", - "retention": "5 years", + "description": "OCD_KievRPSAuth_description", + "retention": "OCD_retention_5_years", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2304,8 +2694,8 @@ "category": "Functional", "name": "WLSSC", "domain": "microsoft.com (3rd party)", - "description": "Helps to authenticate you when you sign in with your Microsoft account.", - "retention": "5 years", + "description": "OCD_WLSSC_description", + "retention": "OCD_retention_5_years", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2317,8 +2707,8 @@ "category": "Functional", "name": "MSPProf", "domain": "microsoft.com (3rd party)", - "description": "Helps to authenticate you when you sign in with your Microsoft account.", - "retention": "5 years", + "description": "OCD_MSPProf_description", + "retention": "OCD_retention_5_years", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2330,8 +2720,8 @@ "category": "Functional", "name": "MC0", "domain": "microsoft.com (3rd party)", - "description": "Detects whether cookies are enabled in the browser.", - "retention": "End of session (browser)", + "description": "OCD_MC0_description", + "retention": "OCD_retention_End_of_session__browser_", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2343,8 +2733,8 @@ "category": "Functional", "name": "MS0", "domain": "microsoft.com (3rd party)", - "description": "Identifies a specific session.", - "retention": "End of session (browser)", + "description": "OCD_MS0_description", + "retention": "OCD_retention_End_of_session__browser_", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2356,8 +2746,8 @@ "category": "Marketing", "name": "NAP", "domain": "microsoft.com (3rd party)", - "description": "Contains an encrypted version of your country, postal code, age, gender, language and occupation, if known, based on your Microsoft account profile.", - "retention": "1 year", + "description": "OCD_NAP_description", + "retention": "OCD_retention_1_year", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2369,8 +2759,8 @@ "category": "Marketing", "name": "MH", "domain": "microsoft.com (3rd party)", - "description": "Appears on co-branded sites where Microsoft is partnering with an advertiser. This cookie identifies the advertiser, so the right ad is selected.", - "retention": "End of session (browser)", + "description": "OCD_MH_description", + "retention": "OCD_retention_End_of_session__browser_", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2382,8 +2772,8 @@ "category": "Marketing", "name": "childinfo", "domain": "microsoft.com (3rd party)", - "description": "Contains information that Microsoft account uses within its pages in relation to child accounts.", - "retention": "5 years", + "description": "OCD_childinfo_description", + "retention": "OCD_retention_5_years", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2395,8 +2785,8 @@ "category": "Marketing", "name": "kcdob", "domain": "microsoft.com (3rd party)", - "description": "Contains information that Microsoft account uses within its pages in relation to child accounts.", - "retention": "5 years", + "description": "OCD_kcdob_description", + "retention": "OCD_retention_5_years", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2408,8 +2798,8 @@ "category": "Marketing", "name": "kcrelid", "domain": "microsoft.com (3rd party)", - "description": "Contains information that Microsoft account uses within its pages in relation to child accounts.", - "retention": "5 years", + "description": "OCD_kcrelid_description", + "retention": "OCD_retention_5_years", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2421,8 +2811,8 @@ "category": "Marketing", "name": "kcru", "domain": "microsoft.com (3rd party)", - "description": "Contains information that Microsoft account uses within its pages in relation to child accounts.", - "retention": "5 years", + "description": "OCD_kcru_description", + "retention": "OCD_retention_5_years", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2434,8 +2824,8 @@ "category": "Marketing", "name": "pcfm", "domain": "microsoft.com (3rd party)", - "description": "Contains information that Microsoft account uses within its pages in relation to child accounts.", - "retention": "5 years", + "description": "OCD_pcfm_description", + "retention": "OCD_retention_5_years", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2447,8 +2837,8 @@ "category": "Functional", "name": "x-ms-gateway-slice", "domain": "microsoft.com (3rd party)", - "description": "Identifies a gateway for load balancing.", - "retention": "End of session (browser)", + "description": "OCD_x_ms_gateway_slice_description", + "retention": "OCD_retention_End_of_session__browser_", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2460,8 +2850,8 @@ "category": "Marketing", "name": "ToptOut", "domain": ".bing.com (3rd party) or .microsoft.com (3rd party)", - "description": "Records your decision not to receive interest-based advertising delivered by Microsoft.", - "retention": "1 year", + "description": "OCD_ToptOut_description", + "retention": "OCD_retention_1_year", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2473,8 +2863,8 @@ "category": "Marketing", "name": "ACH01", "domain": ".bing.com (3rd party) or .microsoft.com (3rd party)", - "description": "Maintains information about which ad and where the user clicked on the ad.", - "retention": "End of session (browser)", + "description": "OCD_ACH01_description", + "retention": "OCD_retention_End_of_session__browser_", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2486,8 +2876,8 @@ "category": "Functional", "name": "AADSSO", "domain": "microsoft.com (3rd party)", - "description": "Microsoft Microsoft Online Authentication Cookie", - "retention": "End of session (browser)", + "description": "OCD_AADSSO_description", + "retention": "OCD_retention_End_of_session__browser_", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2499,8 +2889,8 @@ "category": "Functional", "name": "brcap", "domain": "microsoft.com (3rd party)", - "description": "Microsoft Microsoft Online Authentication Cookie", - "retention": "1 year", + "description": "OCD_brcap_description", + "retention": "OCD_retention_1_year", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2512,8 +2902,8 @@ "category": "Functional", "name": "SRM_B", "domain": "microsoft.com (3rd party)", - "description": "Collected user data is specifically adapted to the user or device. The usercan also be followed outside of the loaded website, creating a picture of the visitor's behavior.", - "retention": "1 year", + "description": "OCD_SRM_B_description", + "retention": "OCD_retention_1_year", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2525,8 +2915,8 @@ "category": "Marketing", "name": "_RwBf", "domain": "bing.com", - "description": "This cookie helps us to track the effectiveness of advertising campaigns on the Bing advertising network.", - "retention": "1 year", + "description": "OCD__RwBf_description", + "retention": "OCD_retention_1_year", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2538,8 +2928,8 @@ "category": "Marketing", "name": "_HPVN", "domain": "bing.com", - "description": "Analysis service that connects data from the Bing advertising network with actions performed on the website.", - "retention": "1 year", + "description": "OCD__HPVN_description", + "retention": "OCD_retention_1_year", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2551,8 +2941,8 @@ "category": "Marketing", "name": "_UR", "domain": "bing.com", - "description": "This cookie is used by the Bing advertising network for advertising tracking purposes.", - "retention": "1 year", + "description": "OCD__UR_description", + "retention": "OCD_retention_1_year", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2564,8 +2954,8 @@ "category": "Marketing", "name": "OID", "domain": "bing.com", - "description": "This cookie is used by the Bing advertising network for advertising tracking purposes.", - "retention": "3 months", + "description": "OCD_OID_description", + "retention": "OCD_retention_3_months", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2577,8 +2967,8 @@ "category": "Marketing", "name": "OIDI", "domain": "bing.com", - "description": "This cookie is used by the Bing advertising network for advertising tracking purposes.", - "retention": "3 months", + "description": "OCD_OIDI_description", + "retention": "OCD_retention_3_months", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2590,8 +2980,8 @@ "category": "Marketing", "name": "OIDR", "domain": "bing.com", - "description": "This cookie is used by the Bing advertising network for advertising tracking purposes.", - "retention": "3 months", + "description": "OCD_OIDR_description", + "retention": "OCD_retention_3_months", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2603,8 +2993,8 @@ "category": "Marketing", "name": "BCP", "domain": "bing.com", - "description": "This cookie is used for advertisement tracking purposes.", - "retention": "1 year", + "description": "OCD_BCP_description", + "retention": "OCD_retention_1_year", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2616,8 +3006,8 @@ "category": "Marketing", "name": "BFBUSR", "domain": "bing.com", - "description": "This cookie is used for advertisement tracking purposes.", - "retention": "1 year", + "description": "OCD_BFBUSR_description", + "retention": "OCD_retention_1_year", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2629,8 +3019,8 @@ "category": "Marketing", "name": "BFB", "domain": "bing.com", - "description": "This cookie is used for advertisement tracking purposes.", - "retention": "1 year", + "description": "OCD_BFB_description", + "retention": "OCD_retention_1_year", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2642,8 +3032,8 @@ "category": "Marketing", "name": "ACL", "domain": "bing.com", - "description": "This cookie is used for advertisement tracking purposes.", - "retention": "3 months", + "description": "OCD_ACL_description", + "retention": "OCD_retention_3_months", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2655,8 +3045,8 @@ "category": "Marketing", "name": "ACLUSR", "domain": "bing.com", - "description": "This cookie is used for advertisement tracking purposes.", - "retention": "1 year", + "description": "OCD_ACLUSR_description", + "retention": "OCD_retention_1_year", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2668,8 +3058,8 @@ "category": "Marketing", "name": "MSPTC", "domain": "bing.com", - "description": "This cookie registers data on the visitor. The information is used to optimize advertisement relevance.", - "retention": "1 year", + "description": "OCD_MSPTC_description", + "retention": "OCD_retention_1_year", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2681,8 +3071,8 @@ "category": "Functional", "name": "buid", "domain": "microsoft.com (3rd party)", - "description": "This cookie is used by Microsoft to securely verify your login information", - "retention": "1 month", + "description": "OCD_buid_description", + "retention": "OCD_retention_1_month", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2694,8 +3084,8 @@ "category": "Functional", "name": "esctx", "domain": "microsoft.com (3rd party)", - "description": "This cookie is used by Microsoft to securely verify your login information", - "retention": "session", + "description": "OCD_esctx_description", + "retention": "OCD_retention_session", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2707,8 +3097,8 @@ "category": "Functional", "name": "fpc", "domain": "microsoft.com (3rd party)", - "description": "This cookie is used by Microsoft to securely verify your login information", - "retention": "session", + "description": "OCD_fpc_description", + "retention": "OCD_retention_session", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2720,8 +3110,8 @@ "category": "Functional", "name": "stsservicecookie", "domain": "microsoft.com (3rd party)", - "description": "Cookie for Azure Active Directory B2C-verification", - "retention": "session", + "description": "OCD_stsservicecookie_description", + "retention": "OCD_retention_session", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2733,8 +3123,8 @@ "category": "Functional", "name": "ARRAffinity", "domain": "", - "description": "When using Microsoft Azure as a hosting platform and enabling load balancing, this cookie ensures that requests from one visitor's browsing session are always handled by the same server in the cluster.", - "retention": "1 year", + "description": "OCD_ARRAffinity_description", + "retention": "OCD_retention_1_year", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2746,8 +3136,8 @@ "category": "Functional", "name": "ARRAffinitySameSite", "domain": "", - "description": "When using Microsoft Azure as a hosting platform and enabling load balancing, this cookie ensures that requests from one visitor's browsing session are always handled by the same server in the cluster.", - "retention": "1 year", + "description": "OCD_ARRAffinitySameSite_description", + "retention": "OCD_retention_1_year", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2759,8 +3149,8 @@ "category": "Functional", "name": "__AntiXsrfToken", "domain": "", - "description": "This cookie is used to prevent Cross-site request forgery (often abbreviated as CSRF) attacks of the website. CSRF attacks exploit the trust that a site has in a user's browser.", - "retention": "session", + "description": "OCD___AntiXsrfToken_description", + "retention": "OCD_retention_session", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2772,8 +3162,8 @@ "category": "Functional", "name": ".ASPXANONYMOUS", "domain": "", - "description": "Created by ASP.Net. This cookie configures anonymous identification for application authorization. This is required to identify entities that are not authenticated when authorization is required.", - "retention": "session", + "description": "OCD__ASPXANONYMOUS_description", + "retention": "OCD_retention_session", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2785,8 +3175,8 @@ "category": "Functional", "name": ".ASPXAUTH", "domain": "", - "description": "Created by ASP.Net. .ASPXAUTH is a cookie to identify if the user is authenticated( As user's identity has been verified)", - "retention": "session", + "description": "OCD__ASPXAUTH_description", + "retention": "OCD_retention_session", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2798,8 +3188,8 @@ "category": "Functional", "name": "nSGt-*", "domain": "microsoft.com (3rd party)", - "description": "This cookie is used by Microsoft to securely verify your Sharepoint login information", - "retention": "session", + "description": "OCD_nSGt___description", + "retention": "OCD_retention_session", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "1" @@ -2811,8 +3201,8 @@ "category": "Functional", "name": "RpsContextCookie", "domain": "microsoft.com (3rd party)", - "description": "This cookie is used by Microsoft to securely verify your Sharepoint login information", - "retention": "session", + "description": "OCD_RpsContextCookie_description", + "retention": "OCD_retention_session", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2824,8 +3214,8 @@ "category": "Functional", "name": "ASLBSACORS", "domain": "", - "description": "Microsoft App Service and Front Door Affinity Cookies. These cookies are used to direct your browser to use the appropriate backend server.", - "retention": "Session", + "description": "OCD_ASLBSACORS_description", + "retention": "OCD_retention_Session", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2837,8 +3227,8 @@ "category": "Functional", "name": "ASLBSA", "domain": "", - "description": "Microsoft App Service and Front Door Affinity Cookies. These cookies are used to direct your browser to use the appropriate backend server.", - "retention": "Session", + "description": "OCD_ASLBSA_description", + "retention": "OCD_retention_Session", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2850,8 +3240,8 @@ "category": "Functional", "name": "ASPSESSIO*", "domain": "", - "description": "Browsing session: the asterisks identify an alphanumerical code that varies from session to session in automatic mode.", - "retention": "Session", + "description": "OCD_ASPSESSIO__description", + "retention": "OCD_retention_Session", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "1" @@ -2863,8 +3253,8 @@ "category": "Functional", "name": "ApplicationGatewayAffinity", "domain": "", - "description": "This cookie is used by Azure Apps to keep a user session on the same server.", - "retention": "Session", + "description": "OCD_ApplicationGatewayAffinity_description", + "retention": "OCD_retention_Session", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2876,8 +3266,8 @@ "category": "Functional", "name": "ApplicationGatewayAffinityCORS", "domain": "", - "description": "This cookie is used by Azure Apps to keep a user session on the same server.", - "retention": "Session", + "description": "OCD_ApplicationGatewayAffinityCORS_description", + "retention": "OCD_retention_Session", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2889,8 +3279,8 @@ "category": "Functional", "name": "VisitorStorageGuid", "domain": "", - "description": "This cookie is used by Azure Apps to keep a user session on the same server.", - "retention": "Session", + "description": "OCD_VisitorStorageGuid_description", + "retention": "OCD_retention_Session", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2902,8 +3292,8 @@ "category": "Functional", "name": "ai_session", "domain": "", - "description": "This is a unique anonymous session identifier cookie.", - "retention": "Session", + "description": "OCD_ai_session_description", + "retention": "OCD_retention_Session", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2915,8 +3305,8 @@ "category": "Functional", "name": "ai_user", "domain": "", - "description": "This is a unique user identifier cookie enabling counting of the number of users accessing the application over time.", - "retention": "1 year", + "description": "OCD_ai_user_description", + "retention": "OCD_retention_1_year", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2928,8 +3318,8 @@ "category": "Functional", "name": "AADNonce.forms", "domain": "forms.office.comm", - "description": "Unique identifier of one authentication session to prevent replay.", - "retention": "session", + "description": "OCD_AADNonce_forms_description", + "retention": "OCD_retention_session", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2941,8 +3331,8 @@ "category": "Functional", "name": "DcLcid", "domain": "forms.office.comm", - "description": "Saves language preference.", - "retention": "90 days", + "description": "OCD_DcLcid_description", + "retention": "OCD_retention_90_days", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2954,8 +3344,8 @@ "category": "Analytics", "name": "_clck", "domain": "clarity.ms", - "description": " This cookie is installed by Microsoft Clarity to store information of how visitors use a website and help in creating an analytics report of how the website is doing. The data collected including the number visitors, the source where they have come from, and the pages visited in an anonymous form.", - "retention": "1 year", + "description": "OCD__clck_description", + "retention": "OCD_retention_1_year", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2967,8 +3357,8 @@ "category": "Analytics", "name": "_clsk", "domain": "clarity.ms", - "description": "This cookie is installed by Microsoft Clarity to store information of how visitors use a website and help in creating an analytics report of how the website is doing. The data collected including the number visitors, the source where they have come from, and the pages visited in an anonymous form.", - "retention": "session", + "description": "OCD__clsk_description", + "retention": "OCD_retention_session", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2980,8 +3370,8 @@ "category": "Analytics", "name": "SM", "domain": "clarity.ms", - "description": "This is a Microsoft cookie which we use to measure the use of the website for internal analytics", - "retention": "session", + "description": "OCD_SM_description", + "retention": "OCD_retention_session", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -2993,8 +3383,8 @@ "category": "Analytics", "name": "CLID", "domain": "clarity.ms", - "description": "The cookie is set by embedded Microsoft Clarity scripts. The purpose of this cookie is for heatmap and session recording.", - "retention": "1 year", + "description": "OCD_CLID_description", + "retention": "OCD_retention_1_year", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -3006,8 +3396,8 @@ "category": "Analytics", "name": "MicrosoftApplicationsTelemetryDeviceId", "domain": "", - "description": "Used to store a unique device ID for tracking behavior and usage of the website", - "retention": "1 year", + "description": "OCD_MicrosoftApplicationsTelemetryDeviceId_description", + "retention": "OCD_retention_1_year", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -3019,8 +3409,8 @@ "category": "Functional", "name": "esctx-*", "domain": ".login.microsoftonline.com", - "description": "This cookie is set by Microsoft for secure authentication of the users' login details", - "retention": "session", + "description": "OCD_esctx___description", + "retention": "OCD_retention_session", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "1" @@ -3032,8 +3422,8 @@ "category": "Functional", "name": "ASP.NET_SessionId", "domain": "", - "description": "ASP.Net_SessionId is a cookie which is used to identify the users session on the server. The session being an area on the server which can be used to store session state in between http requests.", - "retention": "session", + "description": "OCD_ASP_NET_SessionId_description", + "retention": "OCD_retention_session", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -3045,8 +3435,8 @@ "category": "Marketing", "name": "guest_id", "domain": "twitter.com (3rd party)", - "description": "This cookie is set by X to identify and track the website visitor. Registers if a users is signed in the X platform and collects information about ad preferences.", - "retention": "2 years", + "description": "OCD_guest_id_description", + "retention": "OCD_retention_2_years", "dataController": "X", "gdprUrl": "https://help.twitter.com/nl/safety-and-security#ads-and-data-privacy", "wildcard": "0" @@ -3058,11 +3448,22 @@ "category": "Marketing", "name": "personalization_id", "domain": "twitter.com (3rd party)", - "description": "Unique value with which users can be identified by X. Collected information is used to be personalize X services, including X trends, stories, ads and suggestions.", - "retention": "2 years", + "description": "OCD_personalization_id_description", + "retention": "OCD_retention_2_years", "dataController": "X", "gdprUrl": "https://help.twitter.com/nl/safety-and-security#ads-and-data-privacy", "wildcard": "0" + }, + { + "platform": "Wistia", + "category": "Functional", + "name": "personalization_id", + "domain": "", + "description": "This cookie is used by the Wistia video player to remember where you are in a video so that if playback is interrupted (for example, by losing your internet connection) then you can get right back to where you left off.", + "retention": "1 Year", + "dataController": "Wistia", + "gdprUrl": "https://wistia.com/privacy", + "wildcard": "0" } ], "ct0": [ @@ -3071,8 +3472,8 @@ "category": "Marketing", "name": "ct0", "domain": "twitter.com (3rd party)", - "description": "These cookies enable us to track visitor activity from our X ads on our website, and also to allow users to share content from our websites. They cookies do not provide us with any confidential information relating to your account.", - "retention": "2 years", + "description": "OCD_ct0_description", + "retention": "OCD_retention_2_years", "dataController": "X", "gdprUrl": "https://twitter.com/en/privacy", "wildcard": "0" @@ -3084,8 +3485,8 @@ "category": "Marketing", "name": "gt", "domain": "twitter.com (3rd party)", - "description": "Twitter uses these cookies to support plugin integration with our website. If you use the Tweet plugin and log into your X account, X will set some of these cookies to remember that you are logged in. X will also use cookies for their own analytics purposes.", - "retention": "1 year", + "description": "OCD_gt_description", + "retention": "OCD_retention_1_year", "dataController": "X", "gdprUrl": "https://twitter.com/en/privacy", "wildcard": "0" @@ -3097,8 +3498,8 @@ "category": "Marketing", "name": "guest_id_marketing", "domain": "twitter.com (3rd party)", - "description": "This cookie is for advertising when logged out", - "retention": "2 years", + "description": "OCD_guest_id_marketing_description", + "retention": "OCD_retention_2_years", "dataController": "X", "gdprUrl": "https://twitter.com/en/privacy", "wildcard": "0" @@ -3110,8 +3511,8 @@ "category": "Marketing", "name": "guest_id_ads", "domain": "twitter.com (3rd party)", - "description": "This cookie is for advertising when logged out", - "retention": "2 years", + "description": "OCD_guest_id_ads_description", + "retention": "OCD_retention_2_years", "dataController": "X", "gdprUrl": "https://twitter.com/en/privacy", "wildcard": "0" @@ -3123,8 +3524,8 @@ "category": "Marketing", "name": "muc_ads", "domain": "t.co", - "description": "These cookies are placed when you come to our website via X. A cookie from X is also placed on our website, with which we can later show a relevant offer on X", - "retention": "24 months", + "description": "OCD_muc_ads_description", + "retention": "OCD_retention_24_months", "dataController": "X", "gdprUrl": "https://twitter.com/en/privacy", "wildcard": "0" @@ -3136,8 +3537,8 @@ "category": "Marketing", "name": "_twitter_sess", "domain": "twitter.com (3rd party)", - "description": "This cookie is set due to X integration and sharing capabilities for the social media.", - "retention": "Session", + "description": "OCD__twitter_sess_description", + "retention": "OCD_retention_Session", "dataController": "X", "gdprUrl": "https://twitter.com/en/privacy", "wildcard": "0" @@ -3149,8 +3550,8 @@ "category": "Marketing", "name": "ads_prefs", "domain": ".twitter.com", - "description": "These cookies enable us to track visitor activity from our X ads on our website, and also to allow users to share content from our websites. They cookies do not provide us with any confidential information relating to your account.", - "retention": "2 years", + "description": "OCD_ads_prefs_description", + "retention": "OCD_retention_2_years", "dataController": "X", "gdprUrl": "https://twitter.com/en/privacy", "wildcard": "0" @@ -3162,8 +3563,8 @@ "category": "Marketing", "name": "auth_token", "domain": ".twitter.com", - "description": "These cookies enable us to track visitor activity from our X ads on our website, and also to allow users to share content from our websites. They cookies do not provide us with any confidential information relating to your account.", - "retention": "2 years", + "description": "OCD_auth_token_description", + "retention": "OCD_retention_2_years", "dataController": "X", "gdprUrl": "https://twitter.com/en/privacy", "wildcard": "0" @@ -3175,8 +3576,8 @@ "category": "Marketing", "name": "csrf_same_site", "domain": ".twitter.com", - "description": "These cookies enable us to track visitor activity from our X ads on our website, and also to allow users to share content from our websites. They cookies do not provide us with any confidential information relating to your account.", - "retention": "2 years", + "description": "OCD_csrf_same_site_description", + "retention": "OCD_retention_2_years", "dataController": "X", "gdprUrl": "https://twitter.com/en/privacy", "wildcard": "0" @@ -3188,8 +3589,8 @@ "category": "Marketing", "name": "csrf_same_site_set", "domain": ".twitter.com", - "description": "These cookies enable us to track visitor activity from our X ads on our website, and also to allow users to share content from our websites. They cookies do not provide us with any confidential information relating to your account.", - "retention": "2 years", + "description": "OCD_csrf_same_site_set_description", + "retention": "OCD_retention_2_years", "dataController": "X", "gdprUrl": "https://twitter.com/en/privacy", "wildcard": "0" @@ -3201,8 +3602,8 @@ "category": "Marketing", "name": "dnt", "domain": ".twitter.com", - "description": "These are third party X cookies. These cookies enable users, if they wish, to login to their X account share content from our websites with their friends. These cookies do not allow us access to your accounts or provide us with any confidential information relating to your accounts. These cookies also allow a news feed of tweets to appear on the website.", - "retention": "2 years", + "description": "OCD_dnt_description", + "retention": "OCD_retention_2_years", "dataController": "X", "gdprUrl": "https://twitter.com/en/privacy", "wildcard": "0" @@ -3214,8 +3615,8 @@ "category": "Marketing", "name": "eu_cn", "domain": ".twitter.com", - "description": "These are third party X cookies. These cookies enable users, if they wish, to login to their X account share content from our websites with their friends. These cookies do not allow us access to your accounts or provide us with any confidential information relating to your accounts. These cookies also allow a news feed of tweets to appear on the website.", - "retention": "2 years", + "description": "OCD_eu_cn_description", + "retention": "OCD_retention_2_years", "dataController": "X", "gdprUrl": "https://twitter.com/en/privacy", "wildcard": "0" @@ -3227,8 +3628,8 @@ "category": "Marketing", "name": "external_referer", "domain": ".twitter.com", - "description": "Our Website uses X buttons to allow our visitors to follow our promotional X feeds, and sometimes embed feeds on our Website.", - "retention": "2 years", + "description": "OCD_external_referer_description", + "retention": "OCD_retention_2_years", "dataController": "X", "gdprUrl": "https://twitter.com/en/privacy", "wildcard": "0" @@ -3240,8 +3641,8 @@ "category": "Marketing", "name": "kdt", "domain": ".twitter.com", - "description": "These are third party X cookies. These cookies enable users, if they wish, to login to their X account share content from our websites with their friends. These cookies do not allow us access to your accounts or provide us with any confidential information relating to your accounts. These cookies also allow a news feed of tweets to appear on the website.", - "retention": "2 years", + "description": "OCD_kdt_description", + "retention": "OCD_retention_2_years", "dataController": "X", "gdprUrl": "https://twitter.com/en/privacy", "wildcard": "0" @@ -3253,8 +3654,8 @@ "category": "Marketing", "name": "remember_checked_on", "domain": ".twitter.com", - "description": "These cookies enable us to track visitor activity from our X ads on our website, and also to allow users to share content from our websites. These cookies do not provide us with any confidential information relating to your account.", - "retention": "2 years", + "description": "OCD_remember_checked_on_description", + "retention": "OCD_retention_2_years", "dataController": "X", "gdprUrl": "https://twitter.com/en/privacy", "wildcard": "0" @@ -3266,8 +3667,8 @@ "category": "Marketing", "name": "rweb_optin", "domain": ".twitter.com", - "description": "These cookies enable us to track visitor activity from our X ads on our website, and also to allow users to share content from our websites. These cookies do not provide us with any confidential information relating to your account.", - "retention": "2 years", + "description": "OCD_rweb_optin_description", + "retention": "OCD_retention_2_years", "dataController": "X", "gdprUrl": "https://twitter.com/en/privacy", "wildcard": "0" @@ -3279,8 +3680,8 @@ "category": "Marketing", "name": "syndication_guest_id", "domain": ".twitter.com", - "description": "Used to collect information about users browsing behaviour for marketing purposes including digital display and social media advertising.", - "retention": "2 years", + "description": "OCD_syndication_guest_id_description", + "retention": "OCD_retention_2_years", "dataController": "X", "gdprUrl": "https://twitter.com/en/privacy", "wildcard": "0" @@ -3292,8 +3693,8 @@ "category": "Marketing", "name": "twid", "domain": ".twitter.com", - "description": "These cookies enable us to track visitor activity from our X ads on our website, and also to allow users to share content from our websites. They cookies do not provide us with any confidential information relating to your account.", - "retention": "2 years", + "description": "OCD_twid_description", + "retention": "OCD_retention_2_years", "dataController": "X", "gdprUrl": "https://twitter.com/en/privacy", "wildcard": "0" @@ -3305,8 +3706,8 @@ "category": "Marketing", "name": "tfw_exp", "domain": ".twitter.com", - "description": "These cookies enable us to track visitor activity from our X ads on our website, and also to allow users to share content from our websites. They cookies do not provide us with any confidential information relating to your account.", - "retention": "2 years", + "description": "OCD_tfw_exp_description", + "retention": "OCD_retention_2_years", "dataController": "X", "gdprUrl": "https://twitter.com/en/privacy", "wildcard": "0" @@ -3318,8 +3719,8 @@ "category": "Analytics", "name": "__insp_pad", "domain": "Inspectlet.com (3rd party)", - "description": "This cookie contains the page number of the session recording.", - "retention": "End of session (browser)", + "description": "OCD___insp_pad_description", + "retention": "OCD_retention_End_of_session__browser_", "dataController": "Inspectlet", "gdprUrl": "https://docs.inspectlet.com/hc/en-us", "wildcard": "0" @@ -3331,8 +3732,8 @@ "category": "Analytics", "name": "__insp_sid", "domain": "Inspectlet.com (3rd party)", - "description": "This cookie contains the ID of the Inspectlet session that is being recorded.", - "retention": "End of session (browser)", + "description": "OCD___insp_sid_description", + "retention": "OCD_retention_End_of_session__browser_", "dataController": "Inspectlet", "gdprUrl": "https://docs.inspectlet.com/hc/en-us", "wildcard": "0" @@ -3344,8 +3745,8 @@ "category": "Analytics", "name": "__insp_ref", "domain": "Inspectlet.com (3rd party)", - "description": "The cookie contains the referrer source/URL", - "retention": "End of session (browser)", + "description": "OCD___insp_ref_description", + "retention": "OCD_retention_End_of_session__browser_", "dataController": "Inspectlet", "gdprUrl": "https://docs.inspectlet.com/hc/en-us", "wildcard": "0" @@ -3357,8 +3758,8 @@ "category": "Analytics", "name": "__insp_scpt", "domain": "Inspectlet.com (3rd party)", - "description": "This cookie contains an integer that allows us to know if the screen capture was triggered or not.", - "retention": "End of session (browser)", + "description": "OCD___insp_scpt_description", + "retention": "OCD_retention_End_of_session__browser_", "dataController": "Inspectlet", "gdprUrl": "https://docs.inspectlet.com/hc/en-us", "wildcard": "0" @@ -3370,8 +3771,8 @@ "category": "Analytics", "name": "__insp_nv", "domain": "Inspectlet.com (3rd party)", - "description": "This cookie contains a value that allows Inspectlet to know if this user is a new visitor or a returning visitor.", - "retention": "End of session (browser)", + "description": "OCD___insp_nv_description", + "retention": "OCD_retention_End_of_session__browser_", "dataController": "Inspectlet", "gdprUrl": "https://docs.inspectlet.com/hc/en-us", "wildcard": "0" @@ -3383,8 +3784,8 @@ "category": "Analytics", "name": "__insp_wid", "domain": "Inspectlet.com (3rd party)", - "description": "This cookie contains an uniqe user ID provided by the website if set up.", - "retention": "End of session (browser)", + "description": "OCD___insp_wid_description", + "retention": "OCD_retention_End_of_session__browser_", "dataController": "Inspectlet", "gdprUrl": "https://docs.inspectlet.com/hc/en-us", "wildcard": "0" @@ -3396,8 +3797,8 @@ "category": "Analytics", "name": "__insp_uid", "domain": "Inspectlet.com (3rd party)", - "description": "This cookie contains random ID assigned to a visitor.", - "retention": "1 year", + "description": "OCD___insp_uid_description", + "retention": "OCD_retention_1_year", "dataController": "Inspectlet", "gdprUrl": "https://docs.inspectlet.com/hc/en-us", "wildcard": "0" @@ -3409,8 +3810,8 @@ "category": "Analytics", "name": "__insp_dct", "domain": "Inspectlet.com (3rd party)", - "description": "Registers statistical data on visitors' behaviour on the website. Used for internal analytics by the website operator.", - "retention": "Session", + "description": "OCD___insp_dct_description", + "retention": "OCD_retention_Session", "dataController": "Inspectlet", "gdprUrl": "https://docs.inspectlet.com/hc/en-us", "wildcard": "0" @@ -3421,11 +3822,11 @@ "platform": "Indeed", "category": "Analytics", "name": "CTK", - "domain": "conv.indeed.com (3rd party)", - "description": "Contains information related to registering (counting) a job application via a job listing on indeed.com.", - "retention": "17 years", + "domain": "indeed.com", + "description": "OCD_CTK_description", + "retention": "OCD_retention_1825_days", "dataController": "Indeed", - "gdprUrl": "https://www.indeed.nl/legal", + "gdprUrl": "https://www.indeed.com/legal", "wildcard": "0" } ], @@ -3434,24 +3835,24 @@ "platform": "Indeed", "category": "Analytics", "name": "ctkgen", - "domain": "conv.indeed.com (3rd party)", - "description": "Contains information related to registering (counting) a job application via a job listing on indeed.com.", - "retention": "1 day", + "domain": "", + "description": "OCD_ctkgen_description", + "retention": "OCD_retention_1_day", "dataController": "Indeed", - "gdprUrl": "https://www.indeed.nl/legal", + "gdprUrl": "https://www.indeed.com/legal", "wildcard": "0" } ], "INDEED_CSRF_TOKEN": [ { "platform": "Indeed", - "category": "Analytics", + "category": "Functional", "name": "INDEED_CSRF_TOKEN", - "domain": "conv.indeed.com (3rd party)", - "description": "Contains information related to registering (counting) a job application via a job listing on indeed.com.", - "retention": "End of session (browser)", + "domain": "", + "description": "OCD_INDEED_CSRF_TOKEN_description", + "retention": "OCD_retention_Session", "dataController": "Indeed", - "gdprUrl": "https://www.indeed.nl/legal", + "gdprUrl": "https://www.indeed.com/legal", "wildcard": "0" } ], @@ -3460,11 +3861,11 @@ "platform": "Indeed", "category": "Analytics", "name": "jasx_pool_id", - "domain": "conv.indeed.com (3rd party)", - "description": "Contains information related to registering (counting) a job application via a job listing on indeed.com.", - "retention": "End of session (browser)", + "domain": "", + "description": "OCD_jasx_pool_id_description", + "retention": "OCD_retention_Session", "dataController": "Indeed", - "gdprUrl": "https://www.indeed.nl/legal", + "gdprUrl": "https://www.indeed.com/legal", "wildcard": "0" } ], @@ -3473,11 +3874,11 @@ "platform": "Indeed", "category": "Analytics", "name": "pagead/conv/%INTEGER%", - "domain": "conv.indeed.com (3rd party)", - "description": "Contains information related to registering (counting) a job application via a job listing on indeed.com.", - "retention": "End of session (browser)", + "domain": "", + "description": "OCD_pagead_conv__INTEGER__description", + "retention": "OCD_retention_Session", "dataController": "Indeed", - "gdprUrl": "https://www.indeed.nl/legal", + "gdprUrl": "https://www.indeed.com/legal", "wildcard": "0" } ], @@ -3487,8 +3888,8 @@ "category": "Analytics", "name": "tv_spot_tracker", "domain": "", - "description": "Contains information about the timeslot of a running TV ad", - "retention": "End of session (browser)", + "description": "OCD_tv_spot_tracker_description", + "retention": "OCD_retention_End_of_session__browser_", "dataController": "Abovo Media", "gdprUrl": "https://www.abovomedia.nl/cookies/", "wildcard": "0" @@ -3500,8 +3901,8 @@ "category": "Functional", "name": "cookie-consent-io", "domain": "", - "description": "Registers cookie preferences of a user", - "retention": "1 year", + "description": "OCD_cookie_consent_io_description", + "retention": "OCD_retention_1_year", "dataController": "CookieConsent.io", "gdprUrl": "https://www.cookieconsent.io/cookies/", "wildcard": "0" @@ -3513,8 +3914,8 @@ "category": "Functional", "name": "cookie-consent-io-timestamp*", "domain": "", - "description": "Registers user activity timestamp", - "retention": "30 days", + "description": "OCD_cookie_consent_io_timestamp__description", + "retention": "OCD_retention_30_days", "dataController": "CookieConsent.io", "gdprUrl": "https://www.cookieconsent.io/cookies/", "wildcard": "1" @@ -3526,8 +3927,8 @@ "category": "Functional", "name": "cookie-consent-io-gdpr", "domain": "", - "description": "Register anonymous consent identifier for GDPR consent compliance", - "retention": "1 year", + "description": "OCD_cookie_consent_io_gdpr_description", + "retention": "OCD_retention_1_year", "dataController": "CookieConsent.io", "gdprUrl": "https://www.cookieconsent.io/cookies/", "wildcard": "0" @@ -3539,8 +3940,8 @@ "category": "Marketing", "name": "ccec_user", "domain": "", - "description": "Contains information about the customer to allow retargeting.", - "retention": "1 year", + "description": "OCD_ccec_user_description", + "retention": "OCD_retention_1_year", "dataController": "CookieConsent.io", "gdprUrl": "https://www.cookieconsent.io/cookies/", "wildcard": "0" @@ -3552,10 +3953,10 @@ "category": "Marketing", "name": "GPS", "domain": "youtube.com (3rd party)", - "description": "Registers a unique ID on mobile devices to enable tracking based on geographical GPS location.", - "retention": "1 day", + "description": "OCD_GPS_description", + "retention": "OCD_retention_1_day", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -3565,10 +3966,10 @@ "category": "Marketing", "name": "VISITOR_INFO1_LIVE", "domain": "youtube.com (3rd party)", - "description": "Tries to estimate the users' bandwidth on pages with integrated YouTube videos. Also used for marketing", - "retention": "179 days", + "description": "OCD_VISITOR_INFO1_LIVE_description", + "retention": "OCD_retention_179_days", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -3578,10 +3979,10 @@ "category": "Functional", "name": "PREF", "domain": "youtube.com (3rd party)", - "description": "This cookie stores your preferences and other information, in particular preferred language, how many search results you wish to be shown on your page, and whether or not you wish to have Google’s SafeSearch filter turned on.", - "retention": "10 years from set/ update", + "description": "OCD_PREF_description", + "retention": "OCD_retention_10_years_from_set__update", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -3591,10 +3992,10 @@ "category": "Functional", "name": "YSC", "domain": "youtube.com (3rd party)", - "description": "Registers a unique ID to keep statistics of what videos from YouTube the user has seen.", - "retention": "Session", + "description": "OCD_YSC_description", + "retention": "OCD_retention_Session", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -3604,10 +4005,10 @@ "category": "Functional", "name": "DEVICE_INFO", "domain": "youtube.com (3rd party)", - "description": "Used to detect if the visitor has accepted the marketing category in the cookie banner. This cookie is necessary for GDPR-compliance of the website.", - "retention": "179 days", + "description": "OCD_DEVICE_INFO_description", + "retention": "OCD_retention_179_days", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -3617,10 +4018,10 @@ "category": "Functional", "name": "LOGIN_INFO", "domain": "youtube.com (3rd party)", - "description": "This cookie is used to play YouTube videos embedded on the website.", - "retention": "2 years", + "description": "OCD_LOGIN_INFO_description", + "retention": "OCD_retention_2_years", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -3630,10 +4031,10 @@ "category": "Marketing", "name": "VISITOR_PRIVACY_METADATA", "domain": "youtube.com (3rd party)", - "description": "Youtube visitor privacy metadata cookie", - "retention": "180 days", + "description": "OCD_VISITOR_PRIVACY_METADATA_description", + "retention": "OCD_retention_180_days", "dataController": "Google", - "gdprUrl": "https://privacy.google.com/take-control.html", + "gdprUrl": "https://policies.google.com/privacy", "wildcard": "0" } ], @@ -3643,8 +4044,8 @@ "category": "Marketing", "name": "__adm_ui", "domain": "admatic.com.tr (3rd party)", - "description": "Used to track visitors on multiple websites, in order to present relevant advertisement based on the visitor's preferences.", - "retention": "1 year", + "description": "OCD___adm_ui_description", + "retention": "OCD_retention_1_year", "dataController": "Admatic", "gdprUrl": "http://www.admatic.com.tr/en/privacy-policy.html", "wildcard": "0" @@ -3656,8 +4057,8 @@ "category": "Marketing", "name": "__adm_uiex", "domain": "admatic.com.tr (3rd party)", - "description": "Used to track visitors on multiple websites, in order to present relevant advertisement based on the visitor's preferences.", - "retention": "1 year", + "description": "OCD___adm_uiex_description", + "retention": "OCD_retention_1_year", "dataController": "Admatic", "gdprUrl": "http://www.admatic.com.tr/en/privacy-policy.html", "wildcard": "0" @@ -3669,8 +4070,8 @@ "category": "Marketing", "name": "__adm_usyncc", "domain": "admatic.com.tr (3rd party)", - "description": "Used to identify the visitor across visits and devices. This allows the website to present the visitor with relevant advertisement - The service is provided by third party advertisement hubs, which facilitate real-time bidding for advertisers.", - "retention": "5 days", + "description": "OCD___adm_usyncc_description", + "retention": "OCD_retention_5_days", "dataController": "Admatic", "gdprUrl": "http://www.admatic.com.tr/en/privacy-policy.html", "wildcard": "0" @@ -3682,8 +4083,8 @@ "category": "Marketing", "name": "uids", "domain": "admatic.com.tr (3rd party)", - "description": "Registers user behaviour and navigation on the website, and any interaction with active campaigns. This is used for optimizing advertisement and for efficient retargeting.", - "retention": "3 months", + "description": "OCD_uids_description", + "retention": "OCD_retention_3_months", "dataController": "Admatic", "gdprUrl": "http://www.admatic.com.tr/en/privacy-policy.html", "wildcard": "0" @@ -3706,8 +4107,8 @@ "category": "Functional", "name": "__cfduid", "domain": "", - "description": "The '__cfduid' cookie is set by the CloudFlare service to identify trusted web traffic. It does not correspond to any user id in the web application, nor does the cookie store any personally identifiable", - "retention": "5 years", + "description": "OCD___cfduid_description", + "retention": "OCD_retention_5_years", "dataController": "Cloudflare", "gdprUrl": "https://www.cloudflare.com/privacypolicy/", "wildcard": "0" @@ -3719,8 +4120,8 @@ "category": "Functional", "name": "__cfruid", "domain": "", - "description": "Used by the content network, Cloudflare, to identify trusted web traffic.", - "retention": "session", + "description": "OCD___cfruid_description", + "retention": "OCD_retention_session", "dataController": "Cloudflare", "gdprUrl": "https://www.cloudflare.com/privacypolicy/", "wildcard": "0" @@ -3732,8 +4133,8 @@ "category": "Functional", "name": "__cf_bm", "domain": "", - "description": "Cloudflare's bot products identify and mitigate automated traffic to protect your site from bad bots. Cloudflare places the __cf_bm cookie on End User devices that access Customer sites that are protected by Bot Management or Bot Fight Mode. The __cf_bm cookie is necessary for the proper functioning of these bot solutions.", - "retention": "session", + "description": "OCD___cf_bm_description", + "retention": "OCD_retention_session", "dataController": "Cloudflare", "gdprUrl": "https://www.cloudflare.com/privacypolicy/", "wildcard": "0" @@ -3745,8 +4146,8 @@ "category": "Functional", "name": "cf_chl_2", "domain": "", - "description": "Used by Cloudflare for the execution of Javascript or Captcha challenges. These cookies are not used for tracking or beyond the scope of the challenge.", - "retention": "session", + "description": "OCD_cf_chl_2_description", + "retention": "OCD_retention_session", "dataController": "Cloudflare", "gdprUrl": "https://www.cloudflare.com/privacypolicy/", "wildcard": "0" @@ -3758,8 +4159,8 @@ "category": "Functional", "name": "__cflb", "domain": "", - "description": "When enabling session affinity with Cloudflare Load Balancer, Cloudflare sets a __cflb cookie with a unique value on the first response to the requesting client. Cloudflare routes future requests to the same origin, optimizing network resource usage. In the event of a failover, Cloudflare sets a new __cflb cookie to direct future requests to the failover pool.", - "retention": "session", + "description": "OCD___cflb_description", + "retention": "OCD_retention_session", "dataController": "Cloudflare", "gdprUrl": "https://www.cloudflare.com/privacypolicy/", "wildcard": "0" @@ -3771,8 +4172,8 @@ "category": "Functional", "name": "_cfuvid", "domain": "", - "description": "The _cfuvid cookie is only set when a site uses this option in a Rate Limiting Rule, and is only used to allow the Cloudflare WAF to distinguish individual users who share the same IP address.", - "retention": "session", + "description": "OCD__cfuvid_description", + "retention": "OCD_retention_session", "dataController": "Cloudflare", "gdprUrl": "https://www.cloudflare.com/privacypolicy/", "wildcard": "0" @@ -3784,8 +4185,8 @@ "category": "Functional", "name": "cf_clearance", "domain": "", - "description": "Whether a CAPTCHA or Javascript challenge has been solved.", - "retention": "session", + "description": "OCD_cf_clearance_description", + "retention": "OCD_retention_session", "dataController": "Cloudflare", "gdprUrl": "https://www.cloudflare.com/privacypolicy/", "wildcard": "0" @@ -3797,8 +4198,8 @@ "category": "Marketing", "name": "__uin_bw", "domain": ".go.sonobi.com (3rd party)", - "description": "Collects information on visitor behaviour on multiple websites. This information is used on the website, in order to optimize the relevance of advertisement.", - "retention": "1 month", + "description": "OCD___uin_bw_description", + "retention": "OCD_retention_1_month", "dataController": "Sonobi", "gdprUrl": "https://sonobi.com/privacy-policy/", "wildcard": "0" @@ -3810,8 +4211,8 @@ "category": "Marketing", "name": "__uir_bw", "domain": ".go.sonobi.com (3rd party)", - "description": "Collects data on visitors' behaviour and interaction - This is used to optimize the website and make advertisement on the website more relevant.", - "retention": "1 day", + "description": "OCD___uir_bw_description", + "retention": "OCD_retention_1_day", "dataController": "Sonobi", "gdprUrl": "https://sonobi.com/privacy-policy/", "wildcard": "0" @@ -3823,8 +4224,19 @@ "category": "Marketing", "name": "__uis", "domain": ".go.sonobi.com (3rd party)", - "description": "Used to track visitors on multiple websites, in order to present relevant advertisement based on the visitor's preferences.", - "retention": "29 days", + "description": "OCD___uis_description", + "retention": "OCD_retention_29_days", + "dataController": "Sonobi", + "gdprUrl": "https://sonobi.com/privacy-policy/", + "wildcard": "0" + }, + { + "platform": "Sonobi", + "category": "Marketing", + "name": "__uis", + "domain": "go.sonobi.com", + "description": "This domain is owned by Sonobi, an automated online advertising buying and selling platform.", + "retention": "29 Days", "dataController": "Sonobi", "gdprUrl": "https://sonobi.com/privacy-policy/", "wildcard": "0" @@ -3836,8 +4248,8 @@ "category": "Marketing", "name": "HAPLB5S", "domain": ".go.sonobi.com (3rd party)", - "description": "Used to track visitors on multiple websites, in order to present relevant advertisement based on the visitor's preferences.", - "retention": "29 days", + "description": "OCD_HAPLB5S_description", + "retention": "OCD_retention_29_days", "dataController": "Sonobi", "gdprUrl": "https://sonobi.com/privacy-policy/", "wildcard": "0" @@ -3849,8 +4261,8 @@ "category": "Marketing", "name": "_dbefe", "domain": "contextweb.com (3rd party)", - "description": "Collects information on user preferences and/or interaction with web-campaign content - This is used on CRM-campaign-platform used by website owners for promoting events or products.", - "retention": "Session", + "description": "OCD__dbefe_description", + "retention": "OCD_retention_Session", "dataController": "Pulsepoint", "gdprUrl": "http://pulsepoints-new-website.webflow.io/privacy-policy/platform#consumer-choice", "wildcard": "0" @@ -3862,8 +4274,8 @@ "category": "Analytics", "name": "_hjid", "domain": "", - "description": "Hotjar cookie. This cookie is set when the customer first lands on a page with the Hotjar script. It is used to persist the random user ID, unique to that site on the browser. This ensures that behavior in subsequent visits to the same site will be attributed to the same user ID.", - "retention": "365 days", + "description": "OCD__hjid_description", + "retention": "OCD_retention_365_days", "dataController": "Hotjar", "gdprUrl": "https://www.hotjar.com/legal/policies/privacy/", "wildcard": "0" @@ -3875,8 +4287,8 @@ "category": "Analytics", "name": "_hjIncludedInSample", "domain": "", - "description": "Hotjar cookie. This session cookie is set to let Hotjar know whether that visitor is included in the sample which is used to generate funnels.", - "retention": "365 days", + "description": "OCD__hjIncludedInSample_description", + "retention": "OCD_retention_365_days", "dataController": "Hotjar", "gdprUrl": "https://www.hotjar.com/legal/policies/privacy/", "wildcard": "0" @@ -3888,8 +4300,8 @@ "category": "Analytics", "name": "_hjClosedSurveyInvites", "domain": "", - "description": "Hotjar cookie. This cookie is set once a visitor interacts with a Survey invitation modal popup. It is used to ensure that the same invite does not re-appear if it has already been shown.", - "retention": "365 days", + "description": "OCD__hjClosedSurveyInvites_description", + "retention": "OCD_retention_365_days", "dataController": "Hotjar", "gdprUrl": "https://www.hotjar.com/legal/policies/privacy/", "wildcard": "0" @@ -3901,8 +4313,8 @@ "category": "Analytics", "name": "_hjDonePolls", "domain": "", - "description": "Hotjar cookie. This cookie is set once a visitor completes a poll using the Feedback Poll widget. It is used to ensure that the same poll does not re-appear if it has already been filled in.", - "retention": "365 days", + "description": "OCD__hjDonePolls_description", + "retention": "OCD_retention_365_days", "dataController": "Hotjar", "gdprUrl": "https://www.hotjar.com/legal/policies/privacy/", "wildcard": "0" @@ -3914,8 +4326,8 @@ "category": "Analytics", "name": "_hjMinimizedPolls", "domain": "", - "description": "Hotjar cookie. This cookie is set once a visitor minimizes a Feedback Poll widget. It is used to ensure that the widget stays minimizes when the visitor navigates through your site.", - "retention": "365 days", + "description": "OCD__hjMinimizedPolls_description", + "retention": "OCD_retention_365_days", "dataController": "Hotjar", "gdprUrl": "https://www.hotjar.com/legal/policies/privacy/", "wildcard": "0" @@ -3927,8 +4339,8 @@ "category": "Analytics", "name": "_hjDoneTestersWidgets", "domain": "", - "description": "Hotjar cookie. This cookie is set once a visitor submits their information in the Recruit User Testers widget. It is used to ensure that the same form does not re-appear if it has already been filled in.", - "retention": "365 days", + "description": "OCD__hjDoneTestersWidgets_description", + "retention": "OCD_retention_365_days", "dataController": "Hotjar", "gdprUrl": "https://www.hotjar.com/legal/policies/privacy/", "wildcard": "0" @@ -3940,8 +4352,8 @@ "category": "Analytics", "name": "_hjMinimizedTestersWidgets", "domain": "", - "description": "Hotjar cookie. This cookie is set once a visitor minimizes a Recruit User Testers widget. It is used to ensure that the widget stays minimizes when the visitor navigates through your site.", - "retention": "365 days", + "description": "OCD__hjMinimizedTestersWidgets_description", + "retention": "OCD_retention_365_days", "dataController": "Hotjar", "gdprUrl": "https://www.hotjar.com/legal/policies/privacy/", "wildcard": "0" @@ -3953,8 +4365,8 @@ "category": "Analytics", "name": "_hjShownFeedbackMessage", "domain": "", - "description": "This cookie is set when a visitor minimizes or completes Incoming Feedback. This is done so that the Incoming Feedback will load as minimized immediately if they navigate to another page where it is set to show.", - "retention": "365 days", + "description": "OCD__hjShownFeedbackMessage_description", + "retention": "OCD_retention_365_days", "dataController": "Hotjar", "gdprUrl": "https://www.hotjar.com/legal/policies/privacy/", "wildcard": "0" @@ -3966,8 +4378,8 @@ "category": "Functional", "name": "_hjTLDTest", "domain": "", - "description": "When the Hotjar script executes we try to determine the most generic cookie path we should use, instead of the page hostname. This is done so that cookies can be shared across subdomains (where applicable). To determine this, we try to store the _hjTLDTest cookie for different URL substring alternatives until it fails. After this check, the cookie is removed.", - "retention": "session", + "description": "OCD__hjTLDTest_description", + "retention": "OCD_retention_session", "dataController": "Hotjar", "gdprUrl": "https://www.hotjar.com/legal/policies/privacy/", "wildcard": "0" @@ -3979,8 +4391,8 @@ "category": "Functional", "name": "_hjUserAttributesHash", "domain": "", - "description": "User Attributes sent through the Hotjar Identify API are cached for the duration of the session in order to know when an attribute has changed and needs to be updated.", - "retention": "session", + "description": "OCD__hjUserAttributesHash_description", + "retention": "OCD_retention_session", "dataController": "Hotjar", "gdprUrl": "https://www.hotjar.com/legal/policies/privacy/", "wildcard": "0" @@ -3992,8 +4404,8 @@ "category": "Analytics", "name": "_hjCachedUserAttributes", "domain": "", - "description": "This cookie stores User Attributes which are sent through the Hotjar Identify API, whenever the user is not in the sample. These attributes will only be saved if the user interacts with a Hotjar Feedback tool.", - "retention": "session", + "description": "OCD__hjCachedUserAttributes_description", + "retention": "OCD_retention_session", "dataController": "Hotjar", "gdprUrl": "https://www.hotjar.com/legal/policies/privacy/", "wildcard": "0" @@ -4005,8 +4417,8 @@ "category": "Functional", "name": "_hjLocalStorageTest", "domain": "", - "description": "This cookie is used to check if the Hotjar Tracking Script can use local storage. If it can, a value of 1 is set in this cookie. The data stored in_hjLocalStorageTest has no expiration time, but it is deleted immediately after creating it so the expected storage time is under 100ms.", - "retention": "", + "description": "OCD__hjLocalStorageTest_description", + "retention": "OCD_retention_", "dataController": "Hotjar", "gdprUrl": "https://www.hotjar.com/legal/policies/privacy/", "wildcard": "0" @@ -4018,8 +4430,8 @@ "category": "Functional", "name": "_hjptid", "domain": "", - "description": "This cookie is set for logged in users of Hotjar, who have Admin Team Member permissions. It is used during pricing experiments to show the Admin consistent pricing across the site.", - "retention": "session", + "description": "OCD__hjptid_description", + "retention": "OCD_retention_session", "dataController": "Hotjar", "gdprUrl": "https://www.hotjar.com/legal/policies/privacy/", "wildcard": "0" @@ -4031,8 +4443,8 @@ "category": "Functional", "name": "_hjAbsoluteSessionInProgress", "domain": "", - "description": "The cookie is set so Hotjar can track the beginning of the user's journey for a total session count. It does not contain any identifiable information.", - "retention": "30 minutes", + "description": "OCD__hjAbsoluteSessionInProgress_description", + "retention": "OCD_retention_30_minutes", "dataController": "Hotjar", "gdprUrl": "https://www.hotjar.com/legal/policies/privacy/", "wildcard": "0" @@ -4044,8 +4456,8 @@ "category": "Functional", "name": "_hjFirstSeen", "domain": "", - "description": "The cookie is set so Hotjar can track the beginning of the user's journey for a total session count. It does not contain any identifiable information.", - "retention": "30 minutes", + "description": "OCD__hjFirstSeen_description", + "retention": "OCD_retention_30_minutes", "dataController": "Hotjar", "gdprUrl": "https://www.hotjar.com/legal/policies/privacy/", "wildcard": "0" @@ -4057,8 +4469,8 @@ "category": "Functional", "name": "_hjIncludedInPageviewSample", "domain": "", - "description": "This cookie is set to let Hotjar know whether that visitor is included in the data sampling defined by your site's page view limit.", - "retention": "30 minutes", + "description": "OCD__hjIncludedInPageviewSample_description", + "retention": "OCD_retention_30_minutes", "dataController": "Hotjar", "gdprUrl": "https://www.hotjar.com/legal/policies/privacy/", "wildcard": "0" @@ -4070,8 +4482,8 @@ "category": "Functional", "name": "_hjIncludedInSessionSample*", "domain": "", - "description": "This cookie is set to let Hotjar know whether that visitor is included in the data sampling defined by your site's daily session limit", - "retention": "30 minutes", + "description": "OCD__hjIncludedInSessionSample__description", + "retention": "OCD_retention_30_minutes", "dataController": "Hotjar", "gdprUrl": "https://www.hotjar.com/legal/policies/privacy/", "wildcard": "1" @@ -4083,8 +4495,8 @@ "category": "Functional", "name": "_hjSession_*", "domain": "", - "description": "A cookie that holds the current session data. This ensues that subsequent requests within the session window will be attributed to the same Hotjar session.", - "retention": "30 minutes", + "description": "OCD__hjSession___description", + "retention": "OCD_retention_30_minutes", "dataController": "Hotjar", "gdprUrl": "https://www.hotjar.com/legal/policies/privacy/", "wildcard": "1" @@ -4096,8 +4508,8 @@ "category": "Functional", "name": "_hjSessionUser_*", "domain": "", - "description": "Hotjar cookie that is set when a user first lands on a page with the Hotjar script. It is used to persist the Hotjar User ID, unique to that site on the browser. This ensures that behavior in subsequent visits to the same site will be attributed to the same user ID.", - "retention": "365 days", + "description": "OCD__hjSessionUser___description", + "retention": "OCD_retention_365_days", "dataController": "Hotjar", "gdprUrl": "https://www.hotjar.com/legal/policies/privacy/", "wildcard": "1" @@ -4109,8 +4521,8 @@ "category": "Functional", "name": "_hjSessionTooLarge", "domain": "", - "description": "Causes Hotjar to stop collecting data if a session becomes too large. This is determined automatically by a signal from the WebSocket server if the session size exceeds the limit.", - "retention": "session", + "description": "OCD__hjSessionTooLarge_description", + "retention": "OCD_retention_session", "dataController": "Hotjar", "gdprUrl": "https://www.hotjar.com/legal/policies/privacy/", "wildcard": "0" @@ -4122,8 +4534,8 @@ "category": "Functional", "name": "_hjSessionRejected", "domain": "", - "description": "If present, this cookie will be set to 1 for the duration of a user’s session, if Hotjar rejected the session from connecting to our WebSocket due to server overload. This cookie is only applied in extremely rare situations to prevent severe performance issues.", - "retention": "session", + "description": "OCD__hjSessionRejected_description", + "retention": "OCD_retention_session", "dataController": "Hotjar", "gdprUrl": "https://www.hotjar.com/legal/policies/privacy/", "wildcard": "0" @@ -4135,47 +4547,86 @@ "category": "Functional", "name": "_hjSessionResumed", "domain": "", - "description": "A cookie that is set when a session/recording is reconnected to Hotjar servers after a break in connection.", - "retention": "session", + "description": "OCD__hjSessionResumed_description", + "retention": "OCD_retention_session", "dataController": "Hotjar", "gdprUrl": "https://www.hotjar.com/legal/policies/privacy/", "wildcard": "0" } ], - "ac_enable_tracking": [ + "hjViewportId": [ { - "platform": "Active Campaign", - "category": "Marketing", - "name": "ac_enable_tracking", + "platform": "Hotjar", + "category": "Functional", + "name": "hjViewportId", "domain": "", - "description": "This cookie is associated with Active Campaign and is set to confirm that tracking has been enabled for the website. Tracking is used to create reports of our web traffic and improve the user experience of the website.", - "retention": "29 days", - "dataController": "Active Campaign", - "gdprUrl": "https://www.activecampaign.com/gdpr-updates/", + "description": "OCD_hjViewportId_description", + "retention": "OCD_retention_Session", + "dataController": "Hotjar", + "gdprUrl": "https://www.hotjar.com/legal/policies/privacy/", "wildcard": "0" } ], - "prism_*": [ + "_hjSessionStorageTest": [ { - "platform": "Active Campaign", - "category": "Marketing", - "name": "prism_*", + "platform": "Hotjar", + "category": "Functional", + "name": "_hjSessionStorageTest", "domain": "", - "description": "This cookie is used by Active Campaign for site tracking purposes.", - "retention": "30 days", - "dataController": "Active Campaign", - "gdprUrl": "https://www.activecampaign.com/gdpr-updates/", - "wildcard": "1" + "description": "OCD__hjSessionStorageTest_description", + "retention": "OCD_retention_Session", + "dataController": "Hotjar", + "gdprUrl": "https://www.hotjar.com/legal/policies/privacy/", + "wildcard": "0" } ], - "ASP.NET_Sessio": [ + "_hjCookieTest": [ + { + "platform": "Hotjar", + "category": "Functional", + "name": "_hjCookieTest", + "domain": "", + "description": "OCD__hjCookieTest_description", + "retention": "OCD_retention_Session", + "dataController": "Hotjar", + "gdprUrl": "https://www.hotjar.com/legal/policies/privacy/", + "wildcard": "0" + } + ], + "ac_enable_tracking": [ + { + "platform": "Active Campaign", + "category": "Marketing", + "name": "ac_enable_tracking", + "domain": "", + "description": "OCD_ac_enable_tracking_description", + "retention": "OCD_retention_29_days", + "dataController": "Active Campaign", + "gdprUrl": "https://www.activecampaign.com/gdpr-updates/", + "wildcard": "0" + } + ], + "prism_*": [ + { + "platform": "Active Campaign", + "category": "Marketing", + "name": "prism_*", + "domain": "", + "description": "OCD_prism___description", + "retention": "OCD_retention_30_days", + "dataController": "Active Campaign", + "gdprUrl": "https://www.activecampaign.com/gdpr-updates/", + "wildcard": "1" + } + ], + "ASP.NET_Sessio": [ { "platform": "Microsoft", "category": "Functional", "name": "ASP.NET_Sessio", "domain": "", - "description": "General purpose platform session cookie, used by sites written with Microsoft .NET based technologies. Usually used to maintain an anonymised user session by the server.", - "retention": "Session", + "description": "OCD_ASP_NET_Sessio_description", + "retention": "OCD_retention_Session", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -4187,8 +4638,8 @@ "category": "Functional", "name": "ASP.NET_Sessio_Fallback", "domain": "", - "description": "Fallback session cookie to support older browsers that haven't implemented the Secure flag, in modern evergreen browsers this cookie is never set as it haven't got the Secure flag.", - "retention": "Session", + "description": "OCD_ASP_NET_Sessio_Fallback_description", + "retention": "OCD_retention_Session", "dataController": "Microsoft", "gdprUrl": "https://account.microsoft.com/privacy", "wildcard": "0" @@ -4200,8 +4651,8 @@ "category": "Functional", "name": "JSESSIO", "domain": "", - "description": "JSESSIO is a platform session cookie and is used by sites with JavaServer Pages (JSP). The cookie is used to maintain an anonymous user session by the server.", - "retention": "Session", + "description": "OCD_JSESSIO_description", + "retention": "OCD_retention_Session", "dataController": "Oracle", "gdprUrl": "https://www.oracle.com/privacy", "wildcard": "0" @@ -4213,8 +4664,8 @@ "category": "Functional", "name": "ORA_WWV_APP_*", "domain": "", - "description": "Security cookie for applications.", - "retention": "Session", + "description": "OCD_ORA_WWV_APP___description", + "retention": "OCD_retention_Session", "dataController": "Oracle", "gdprUrl": "https://www.oracle.com/privacy", "wildcard": "1" @@ -4226,8 +4677,8 @@ "category": "Analytics", "name": "ELOQUA", "domain": ".eloqua.com", - "description": "This cookies allow better understand how visitors use the website. This cookie data may be used to personalise the content or design of the website", - "retention": "13 months", + "description": "OCD_ELOQUA_description", + "retention": "OCD_retention_13_months", "dataController": "Oracle", "gdprUrl": "https://www.oracle.com/privacy", "wildcard": "0" @@ -4239,8 +4690,8 @@ "category": "Analytics", "name": "ELQSTATUS", "domain": ".eloqua.com", - "description": "This cookie is used to track individual visitors and their use of the site. It is set when you first visit the site and updated on subsequent visits.", - "retention": "13 months", + "description": "OCD_ELQSTATUS_description", + "retention": "OCD_retention_13_months", "dataController": "Oracle", "gdprUrl": "https://www.oracle.com/privacy", "wildcard": "0" @@ -4252,8 +4703,8 @@ "category": "Functional", "name": "laravel_session", "domain": "", - "description": "Internally laravel uses laravel_session to identify a session instance for a user", - "retention": "Session", + "description": "OCD_laravel_session_description", + "retention": "OCD_retention_Session", "dataController": "Laravel", "gdprUrl": "https://www.laravel.com", "wildcard": "0" @@ -4265,8 +4716,8 @@ "category": "Functional", "name": "PHPSESSID", "domain": "", - "description": "Cookie generated by applications based on the PHP language. This is a general purpose identifier used to maintain user session variables. It is normally a random generated number, how it is used can be specific to the site, but a good example is maintaining a logged-in status for a user between pages.", - "retention": "Sessions", + "description": "OCD_PHPSESSID_description", + "retention": "OCD_retention_Sessions", "dataController": "PHP.net", "gdprUrl": "https://www.php.net/privacy.php", "wildcard": "0" @@ -4278,8 +4729,8 @@ "category": "Functional", "name": "__Secure-PHPSESSID", "domain": "", - "description": "Cookie generated by applications based on the PHP language. This is a general purpose identifier used to maintain user session variables. It is normally a random generated number, how it is used can be specific to the site, but a good example is maintaining a logged-in status for a user between pages.", - "retention": "Sessions", + "description": "OCD___Secure_PHPSESSID_description", + "retention": "OCD_retention_Sessions", "dataController": "PHP.net", "gdprUrl": "https://www.php.net/privacy.php", "wildcard": "0" @@ -4291,8 +4742,8 @@ "category": "Functional", "name": "XSRF-TOKEN", "domain": "", - "description": "This cookie is written to help with site security in preventing Cross-Site Request Forgery attacks.", - "retention": "Session", + "description": "OCD_XSRF_TOKEN_description", + "retention": "OCD_retention_Session", "dataController": "", "gdprUrl": "", "wildcard": "0" @@ -4304,8 +4755,8 @@ "category": "Marketing", "name": "lidc", "domain": "linkedin.com (3rd party)", - "description": "Used by the social networking service, LinkedIn, for tracking the use of embedded services.", - "retention": "1 day", + "description": "OCD_lidc_description", + "retention": "OCD_retention_1_day", "dataController": "LinkedIn", "gdprUrl": "https://www.linkedin.com/legal/privacy-policy", "wildcard": "0" @@ -4317,8 +4768,8 @@ "category": "Marketing", "name": "bcookie", "domain": "linkedin.com (3rd party)", - "description": "Used by LinkedIn to track the use of embedded services.", - "retention": "1 year", + "description": "OCD_bcookie_description", + "retention": "OCD_retention_1_year", "dataController": "LinkedIn", "gdprUrl": "https://www.linkedin.com/legal/privacy-policy", "wildcard": "0" @@ -4330,8 +4781,8 @@ "category": "Marketing", "name": "bscookie", "domain": "linkedin.com (3rd party)", - "description": "Used by LinkedIn to track the use of embedded services.", - "retention": "1 year", + "description": "OCD_bscookie_description", + "retention": "OCD_retention_1_year", "dataController": "LinkedIn", "gdprUrl": "https://www.linkedin.com/legal/privacy-policy", "wildcard": "0" @@ -4343,8 +4794,8 @@ "category": "Marketing", "name": "trkCode", "domain": "linkedin.com (3rd party)", - "description": "This cookie is used by LinkedIn to support the functionality of adding a panel invite labeled 'Follow Us'", - "retention": "1 year", + "description": "OCD_trkCode_description", + "retention": "OCD_retention_1_year", "dataController": "LinkedIn", "gdprUrl": "https://www.linkedin.com/legal/privacy-policy", "wildcard": "0" @@ -4356,8 +4807,8 @@ "category": "Marketing", "name": "trkInfo", "domain": "linkedin.com (3rd party)", - "description": "This cookie is used by LinkedIn to support the functionality of adding a panel invite labeled 'Follow Us'", - "retention": "1 year", + "description": "OCD_trkInfo_description", + "retention": "OCD_retention_1_year", "dataController": "LinkedIn", "gdprUrl": "https://www.linkedin.com/legal/privacy-policy", "wildcard": "0" @@ -4369,8 +4820,8 @@ "category": "Marketing", "name": "li_oatml", "domain": "linkedin.com (3rd party)", - "description": "Collects information about how visitors use our site.", - "retention": "30 days", + "description": "OCD_li_oatml_description", + "retention": "OCD_retention_30_days", "dataController": "LinkedIn", "gdprUrl": "https://www.linkedin.com/legal/privacy-policy", "wildcard": "0" @@ -4382,8 +4833,8 @@ "category": "Marketing", "name": "liap", "domain": "linkedin.com (3rd party)", - "description": "Cookie used for Sign-in with Linkedin and/or to allow for the Linkedin follow feature.", - "retention": "90 days", + "description": "OCD_liap_description", + "retention": "OCD_retention_90_days", "dataController": "LinkedIn", "gdprUrl": "https://www.linkedin.com/legal/privacy-policy", "wildcard": "0" @@ -4395,8 +4846,8 @@ "category": "Marketing", "name": "lissc", "domain": "linkedin.com (3rd party)", - "description": "Pending", - "retention": "1 year", + "description": "OCD_lissc_description", + "retention": "OCD_retention_1_year", "dataController": "LinkedIn", "gdprUrl": "https://www.linkedin.com/legal/privacy-policy", "wildcard": "0" @@ -4408,8 +4859,8 @@ "category": "Marketing", "name": "spectroscopyId", "domain": "linkedin.com (3rd party)", - "description": "These cookies are set by LinkedIn for advertising purposes, including: tracking visitors so that more relevant ads can be presented, allowing users to use the 'Apply with LinkedIn' or the 'Sign-in with LinkedIn' functions, collecting information about how visitors use the site, etc.", - "retention": "session", + "description": "OCD_spectroscopyId_description", + "retention": "OCD_retention_session", "dataController": "LinkedIn", "gdprUrl": "https://www.linkedin.com/legal/privacy-policy", "wildcard": "0" @@ -4421,8 +4872,8 @@ "category": "Marketing", "name": "UserMatchHistory", "domain": "linkedin.com (3rd party)", - "description": "These cookies are set by LinkedIn for advertising purposes, including: tracking visitors so that more relevant ads can be presented, allowing users to use the 'Apply with LinkedIn' or the 'Sign-in with LinkedIn' functions, collecting information about how visitors use the site, etc.", - "retention": "session", + "description": "OCD_UserMatchHistory_description", + "retention": "OCD_retention_session", "dataController": "LinkedIn", "gdprUrl": "https://www.linkedin.com/legal/privacy-policy", "wildcard": "0" @@ -4434,8 +4885,8 @@ "category": "Functional", "name": "lang", "domain": "linkedin.com (3rd party)", - "description": "Used to remember a user's language setting", - "retention": "session", + "description": "OCD_lang_description", + "retention": "OCD_retention_session", "dataController": "LinkedIn", "gdprUrl": "https://www.linkedin.com/legal/privacy-policy", "wildcard": "0" @@ -4447,8 +4898,8 @@ "category": "Functional", "name": "li_gc", "domain": "linkedin.com (3rd party)", - "description": "Used to store guest consent to the use of cookies for non-essential purposes", - "retention": "2 years", + "description": "OCD_li_gc_description", + "retention": "OCD_retention_2_years", "dataController": "LinkedIn", "gdprUrl": "https://www.linkedin.com/legal/privacy-policy", "wildcard": "0" @@ -4460,8 +4911,8 @@ "category": "Functional", "name": "li_rm", "domain": "linkedin.com (3rd party)", - "description": "Used as part of the LinkedIn Remember Me feature and is set when a user clicks Remember Me on the device to make it easier for him or her to sign in to that device", - "retention": "1 year", + "description": "OCD_li_rm_description", + "retention": "OCD_retention_1_year", "dataController": "LinkedIn", "gdprUrl": "https://www.linkedin.com/legal/privacy-policy", "wildcard": "0" @@ -4473,8 +4924,8 @@ "category": "Analytics", "name": "AnalyticsSyncHistory", "domain": "linkedin.com (3rd party)", - "description": "Used to store information about the time a sync with the lms_analytics cookie took place for users in the Designated Countries", - "retention": "30 days", + "description": "OCD_AnalyticsSyncHistory_description", + "retention": "OCD_retention_30_days", "dataController": "LinkedIn", "gdprUrl": "https://www.linkedin.com/legal/privacy-policy", "wildcard": "0" @@ -4486,8 +4937,8 @@ "category": "Analytics", "name": "ln_or", "domain": "", - "description": "Used to determine if Oribi analytics can be carried out on a specific domain", - "retention": "1 day", + "description": "OCD_ln_or_description", + "retention": "OCD_retention_1_day", "dataController": "LinkedIn", "gdprUrl": "https://www.linkedin.com/legal/privacy-policy", "wildcard": "0" @@ -4499,8 +4950,8 @@ "category": "Marketing", "name": "li_sugr", "domain": "", - "description": "Used to make a probabilistic match of a user's identity outside the Designated Countries", - "retention": "90 days", + "description": "OCD_li_sugr_description", + "retention": "OCD_retention_90_days", "dataController": "LinkedIn", "gdprUrl": "https://www.linkedin.com/legal/privacy-policy", "wildcard": "0" @@ -4512,8 +4963,8 @@ "category": "Functional", "name": "sdsc", "domain": ".linkedin.com", - "description": "This cookie is used for signed data service context cookie used for database routing to ensure consistency across all databases when a change is made. Used to ensure that user-inputted content is immediately available to the submitting user upon submission", - "retention": "Session", + "description": "OCD_sdsc_description", + "retention": "OCD_retention_Session", "dataController": "LinkedIn", "gdprUrl": "https://www.linkedin.com/legal/cookie-policy", "wildcard": "0" @@ -4525,8 +4976,8 @@ "category": "Functional", "name": "li_mc", "domain": ".linkedin.com", - "description": "This cookie is used as a temporary cache to avoid database lookups for a member's consent for use of non-essential cookies and used for having consent information on the client side to enforce consent on the client side", - "retention": "6 months", + "description": "OCD_li_mc_description", + "retention": "OCD_retention_6_months", "dataController": "LinkedIn", "gdprUrl": "https://www.linkedin.com/legal/cookie-policy", "wildcard": "0" @@ -4538,8 +4989,8 @@ "category": "Marketing", "name": "lms_ads", "domain": ".linkedin.com", - "description": "This cookie is used to identify LinkedIn Members off LinkedIn for advertising", - "retention": "30 days", + "description": "OCD_lms_ads_description", + "retention": "OCD_retention_30_days", "dataController": "LinkedIn", "gdprUrl": "https://www.linkedin.com/legal/cookie-policy", "wildcard": "0" @@ -4551,8 +5002,8 @@ "category": "Marketing", "name": "_guid", "domain": "linkedin.com", - "description": "This cookie is used to identify a LinkedIn Member for advertising through Google Ads", - "retention": "90 days", + "description": "OCD__guid_description", + "retention": "OCD_retention_90_days", "dataController": "LinkedIn", "gdprUrl": "https://www.linkedin.com/legal/cookie-policy", "wildcard": "0" @@ -4564,8 +5015,8 @@ "category": "Marketing", "name": "BizographicsOptOut", "domain": ".linkedin.com", - "description": "This cookie is used to determine opt-out status for non-members", - "retention": "10 years", + "description": "OCD_BizographicsOptOut_description", + "retention": "OCD_retention_10_years", "dataController": "LinkedIn", "gdprUrl": "https://www.linkedin.com/legal/cookie-policy", "wildcard": "0" @@ -4577,8 +5028,8 @@ "category": "Marketing", "name": "IRLD", "domain": ".linkedin.com", - "description": "This cookie is used for Affiliate Marketing Cookie for LinkedIn", - "retention": "2 years", + "description": "OCD_IRLD_description", + "retention": "OCD_retention_2_years", "dataController": "LinkedIn", "gdprUrl": "https://www.linkedin.com/legal/cookie-policy", "wildcard": "0" @@ -4590,8 +5041,8 @@ "category": "Analytics", "name": "l_page", "domain": ".linkedin.com", - "description": "This cookie is used for measuring conversion metrics on LinkedIn", - "retention": "6 months", + "description": "OCD_l_page_description", + "retention": "OCD_retention_6_months", "dataController": "LinkedIn", "gdprUrl": "https://www.linkedin.com/legal/cookie-policy", "wildcard": "0" @@ -4603,8 +5054,8 @@ "category": "Marketing", "name": "ABSELB", "domain": ".linkedin.com", - "description": "This is Load Balancer Cookie for affiliate marketing", - "retention": "2 years", + "description": "OCD_ABSELB_description", + "retention": "OCD_retention_2_years", "dataController": "LinkedIn", "gdprUrl": "https://www.linkedin.com/legal/cookie-policy", "wildcard": "0" @@ -4616,8 +5067,8 @@ "category": "Marketing", "name": "brwsr", "domain": ".linkedin.com", - "description": "This cookie is used to Affiliate Marketing Cookie for LinkedIn", - "retention": "2 years", + "description": "OCD_brwsr_description", + "retention": "OCD_retention_2_years", "dataController": "LinkedIn", "gdprUrl": "https://www.linkedin.com/legal/cookie-policy", "wildcard": "0" @@ -4629,8 +5080,8 @@ "category": "Analytics", "name": "oribi_user_guid", "domain": ".oribi.io", - "description": "This cookie is used to identify a unique visitor", - "retention": "1 year", + "description": "OCD_oribi_user_guid_description", + "retention": "OCD_retention_1_year", "dataController": "Oribi", "gdprUrl": "https://www.linkedin.com/legal/cookie-policy", "wildcard": "0" @@ -4642,8 +5093,8 @@ "category": "Analytics", "name": "oribi_cookie_test", "domain": "linkedin.com", - "description": "This cookie is used To determine if tracking can be enabled on a current domain", - "retention": "Session", + "description": "OCD_oribi_cookie_test_description", + "retention": "OCD_retention_Session", "dataController": "Oribi", "gdprUrl": "https://www.linkedin.com/legal/cookie-policy", "wildcard": "0" @@ -4655,8 +5106,8 @@ "category": "Functional", "name": "AWSALB", "domain": "", - "description": "These cookies enable us to allocate server traffic to make the user experience as smooth as possible. A so-called load balancer is used to determine which server currently has the best availability. The information generated cannot identify you as an individual.", - "retention": "Session", + "description": "OCD_AWSALB_description", + "retention": "OCD_retention_Session", "dataController": "Amazon Web Services", "gdprUrl": "https://aws.amazon.com/privacy/", "wildcard": "0" @@ -4668,8 +5119,8 @@ "category": "Functional", "name": "AWSALBCORS", "domain": "", - "description": "For continued stickiness support with CORS use cases after the Chromium update, we are creating additional stickiness cookies for each of these duration-based stickiness features named AWSALBCORS (ALB).", - "retention": "Session", + "description": "OCD_AWSALBCORS_description", + "retention": "OCD_retention_Session", "dataController": "Amazon Web Services", "gdprUrl": "https://aws.amazon.com/privacy/", "wildcard": "0" @@ -4681,8 +5132,8 @@ "category": "Functional", "name": "AWSELBCORS", "domain": "", - "description": "For continued stickiness support with CORS use cases after the Chromium update, we are creating additional stickiness cookies for each of these duration-based stickiness features named AWSELBCORS (ALB).", - "retention": "Session", + "description": "OCD_AWSELBCORS_description", + "retention": "OCD_retention_Session", "dataController": "Amazon Web Services", "gdprUrl": "https://aws.amazon.com/privacy/", "wildcard": "0" @@ -4694,8 +5145,8 @@ "category": "Functional", "name": "AWSELB", "domain": "", - "description": "AWS Classic Load Balancer Cookie: Load Balancing Cookie: Used to map the session to the instance.", - "retention": "Session", + "description": "OCD_AWSELB_description", + "retention": "OCD_retention_Session", "dataController": "Amazon Web Services", "gdprUrl": "https://aws.amazon.com/privacy/", "wildcard": "0" @@ -4707,8 +5158,8 @@ "category": "Functional", "name": "AWSALBTGCORS", "domain": "", - "description": "For continued stickiness support with CORS use cases after the Chromium update, we are creating additional stickiness cookies for each of these duration-based stickiness features named AWSELBCORS (ALB).", - "retention": "Session", + "description": "OCD_AWSALBTGCORS_description", + "retention": "OCD_retention_Session", "dataController": "Amazon Web Services", "gdprUrl": "https://aws.amazon.com/privacy/", "wildcard": "0" @@ -4720,8 +5171,8 @@ "category": "Functional", "name": "AWSALBTG", "domain": "", - "description": "For continued stickiness support with CORS use cases after the Chromium update, we are creating additional stickiness cookies for each of these duration-based stickiness features named AWSELBCORS (ALB).", - "retention": "Session", + "description": "OCD_AWSALBTG_description", + "retention": "OCD_retention_Session", "dataController": "Amazon Web Services", "gdprUrl": "https://aws.amazon.com/privacy/", "wildcard": "0" @@ -4733,8 +5184,8 @@ "category": "Functional", "name": "aws-csds-token", "domain": "", - "description": "Anonymous metrics validation token", - "retention": "1 hour", + "description": "OCD_aws_csds_token_description", + "retention": "OCD_retention_1_hour", "dataController": "Amazon Web Services", "gdprUrl": "https://aws.amazon.com/privacy/", "wildcard": "0" @@ -4746,8 +5197,8 @@ "category": "Functional", "name": "aws_lang", "domain": "", - "description": "Stores the language used with AWS.", - "retention": "Session", + "description": "OCD_aws_lang_description", + "retention": "OCD_retention_Session", "dataController": "Amazon Web Services", "gdprUrl": "https://aws.amazon.com/privacy/", "wildcard": "0" @@ -4759,8 +5210,8 @@ "category": "Analytics", "name": "aws-target-visitor-id", "domain": "", - "description": "Used to collect anonymised information about how which web pages are visited, how long users spend on pages and what users search for.", - "retention": "1 Year", + "description": "OCD_aws_target_visitor_id_description", + "retention": "OCD_retention_1_Year", "dataController": "Amazon Web Services", "gdprUrl": "https://aws.amazon.com/privacy/", "wildcard": "0" @@ -4772,8 +5223,8 @@ "category": "Functional", "name": "aws-priv", "domain": "", - "description": "Anonymous cookie for privacy regulations", - "retention": "1 Year", + "description": "OCD_aws_priv_description", + "retention": "OCD_retention_1_Year", "dataController": "Amazon Web Services", "gdprUrl": "https://aws.amazon.com/privacy/", "wildcard": "0" @@ -4785,8 +5236,8 @@ "category": "Marketing", "name": "ad-id", "domain": "amazon-adsystem.com", - "description": "Clickthroughs to Amazon websites: Noting how the user got to Amazon via this website", - "retention": "190 days", + "description": "OCD_ad_id_description", + "retention": "OCD_retention_190_days", "dataController": "Amazon", "gdprUrl": "https://amazon.com/privacy/", "wildcard": "0" @@ -4798,8 +5249,8 @@ "category": "Marketing", "name": "ad-privacy", "domain": "amazon-adsystem.com", - "description": "Provided by amazon-adsystem.com for tracking user actions on other websites to provide targeted content to the users.", - "retention": "5 years", + "description": "OCD_ad_privacy_description", + "retention": "OCD_retention_5_years", "dataController": "Amazon", "gdprUrl": "https://amazon.com/privacy/", "wildcard": "0" @@ -4811,8 +5262,8 @@ "category": "Marketing", "name": "CMID", "domain": "casalemedia.com", - "description": "Collects visitor data related to the user's visits to the website, such as the number of visits, average time spent on the website and what pages have been loaded, with the purpose of displaying targeted ads.", - "retention": "1 day", + "description": "OCD_CMID_description", + "retention": "OCD_retention_1_day", "dataController": "Casale Media", "gdprUrl": "https://casalemedia.com", "wildcard": "0" @@ -4824,8 +5275,8 @@ "category": "Marketing", "name": "CMPRO", "domain": "casalemedia.com", - "description": "Collects data on visitor behaviour from multiple websites, in order to present more relevant advertisement - This also allows the website to limit the number of times that the visitor is shown the same advertisement.", - "retention": "1 day", + "description": "OCD_CMPRO_description", + "retention": "OCD_retention_1_day", "dataController": "Casale Media", "gdprUrl": "https://casalemedia.com", "wildcard": "0" @@ -4837,8 +5288,8 @@ "category": "Marketing", "name": "CMPS", "domain": "casalemedia.com", - "description": "Collects visitor data related to the user's visits to the website, such as the number of visits, average time spent on the website and what pages have been loaded, with the purpose of displaying targeted ads", - "retention": "1 day", + "description": "OCD_CMPS_description", + "retention": "OCD_retention_1_day", "dataController": "Casale Media", "gdprUrl": "https://casalemedia.com", "wildcard": "0" @@ -4850,8 +5301,8 @@ "category": "Marketing", "name": "CMRUM3", "domain": "casalemedia.com", - "description": "Collects visitor data related to the user's visits to the website, such as the number of visits, average time spent on the website and what pages have been loaded, with the purpose of displaying targeted ads.", - "retention": "1 day", + "description": "OCD_CMRUM3_description", + "retention": "OCD_retention_1_day", "dataController": "Casale Media", "gdprUrl": "https://casalemedia.com", "wildcard": "0" @@ -4863,8 +5314,8 @@ "category": "Marketing", "name": "CMST", "domain": "casalemedia.com", - "description": "Collects visitor data related to the user's visits to the website, such as the number of visits, average time spent on the website and what pages have been loaded, with the purpose of displaying targeted ads.", - "retention": "1 day", + "description": "OCD_CMST_description", + "retention": "OCD_retention_1_day", "dataController": "Casale Media", "gdprUrl": "https://casalemedia.com", "wildcard": "0" @@ -4876,8 +5327,8 @@ "category": "Marketing", "name": "cookieJartestCookie", "domain": "outbrain.com", - "description": "Pending", - "retention": "1 day", + "description": "OCD_cookieJartestCookie_description", + "retention": "OCD_retention_1_day", "dataController": "Outbrain", "gdprUrl": "https://www.outbrain.com/legal/privacy#privacy-policy", "wildcard": "0" @@ -4889,8 +5340,8 @@ "category": "Marketing", "name": "obuid", "domain": "outbrain.com", - "description": "Holds the anonymous user's ID. Used for tracking user actions, such as clicks on the recommendations", - "retention": "3 months", + "description": "OCD_obuid_description", + "retention": "OCD_retention_3_months", "dataController": "Outbrain", "gdprUrl": "https://www.outbrain.com/legal/privacy#privacy-policy", "wildcard": "0" @@ -4902,8 +5353,8 @@ "category": "Marketing", "name": "apnxs", "domain": "outbrain.com", - "description": "This cookie is set by Outbrain and it is used to analyse technical data about the website", - "retention": "4 months", + "description": "OCD_apnxs_description", + "retention": "OCD_retention_4_months", "dataController": "Outbrain", "gdprUrl": "https://www.outbrain.com/legal/privacy#privacy-policy", "wildcard": "0" @@ -4915,8 +5366,8 @@ "category": "Marketing", "name": "criteo", "domain": "outbrain.com", - "description": "This cookie is set by Outbrain and it is used to analyse technical data about the website", - "retention": "1 months", + "description": "OCD_criteo_description", + "retention": "OCD_retention_1_months", "dataController": "Outbrain", "gdprUrl": "https://www.outbrain.com/legal/privacy#privacy-policy", "wildcard": "0" @@ -4928,8 +5379,8 @@ "category": "Marketing", "name": "mdfrc", "domain": "outbrain.com", - "description": "This cookie is set by Outbrain and it is used to analyse technical data about the website", - "retention": "4 months", + "description": "OCD_mdfrc_description", + "retention": "OCD_retention_4_months", "dataController": "Outbrain", "gdprUrl": "https://www.outbrain.com/legal/privacy#privacy-policy", "wildcard": "0" @@ -4941,8 +5392,8 @@ "category": "Marketing", "name": "adrl", "domain": "outbrain.com", - "description": "This cookie is set by Outbrain and it is used to analyse technical data about the website", - "retention": "4 months", + "description": "OCD_adrl_description", + "retention": "OCD_retention_4_months", "dataController": "Outbrain", "gdprUrl": "https://www.outbrain.com/legal/privacy#privacy-policy", "wildcard": "0" @@ -4954,8 +5405,8 @@ "category": "Marketing", "name": "ttd", "domain": "outbrain.com", - "description": "This cookie is set by Outbrain and it is used to analyse technical data about the website", - "retention": "4 months", + "description": "OCD_ttd_description", + "retention": "OCD_retention_4_months", "dataController": "Outbrain", "gdprUrl": "https://www.outbrain.com/legal/privacy#privacy-policy", "wildcard": "0" @@ -4967,8 +5418,8 @@ "category": "Marketing", "name": "recs", "domain": "outbrain.com", - "description": "Stores the recommendations we’re recommending so that we don’t show only the same recommendations on the same page", - "retention": "1 minute", + "description": "OCD_recs_description", + "retention": "OCD_retention_1_minute", "dataController": "Outbrain", "gdprUrl": "https://www.outbrain.com/legal/privacy#privacy-policy", "wildcard": "0" @@ -4980,8 +5431,8 @@ "category": "Marketing", "name": "obsessionid-*", "domain": "outbrain.com", - "description": "Stores a unique identifier of the session so that we don’t show only the same recommendations on the same session", - "retention": "30 minutes", + "description": "OCD_obsessionid___description", + "retention": "OCD_retention_30_minutes", "dataController": "Outbrain", "gdprUrl": "https://www.outbrain.com/legal/privacy#privacy-policy", "wildcard": "1" @@ -4993,8 +5444,8 @@ "category": "Functional", "name": "_cq_duid", "domain": "", - "description": "Used by the website to protect against fraud in relation to its referral system.", - "retention": "3 months", + "description": "OCD__cq_duid_description", + "retention": "OCD_retention_3_months", "dataController": "CHEQ AI Technologies", "gdprUrl": "https://cheq.ai/privacy-compliance/", "wildcard": "0" @@ -5006,8 +5457,8 @@ "category": "Functional", "name": "_cq_suid", "domain": "", - "description": "This cookie is used to distinguish between humans and bots.", - "retention": "3 months", + "description": "OCD__cq_suid_description", + "retention": "OCD_retention_3_months", "dataController": "CHEQ AI Technologies", "gdprUrl": "https://cheq.ai/privacy-compliance/", "wildcard": "0" @@ -5019,8 +5470,8 @@ "category": "Marketing", "name": "jpxumaster", "domain": "justpremium.com", - "description": "Used to present the visitor with relevant content and advertisement - The service is provided by third party advertisement hubs, which facilitate real-time bidding for advertisers.", - "retention": "1 month", + "description": "OCD_jpxumaster_description", + "retention": "OCD_retention_1_month", "dataController": "JustPremium", "gdprUrl": "https://justpremium.com/terms-conditions/", "wildcard": "0" @@ -5032,8 +5483,8 @@ "category": "Marketing", "name": "jpxumatched", "domain": "justpremium.com", - "description": "Used to present the visitor with relevant content and advertisement - The service is provided by third party advertisement hubs, which facilitate real-time bidding for advertisers.", - "retention": "1 month", + "description": "OCD_jpxumatched_description", + "retention": "OCD_retention_1_month", "dataController": "JustPremium", "gdprUrl": "https://justpremium.com/terms-conditions/", "wildcard": "0" @@ -5045,8 +5496,8 @@ "category": "Marketing", "name": "PUBMDCID", "domain": "pubmatic.com", - "description": "Registers a unique ID that identifies the user's device during return visits across websites that use the same ad network. The ID is used to allow targeted ads.", - "retention": "3 months", + "description": "OCD_PUBMDCID_description", + "retention": "OCD_retention_3_months", "dataController": "PubMatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "0" @@ -5058,8 +5509,8 @@ "category": "Marketing", "name": "pp", "domain": "pubmatic.com", - "description": "This cookie tracks the last publisher website that you visited that contained an advertisement served by PubMatic.", - "retention": "3 months", + "description": "OCD_pp_description", + "retention": "OCD_retention_3_months", "dataController": "PubMatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "0" @@ -5071,8 +5522,8 @@ "category": "Marketing", "name": "SPugT", "domain": "pubmatic.com", - "description": "This cookie is used to track when the server-side cookie store was last updated for the browser, and it is used in conjunction with the PugT cookie, described below.", - "retention": "30 days", + "description": "OCD_SPugT_description", + "retention": "OCD_retention_30_days", "dataController": "PubMatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "0" @@ -5084,8 +5535,8 @@ "category": "Marketing", "name": "KADUSERCOOKIE", "domain": "pubmatic.com", - "description": "PubMatic UserId. this identifier to identify each user uniquely. Some of the uses of this anonymous identifier are to support frequency capping, perform UID sync ups with DSP's, DMP's. DMP's / DP's push audicne data against this ID. API publishers sends this ID while making API requests to PubMatic AdServer. UAS Ad Engine also uses this cookie for FCAP purposes.", - "retention": "90 days", + "description": "OCD_KADUSERCOOKIE_description", + "retention": "OCD_retention_90_days", "dataController": "PubMatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "0" @@ -5097,8 +5548,8 @@ "category": "Marketing", "name": "PUBRETARGET", "domain": "pubmatic.com", - "description": "Pixel expiry. Used to indicate if user must be considered for various re-targeting ad campaigns running in PubMatic system.", - "retention": "90 days", + "description": "OCD_PUBRETARGET_description", + "retention": "OCD_retention_90_days", "dataController": "PubMatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "0" @@ -5110,8 +5561,8 @@ "category": "Marketing", "name": "KCCH", "domain": "pubmatic.com", - "description": "To avoid race condition in PubMatic userId generation, showad.js / universalpixel.js set this cookie first. if and only if not set already. Existence of this cookie means that current flow of the execution should not generate PubMatic userId cookie, as its already being set by other flow which has set KCCH.", - "retention": "30 secs", + "description": "OCD_KCCH_description", + "retention": "OCD_retention_30_secs", "dataController": "PubMatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "0" @@ -5123,8 +5574,8 @@ "category": "Marketing", "name": "SyncRTB*", "domain": "pubmatic.com", - "description": "Keeps list of DSP pixel Id's PubMatic synced with so far. PubMatic does userId sync up with DSP's. This cookie holds next sync up time for every pixel. Helps to maintain sync up frequency at DSP level.", - "retention": "90 days", + "description": "OCD_SyncRTB__description", + "retention": "OCD_retention_90_days", "dataController": "PubMatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "1" @@ -5136,8 +5587,8 @@ "category": "Marketing", "name": "DPSync*", "domain": "pubmatic.com", - "description": "Keeps list of DMP pixel Id's PubMatic synced with so far PubMatic does userId sync up with DMP's. This cookie holds next sync up time for every pixel. Helps to maintain sync up frequency at DMP level.", - "retention": "90 days", + "description": "OCD_DPSync__description", + "retention": "OCD_retention_90_days", "dataController": "PubMatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "1" @@ -5149,8 +5600,8 @@ "category": "Marketing", "name": "ADUSERCOOKIE", "domain": "pubmatic.com", - "description": "PubMatic UserId. this identifier to identify each user uniquely. Some of the uses of this anonymous identifier are to support frequency capping, perform UID sync ups with DSP's, DMP's. DMP's / DP's push audicne data against this ID. API publishers sends this ID while making API requests to PubMatic AdServer. UAS Ad Engine also uses this cookie for FCAP purposes.", - "retention": "90 days", + "description": "OCD_ADUSERCOOKIE_description", + "retention": "OCD_retention_90_days", "dataController": "PubMatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "0" @@ -5162,8 +5613,8 @@ "category": "Marketing", "name": "PugT", "domain": "pubmatic.com", - "description": "It is used to track when the cookies were updated on the browser. It is used to limit the number of calls to server side cookie store", - "retention": "30 days", + "description": "OCD_PugT_description", + "retention": "OCD_retention_30_days", "dataController": "PubMatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "0" @@ -5175,8 +5626,8 @@ "category": "Marketing", "name": "KRTBCOOKIE_*", "domain": "pubmatic.com", - "description": "Registers a unique ID that identifies the user's device during return visits across websites that use the same ad network. The ID is used to allow targeted ads.", - "retention": "29 days", + "description": "OCD_KRTBCOOKIE___description", + "retention": "OCD_retention_29_days", "dataController": "Pubmatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "1" @@ -5188,8 +5639,8 @@ "category": "Analytics", "name": "f5_cspm", "domain": "simage2.pubmatic.com", - "description": "This cookie name is associated with the BIG-IP product suite from company F5. It is used to monitor page load speed, as part of site performance monitoring.", - "retention": "Session", + "description": "OCD_f5_cspm_description", + "retention": "OCD_retention_Session", "dataController": "Pubmatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "0" @@ -5201,8 +5652,8 @@ "category": "Functional", "name": "KTPCACOOKIE", "domain": "pubmatic.com", - "description": "We use this cookie to check if third-party cookies are enabled on the user’s browser.", - "retention": "90 days", + "description": "OCD_KTPCACOOKIE_description", + "retention": "OCD_retention_90_days", "dataController": "Pubmatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "0" @@ -5214,8 +5665,8 @@ "category": "Functional", "name": "COKENBLD", "domain": "pubmatic.com", - "description": "This cookie sets a flag to “true” if cookies are enabled on the user’s browser.", - "retention": "1 day", + "description": "OCD_COKENBLD_description", + "retention": "OCD_retention_1_day", "dataController": "Pubmatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "0" @@ -5227,8 +5678,8 @@ "category": "Marketing", "name": "USCC", "domain": "pubmatic.com", - "description": "This cookie enables PubMatic to sync user IDs properly in situations where multiple advertisements might appear on the same webpage.", - "retention": "1 day", + "description": "OCD_USCC_description", + "retention": "OCD_retention_1_day", "dataController": "Pubmatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "0" @@ -5240,8 +5691,8 @@ "category": "Marketing", "name": "DPPIX_ON", "domain": "pubmatic.com", - "description": "These cookies enable PubMatic to properly sync cookie IDs with our partners by ensuring that our partners do not override each other during the sync process.", - "retention": "20 seconds", + "description": "OCD_DPPIX_ON_description", + "retention": "OCD_retention_20_seconds", "dataController": "Pubmatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "0" @@ -5253,8 +5704,8 @@ "category": "Marketing", "name": "SYNCUPPIX_ON", "domain": "pubmatic.com", - "description": "These cookies enable PubMatic to properly sync cookie IDs with our partners by ensuring that our partners do not override each other during the sync process.", - "retention": "20 seconds", + "description": "OCD_SYNCUPPIX_ON_description", + "retention": "OCD_retention_20_seconds", "dataController": "Pubmatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "0" @@ -5266,8 +5717,8 @@ "category": "Functional", "name": "PUBUIDSYNCUPFQ", "domain": "pubmatic.com", - "description": "This cookie indicates the last time that we synced IDs with our partner.", - "retention": "3 months", + "description": "OCD_PUBUIDSYNCUPFQ_description", + "retention": "OCD_retention_3_months", "dataController": "Pubmatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "0" @@ -5279,8 +5730,8 @@ "category": "Marketing", "name": "camfreq_*", "domain": "pubmatic.com", - "description": "This cookie is set for each campaign and indicates the number of times (e.g., frequency) that a particular advertisement may have been shown on the applicable publisher site.", - "retention": "30 days", + "description": "OCD_camfreq___description", + "retention": "OCD_retention_30_days", "dataController": "Pubmatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "1" @@ -5292,8 +5743,8 @@ "category": "Marketing", "name": "pubfreq_*", "domain": "pubmatic.com", - "description": "This cookie is set for each advertising network and indicates the number of times (e.g., frequency) that a particular advertisement may have been shown on the applicable publisher site.", - "retention": "30 days", + "description": "OCD_pubfreq___description", + "retention": "OCD_retention_30_days", "dataController": "Pubmatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "1" @@ -5305,8 +5756,8 @@ "category": "Functional", "name": "pubtime_*", "domain": "pubmatic.com", - "description": "This cookie stores the period of time after which ad frequency counters reset.", - "retention": "1 day", + "description": "OCD_pubtime___description", + "retention": "OCD_retention_1_day", "dataController": "Pubmatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "1" @@ -5318,8 +5769,8 @@ "category": "Functional", "name": "PMFREQ_ON", "domain": "pubmatic.com", - "description": "This cookie ensures the proper functioning of the camfreq and pubfreq cookies, described above, in situations where one cookie may override the other.", - "retention": "1 day", + "description": "OCD_PMFREQ_ON_description", + "retention": "OCD_retention_1_day", "dataController": "Pubmatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "0" @@ -5331,8 +5782,8 @@ "category": "Marketing", "name": "DPFQ", "domain": "pubmatic.com", - "description": "This cookie stores information regarding the number of times that a partner’s pixel is loaded by a user’s browser. This enables us to cap the number of times that a pixel is used to record a user’s visit to a website within a specific period of time.", - "retention": "90 days", + "description": "OCD_DPFQ_description", + "retention": "OCD_retention_90_days", "dataController": "Pubmatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "0" @@ -5344,8 +5795,8 @@ "category": "Marketing", "name": "pi", "domain": "pubmatic.com", - "description": "This cookie enables us to determine which set of pixels needs to be executed on the browser.", - "retention": "1 day", + "description": "OCD_pi_description", + "retention": "OCD_retention_1_day", "dataController": "Pubmatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "0" @@ -5357,8 +5808,8 @@ "category": "Functional", "name": "FPtrust", "domain": "pubmatic.com", - "description": "This cookie is a session cookie used to support the opt-out process via the Network Advertising Initiative.", - "retention": "1 day", + "description": "OCD_FPtrust_description", + "retention": "OCD_retention_1_day", "dataController": "Pubmatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "0" @@ -5370,8 +5821,8 @@ "category": "Functional", "name": "_curtime", "domain": "pubmatic.com", - "description": "This cookie stores the current timestamp.", - "retention": "1 day", + "description": "OCD__curtime_description", + "retention": "OCD_retention_1_day", "dataController": "Pubmatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "0" @@ -5383,8 +5834,8 @@ "category": "Functional", "name": "PMDTSHR", "domain": "pubmatic.com", - "description": "This cookie is set for Komli ad server and is used for default impression when other data is not available.", - "retention": "1 day", + "description": "OCD_PMDTSHR_description", + "retention": "OCD_retention_1_day", "dataController": "Pubmatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "0" @@ -5396,11 +5847,22 @@ "category": "Functional", "name": "chk", "domain": "pubmatic.com", - "description": "This cookie is set on Google Chrome browsers that have a version less 67 or non-Chrome browsers, and is used for testing purposes.", - "retention": "3 months", + "description": "OCD_chk_description", + "retention": "OCD_retention_3_months", "dataController": "Pubmatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "0" + }, + { + "platform": "Mediarithmics", + "category": "Marketing", + "name": "chk", + "domain": ".mediarithmics.com", + "description": "This cookie is added by mediarithmics In the case of a call on events.mediarithmics.com without a cookie mics_vid, this cookie is written to check that the user browser supports third party cookies. It contains a randomly generated UUID.", + "retention": "1 hour", + "dataController": "Mediarithmics", + "gdprUrl": "https://developer.mediarithmics.io/advanced-usages/data-privacy-compliance/cookies", + "wildcard": "0" } ], "chkSecSet": [ @@ -5409,8 +5871,8 @@ "category": "Functional", "name": "chkSecSet", "domain": "pubmatic.com", - "description": "This cookie is set on Google Chrome browsers that have a version less 67 or non-Chrome browsers, and is used for testing purposes.", - "retention": "3 months", + "description": "OCD_chkSecSet_description", + "retention": "OCD_retention_3_months", "dataController": "Pubmatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "0" @@ -5422,8 +5884,8 @@ "category": "Functional", "name": "chkChromeAb67", "domain": "pubmatic.com", - "description": "This cookie is set on Google Chrome browsers that have a version above 67 and is used for testing purposes.", - "retention": "3 months", + "description": "OCD_chkChromeAb67_description", + "retention": "OCD_retention_3_months", "dataController": "Pubmatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "0" @@ -5435,8 +5897,8 @@ "category": "Functional", "name": "chkChromeAb67Sec", "domain": "pubmatic.com", - "description": "This cookie is set on Google Chrome browsers that have a version above 67 and is used for testing purposes.", - "retention": "3 months", + "description": "OCD_chkChromeAb67Sec_description", + "retention": "OCD_retention_3_months", "dataController": "Pubmatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "0" @@ -5448,8 +5910,8 @@ "category": "Functional", "name": "pubsyncexp", "domain": "pubmatic.com", - "description": "Sets a unique ID for the visitor, that allows third party advertisers to target the visitor with relevant advertisement. This pairing service is provided by third party advertisement hubs, which facilitates real-time bidding for advertisers.", - "retention": "1 day", + "description": "OCD_pubsyncexp_description", + "retention": "OCD_retention_1_day", "dataController": "Pubmatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "0" @@ -5461,8 +5923,8 @@ "category": "Functional", "name": "ipc", "domain": "pubmatic.com", - "description": "This cookie is a short-lived cookie that stores information needed to coordinate cookie syncing.", - "retention": "1 year", + "description": "OCD_ipc_description", + "retention": "OCD_retention_1_year", "dataController": "Pubmatic", "gdprUrl": "https://pubmatic.com/legal/privacy/", "wildcard": "0" @@ -5474,8 +5936,8 @@ "category": "Functional", "name": "Kiyohnl", "domain": "kiyoh.nl", - "description": "Cookies are associated with the use of Kiyoh to collect and display customer reviews", - "retention": "1 year", + "description": "OCD_Kiyohnl_description", + "retention": "OCD_retention_1_year", "dataController": "Kiyoh", "gdprUrl": "https://www.kiyoh.nl/disclaimer.html", "wildcard": "0" @@ -5487,8 +5949,8 @@ "category": "Marketing", "name": "id5", "domain": "id5-sync.com", - "description": "Sets a unique ID for the visitor, that allows third party advertisers to target the visitor with relevant advertisement. This pairing service is provided by third party advertisement hubs, which facilitates real-time bidding for advertisers.", - "retention": "1 day", + "description": "OCD_id5_description", + "retention": "OCD_retention_1_day", "dataController": "ID5", "gdprUrl": "https://www.id5.io/privacy-policy", "wildcard": "0" @@ -5500,8 +5962,8 @@ "category": "Marketing", "name": "cip", "domain": "id5-sync.com", - "description": "Used to present the visitor with relevant content and advertisement - The service is provided by third party advertisement hubs, which facilitate real-time bidding for advertisers.", - "retention": "1 day", + "description": "OCD_cip_description", + "retention": "OCD_retention_1_day", "dataController": "ID5", "gdprUrl": "https://www.id5.io/privacy-policy", "wildcard": "0" @@ -5513,8 +5975,8 @@ "category": "Marketing", "name": "car", "domain": "id5-sync.com", - "description": "Used to present the visitor with relevant content and advertisement - The service is provided by third party advertisement hubs, which facilitate real-time bidding for advertisers.", - "retention": "1 day", + "description": "OCD_car_description", + "retention": "OCD_retention_1_day", "dataController": "ID5", "gdprUrl": "https://www.id5.io/privacy-policy", "wildcard": "0" @@ -5526,8 +5988,8 @@ "category": "Marketing", "name": "callback", "domain": "id5-sync.com", - "description": "Collects data on visitor behaviour from multiple websites, in order to present more relevant advertisement - This also allows the website to limit the number of times that the visitor is shown the same advertisement.", - "retention": "1 day", + "description": "OCD_callback_description", + "retention": "OCD_retention_1_day", "dataController": "ID5", "gdprUrl": "https://www.id5.io/privacy-policy", "wildcard": "0" @@ -5539,8 +6001,8 @@ "category": "Marketing", "name": "cnac", "domain": "id5-sync.com", - "description": "Used to present the visitor with relevant content and advertisement - The service is provided by third party advertisement hubs, which facilitate real-time bidding for advertisers.", - "retention": "1 day", + "description": "OCD_cnac_description", + "retention": "OCD_retention_1_day", "dataController": "ID5", "gdprUrl": "https://www.id5.io/privacy-policy", "wildcard": "0" @@ -5552,8 +6014,8 @@ "category": "Marketing", "name": "cf", "domain": "id5-sync.com", - "description": "Sets a unique ID for the visitor, that allows third party advertisers to target the visitor with relevant advertisement. This pairing service is provided by third party advertisement hubs, which facilitates real-time bidding for advertisers.", - "retention": "1 day", + "description": "OCD_cf_description", + "retention": "OCD_retention_1_day", "dataController": "ID5", "gdprUrl": "https://www.id5.io/privacy-policy", "wildcard": "0" @@ -5565,8 +6027,8 @@ "category": "Functional", "name": "gdpr", "domain": "id5-sync.com", - "description": "Determines whether the visitor has accepted the cookie consent box. This ensures that the cookie consent box will not be presented again upon re-en try.", - "retention": "1 day", + "description": "OCD_gdpr_description", + "retention": "OCD_retention_1_day", "dataController": "ID5", "gdprUrl": "https://www.id5.io/privacy-policy", "wildcard": "0" @@ -5578,8 +6040,8 @@ "category": "Marketing", "name": "UIDR", "domain": ".scorecardresearch.com", - "description": "Collects information of the user and his/her movement, such as timestamp for visits, most recently loaded pages and IP address. The data is used by the marketing research network, Scorecard Research, to analyse traffic patterns and carry out surveys to help their clients better understand the customer's preferences.", - "retention": "2 years", + "description": "OCD_UIDR_description", + "retention": "OCD_retention_2_years", "dataController": "ComScore", "gdprUrl": "https://www.comscore.com/About/Privacy-Policy", "wildcard": "0" @@ -5591,8 +6053,8 @@ "category": "Marketing", "name": "UID", "domain": ".scorecardresearch.com", - "description": "Collects information of the user and his/her movement, such as timestamp for visits, most recently loaded pages and IP address. The data is used by the marketing research network, Scorecard Research, to analyse traffic patterns and carry out surveys to help their clients better understand the customer's preferences.", - "retention": "2 years", + "description": "OCD_UID_description", + "retention": "OCD_retention_2_years", "dataController": "ComScore", "gdprUrl": "https://www.comscore.com/About/Privacy-Policy", "wildcard": "0" @@ -5604,8 +6066,8 @@ "category": "Marketing", "name": "PID", "domain": ".scorecardresearch.com", - "description": "Collects a code that identifies the specific website or advertiser participating in the ScorecardResearch data collection program.", - "retention": "1 year", + "description": "OCD_PID_description", + "retention": "OCD_retention_1_year", "dataController": "ComScore", "gdprUrl": "https://www.comscore.com/About/Privacy-Policy", "wildcard": "0" @@ -5617,8 +6079,8 @@ "category": "Marketing", "name": "XID", "domain": ".scorecardresearch.com", - "description": "Collects a unique identifier assigned to a device (computer, phone, tablet) to track the user across different websites.", - "retention": "1 year", + "description": "OCD_XID_description", + "retention": "OCD_retention_1_year", "dataController": "ComScore", "gdprUrl": "https://www.comscore.com/About/Privacy-Policy", "wildcard": "0" @@ -5630,8 +6092,8 @@ "category": "Marketing", "name": "SEUNCY", "domain": "semasio.net", - "description": "Registers a unique ID that identifies the user’s device for return visits.", - "retention": "179 days", + "description": "OCD_SEUNCY_description", + "retention": "OCD_retention_179_days", "dataController": "semasio.net", "gdprUrl": "http://www.semasio.net", "wildcard": "0" @@ -5643,8 +6105,8 @@ "category": "Marketing", "name": "ljt_reader", "domain": "", - "description": "Collects data related to reader interests, context, demographics and other information on behalf of the Lijit platform with the purpose of finding interested users on websites with related content.", - "retention": "1 year", + "description": "OCD_ljt_reader_description", + "retention": "OCD_retention_1_year", "dataController": "Federated Media Publishing", "gdprUrl": "", "wildcard": "0" @@ -5656,8 +6118,8 @@ "category": "Marketing", "name": "rek_content", "domain": "rekmob.com", - "description": "Pending", - "retention": "6 days", + "description": "OCD_rek_content_description", + "retention": "OCD_retention_6_days", "dataController": "rekmob.com", "gdprUrl": "", "wildcard": "0" @@ -5669,8 +6131,8 @@ "category": "Marketing", "name": "um", "domain": "ad.360yield.com", - "description": "To enable the bidding process.", - "retention": "90 days", + "description": "OCD_um_description", + "retention": "OCD_retention_90_days", "dataController": "ad.360yield.com", "gdprUrl": "https://www.improvedigital.com/platform-privacy-policy/", "wildcard": "0" @@ -5682,8 +6144,8 @@ "category": "Marketing", "name": "umeh", "domain": "ad.360yield.com", - "description": "To enable the bidding process.", - "retention": "90 days", + "description": "OCD_umeh_description", + "retention": "OCD_retention_90_days", "dataController": "ad.360yield.com", "gdprUrl": "https://www.improvedigital.com/platform-privacy-policy/", "wildcard": "0" @@ -5695,8 +6157,8 @@ "category": "Marketing", "name": "BSWtracker", "domain": "vmg.host", - "description": "Collects data on visitor behaviour from multiple websites, in order to present more relevant advertisement - This also allows the website to limit the number of times that the visitor is shown the same advertisement.", - "retention": "694 days", + "description": "OCD_BSWtracker_description", + "retention": "OCD_retention_694_days", "dataController": "vmg.host", "gdprUrl": "", "wildcard": "0" @@ -5708,8 +6170,8 @@ "category": "Marketing", "name": "_rxuuid", "domain": "1rx.io", - "description": "Sets a unique ID for the visitor, with which external advertisers can target the visitor with relevant advertisements. This linking service is provided by third-party advertising hubs, facilitating real-time bidding for advertisers.", - "retention": "1 year", + "description": "OCD__rxuuid_description", + "retention": "OCD_retention_1_year", "dataController": "1rx.io", "gdprUrl": "", "wildcard": "0" @@ -5721,8 +6183,8 @@ "category": "Marketing", "name": "AA003", "domain": "atdmt.com", - "description": "Collects information on visitor behaviour on multiple websites. This information is used on the website, in order to optimize the relevance of advertisement.", - "retention": "3 months", + "description": "OCD_AA003_description", + "retention": "OCD_retention_3_months", "dataController": "Atlas", "gdprUrl": "", "wildcard": "0" @@ -5734,8 +6196,8 @@ "category": "Marketing", "name": "ATN", "domain": "atdmt.com", - "description": "Collects information on visitor behaviour on multiple websites. This information is used on the website, in order to optimize the relevance of advertisement.", - "retention": "3 months", + "description": "OCD_ATN_description", + "retention": "OCD_retention_3_months", "dataController": "Atlas", "gdprUrl": "", "wildcard": "0" @@ -5747,8 +6209,8 @@ "category": "Marketing", "name": "tt_viewer", "domain": "teads.com", - "description": "Teads uses a “tt_viewer” cookie to help personalize the video ads you see on our partner websites.", - "retention": "1 year", + "description": "OCD_tt_viewer_description", + "retention": "OCD_retention_1_year", "dataController": "Teads.com", "gdprUrl": "https://www.teads.com/privacy-policy/", "wildcard": "0" @@ -5760,8 +6222,8 @@ "category": "Marketing", "name": "tt_bluekai", "domain": ".teads.tv", - "description": "Avoid calling to bluekai. This avoids unnecessary calls to bluekai.", - "retention": "1 day", + "description": "OCD_tt_bluekai_description", + "retention": "OCD_retention_1_day", "dataController": "Teads.com", "gdprUrl": "https://www.teads.com/privacy-policy/", "wildcard": "0" @@ -5773,8 +6235,8 @@ "category": "Marketing", "name": "tt_exelate", "domain": ".teads.tv", - "description": "Avoid calling to Exelate. This avoids unnecessary calls to Eleate.", - "retention": "1 day", + "description": "OCD_tt_exelate_description", + "retention": "OCD_retention_1_day", "dataController": "Teads.com", "gdprUrl": "https://www.teads.com/privacy-policy/", "wildcard": "0" @@ -5786,8 +6248,8 @@ "category": "Marketing", "name": "tt_liveramp", "domain": ".teads.tv", - "description": "Avoid calling to Liveramp. This avoids unnecessary calls to Liveramp.", - "retention": "1 day", + "description": "OCD_tt_liveramp_description", + "retention": "OCD_retention_1_day", "dataController": "Teads.com", "gdprUrl": "https://www.teads.com/privacy-policy/", "wildcard": "0" @@ -5799,8 +6261,8 @@ "category": "Marketing", "name": "tt_neustar", "domain": ".teads.tv", - "description": "Avoid calling to Nuestar. This avoids unnecessary calls to Neustar.", - "retention": "1 day", + "description": "OCD_tt_neustar_description", + "retention": "OCD_retention_1_day", "dataController": "Teads.com", "gdprUrl": "https://www.teads.com/privacy-policy/", "wildcard": "0" @@ -5812,8 +6274,8 @@ "category": "Marketing", "name": "tt_salesforce", "domain": ".teads.tv", - "description": "Avoid calling to Salesforce. This avoids unnecessary calls to Salesforce.", - "retention": "1 day", + "description": "OCD_tt_salesforce_description", + "retention": "OCD_retention_1_day", "dataController": "Teads.com", "gdprUrl": "https://www.teads.com/privacy-policy/", "wildcard": "0" @@ -5825,8 +6287,8 @@ "category": "Functional", "name": "cfid", "domain": "", - "description": "This cookie is used to determine which type of device the visitor is using, so the website can be properly formatted", - "retention": "1 day", + "description": "OCD_cfid_description", + "retention": "OCD_retention_1_day", "dataController": "Adobe ColdFusion", "gdprUrl": "", "wildcard": "0" @@ -5838,8 +6300,8 @@ "category": "Functional", "name": "cftoken", "domain": "", - "description": "This cookie is used to determine which type of device the visitor is using, so the website can be properly formatted", - "retention": "1 day", + "description": "OCD_cftoken_description", + "retention": "OCD_retention_1_day", "dataController": "Adobe ColdFusion", "gdprUrl": "", "wildcard": "0" @@ -5851,8 +6313,8 @@ "category": "Marketing", "name": "gckp", "domain": "cxense.com", - "description": "Registers a unique ID that identifies a returning user's device. The ID is used for targeted ads.", - "retention": "1 year", + "description": "OCD_gckp_description", + "retention": "OCD_retention_1_year", "dataController": "CXense", "gdprUrl": "https://www.cxense.com/about-us/platform-privacy-policy", "wildcard": "0" @@ -5875,8 +6337,8 @@ "category": "Marketing", "name": "um2", "domain": "visx.net", - "description": "Sets a unique ID for the visitor, that allows third party advertisers to target the visitor with relevant advertisement. This pairing service is provided by third party advertisement hubs, which facilitates real-time bidding for advertisers.", - "retention": "2 years", + "description": "OCD_um2_description", + "retention": "OCD_retention_2_years", "dataController": "visx.net", "gdprUrl": "", "wildcard": "0" @@ -5888,8 +6350,8 @@ "category": "Marketing", "name": "tuuid_lu", "domain": "bidswitch.net", - "description": "Contains a unique visitor ID, which allows Bidswitch.com to track the visitor across multiple websites. This allows Bidswitch to optimize advertisement relevance and ensure that the visitor does not see the same ads multiple times.", - "retention": "3 months", + "description": "OCD_tuuid_lu_description", + "retention": "OCD_retention_3_months", "dataController": "bidswitch.net", "gdprUrl": "", "wildcard": "0" @@ -5901,8 +6363,8 @@ "category": "Marketing", "name": "uu", "domain": "adscale.de", - "description": "Used to target ads by registering the user's movements across websites.", - "retention": "1 year", + "description": "OCD_uu_description", + "retention": "OCD_retention_1_year", "dataController": "adscale.de", "gdprUrl": "", "wildcard": "0" @@ -5914,8 +6376,8 @@ "category": "Marketing", "name": "cct", "domain": "adscale.de", - "description": "Necessary for the shopping cart functionality on the website", - "retention": "session", + "description": "OCD_cct_description", + "retention": "OCD_retention_session", "dataController": "adscale.de", "gdprUrl": "", "wildcard": "0" @@ -5927,8 +6389,8 @@ "category": "Marketing", "name": "tu", "domain": "adscale.de", - "description": "Used to target ads by registering the user's movements across websites.", - "retention": "29 days", + "description": "OCD_tu_description", + "retention": "OCD_retention_29_days", "dataController": "adscale.de", "gdprUrl": "", "wildcard": "0" @@ -5940,8 +6402,8 @@ "category": "Marketing", "name": "betweendigital.com", "domain": "ut", - "description": "Collects data on visitors' behaviour and interaction - This is used to optimize the website and make advertisement on the website more relevant.", - "retention": "10 years", + "description": "OCD_betweendigital_com_description", + "retention": "OCD_retention_10_years", "dataController": "betweendigital.com", "gdprUrl": "", "wildcard": "0" @@ -5953,8 +6415,8 @@ "category": "Functional", "name": "ss", "domain": "betweendigital.com", - "description": "Necessary for the functionality of the website's chat-box function.", - "retention": "10 years", + "description": "OCD_ss_description", + "retention": "OCD_retention_10_years", "dataController": "betweendigital.com", "gdprUrl": "", "wildcard": "0" @@ -5966,8 +6428,8 @@ "category": "Marketing", "name": "st_csd", "domain": "seedtag.com", - "description": "Date of the last cookie-syn", - "retention": "1 year", + "description": "OCD_st_csd_description", + "retention": "OCD_retention_1_year", "dataController": "seedtag.com", "gdprUrl": "https://www.seedtag.com/privacy/", "wildcard": "0" @@ -5979,8 +6441,8 @@ "category": "Marketing", "name": "st_cs", "domain": "seedtag.com", - "description": "Unique identifiers of DSPs", - "retention": "1 year", + "description": "OCD_st_cs_description", + "retention": "OCD_retention_1_year", "dataController": "seedtag.com", "gdprUrl": "https://www.seedtag.com/privacy/", "wildcard": "0" @@ -5992,8 +6454,8 @@ "category": "Marketing", "name": "st_uid", "domain": "seedtag.com", - "description": "This cookie is used to store randomly generated unique browser identifier", - "retention": "1 year", + "description": "OCD_st_uid_description", + "retention": "OCD_retention_1_year", "dataController": "seedtag.com", "gdprUrl": "https://www.seedtag.com/privacy/", "wildcard": "0" @@ -6005,8 +6467,8 @@ "category": "Marketing", "name": "st_cnt", "domain": "seedtag.com", - "description": "This cookie is used to store low precision geolocation (Country, City)", - "retention": "1 year", + "description": "OCD_st_cnt_description", + "retention": "OCD_retention_1_year", "dataController": "seedtag.com", "gdprUrl": "https://www.seedtag.com/privacy/", "wildcard": "0" @@ -6018,8 +6480,8 @@ "category": "Marketing", "name": "st_chc", "domain": "seedtag.com", - "description": "This cookie is used to store Cookie-sync", - "retention": "1 year", + "description": "OCD_st_chc_description", + "retention": "OCD_retention_1_year", "dataController": "seedtag.com", "gdprUrl": "https://www.seedtag.com/privacy/", "wildcard": "0" @@ -6031,8 +6493,8 @@ "category": "Marketing", "name": "st_ssp", "domain": "seedtag.com", - "description": "This cookie is used to store low precision geolocation", - "retention": "1 year", + "description": "OCD_st_ssp_description", + "retention": "OCD_retention_1_year", "dataController": "seedtag.com", "gdprUrl": "https://www.seedtag.com/privacy/", "wildcard": "0" @@ -6044,8 +6506,8 @@ "category": "Marketing", "name": "cnfq", "domain": "smartadserver.com", - "description": "Technical cookie used to trigger the injection of monitoring scripts from a CNAME", - "retention": "360 minutes", + "description": "OCD_cnfq_description", + "retention": "OCD_retention_360_minutes", "dataController": "Equativ.com", "gdprUrl": "https://equativ.com/end-users-privacy-policy/", "wildcard": "0" @@ -6057,8 +6519,8 @@ "category": "Marketing", "name": "lcsrc", "domain": "smartadserver.com", - "description": "Technical cookie used to refresh date serialized in ISO format", - "retention": "13 months", + "description": "OCD_lcsrc_description", + "retention": "OCD_retention_13_months", "dataController": "Equativ.com", "gdprUrl": "https://equativ.com/end-users-privacy-policy/", "wildcard": "0" @@ -6070,8 +6532,8 @@ "category": "Functional", "name": "dyncdn", "domain": "smartadserver.com", - "description": "End-point and traffic data", - "retention": "1 day", + "description": "OCD_dyncdn_description", + "retention": "OCD_retention_1_day", "dataController": "Equativ.com", "gdprUrl": "https://equativ.com/end-users-privacy-policy/", "wildcard": "0" @@ -6083,8 +6545,8 @@ "category": "Marketing", "name": "gid", "domain": "smartadserver.com", - "description": "Global unique ID cross domains associated with an end-user", - "retention": "13 months", + "description": "OCD_gid_description", + "retention": "OCD_retention_13_months", "dataController": "Equativ.com", "gdprUrl": "https://equativ.com/end-users-privacy-policy/", "wildcard": "0" @@ -6096,8 +6558,8 @@ "category": "Marketing", "name": "csfq", "domain": "smartadserver.com", - "description": "Technical cookie used to trigger the injection of monitoring scripts", - "retention": "6 hours", + "description": "OCD_csfq_description", + "retention": "OCD_retention_6_hours", "dataController": "Equativ.com", "gdprUrl": "https://equativ.com/end-users-privacy-policy/", "wildcard": "0" @@ -6109,8 +6571,8 @@ "category": "Marketing", "name": "partner-*", "domain": "smartadserver.com", - "description": "Labeling end-users with keywords defined by a client.", - "retention": "13 months", + "description": "OCD_partner___description", + "retention": "OCD_retention_13_months", "dataController": "Equativ.com", "gdprUrl": "https://equativ.com/end-users-privacy-policy/", "wildcard": "1" @@ -6122,8 +6584,8 @@ "category": "Analytics", "name": "vs", "domain": "smartadserver.com", - "description": "Counting new visits", - "retention": "session", + "description": "OCD_vs_description", + "retention": "OCD_retention_session", "dataController": "Equativ.com", "gdprUrl": "https://equativ.com/end-users-privacy-policy/", "wildcard": "0" @@ -6135,8 +6597,8 @@ "category": "Marketing", "name": "Comp", "domain": "smartadserver.com", - "description": "Labeling end-users with keywords defined by a client.", - "retention": "13 months", + "description": "OCD_Comp_description", + "retention": "OCD_retention_13_months", "dataController": "Equativ.com", "gdprUrl": "https://equativ.com/end-users-privacy-policy/", "wildcard": "0" @@ -6148,8 +6610,8 @@ "category": "Marketing", "name": "Pwb", "domain": "smartadserver.com", - "description": "Allows for the display of ads in the correct format based on browser, screen size, and OS.", - "retention": "2 days", + "description": "OCD_Pwb_description", + "retention": "OCD_retention_2_days", "dataController": "Equativ.com", "gdprUrl": "https://equativ.com/end-users-privacy-policy/", "wildcard": "0" @@ -6161,8 +6623,8 @@ "category": "Functional", "name": "Pdomid", "domain": "smartadserver.com", - "description": "Technical cookie used to distribute the traffic between Smart's servers", - "retention": "13 months", + "description": "OCD_Pdomid_description", + "retention": "OCD_retention_13_months", "dataController": "Equativ.com", "gdprUrl": "https://equativ.com/end-users-privacy-policy/", "wildcard": "0" @@ -6174,8 +6636,8 @@ "category": "Marketing", "name": "sasd", "domain": "smartadserver.com", - "description": "Geolocation collection", - "retention": "1 day", + "description": "OCD_sasd_description", + "retention": "OCD_retention_1_day", "dataController": "Equativ.com", "gdprUrl": "https://equativ.com/end-users-privacy-policy/", "wildcard": "0" @@ -6187,8 +6649,8 @@ "category": "Marketing", "name": "sasd2", "domain": "smartadserver.com", - "description": "Geolocation collection", - "retention": "1 day", + "description": "OCD_sasd2_description", + "retention": "OCD_retention_1_day", "dataController": "Equativ.com", "gdprUrl": "https://equativ.com/end-users-privacy-policy/", "wildcard": "0" @@ -6200,8 +6662,8 @@ "category": "Marketing", "name": "TestIfCookie", "domain": "", - "description": "Technical cookie used to test if persistent cookies are accepted", - "retention": "session cookie", + "description": "OCD_TestIfCookie_description", + "retention": "OCD_retention_session_cookie", "dataController": "Equativ.com", "gdprUrl": "https://equativ.com/end-users-privacy-policy/", "wildcard": "0" @@ -6213,8 +6675,8 @@ "category": "Marketing", "name": "csync", "domain": "smartadserver.com", - "description": "Optimises ad display based on the user's movement combined and various advertiser bids for displaying user ads.", - "retention": "1 day", + "description": "OCD_csync_description", + "retention": "OCD_retention_1_day", "dataController": "Equativ.com", "gdprUrl": "https://equativ.com/privacy-policy/", "wildcard": "0" @@ -6226,8 +6688,8 @@ "category": "Marketing", "name": "TestIfCookieP", "domain": "smartadserver.com", - "description": "Technical cookie used to test if persistent cookies are accepted", - "retention": "13 months", + "description": "OCD_TestIfCookieP_description", + "retention": "OCD_retention_13_months", "dataController": "Equativ.com", "gdprUrl": "https://equativ.com/end-users-privacy-policy/", "wildcard": "0" @@ -6239,8 +6701,8 @@ "category": "Marketing", "name": "pid", "domain": ".smartadserver.com", - "description": "Unique ID associated with an end-user (according to a domain and browser)", - "retention": "13 months", + "description": "OCD_pid_description", + "retention": "OCD_retention_13_months", "dataController": "Equativ.com", "gdprUrl": "https://equativ.com/end-users-privacy-policy/", "wildcard": "0" @@ -6263,8 +6725,8 @@ "category": "Analytics", "name": "pbw", "domain": ".smartadserver.com", - "description": "This cookie collects cached data by browser ID, operating system ID and screen size", - "retention": "13 months", + "description": "OCD_pbw_description", + "retention": "OCD_retention_13_months", "dataController": "Equativ.com", "gdprUrl": "https://equativ.com/end-users-privacy-policy/", "wildcard": "0" @@ -6276,8 +6738,8 @@ "category": "Marketing", "name": "lcsrd", "domain": ".smartadserver.com", - "description": "This cookie is used to present the visitor with relevant content and advertisements", - "retention": "13 months", + "description": "OCD_lcsrd_description", + "retention": "OCD_retention_13_months", "dataController": "Equativ.com", "gdprUrl": "https://equativ.com/end-users-privacy-policy/", "wildcard": "0" @@ -6289,8 +6751,8 @@ "category": "Functional", "name": ".AspNetCore.Antiforgery.*", "domain": "", - "description": "Anti-forgery cookie is a security mechanism to defend against cross-site request forgery (CSRF) attacks.", - "retention": "Session", + "description": "OCD__AspNetCore_Antiforgery___description", + "retention": "OCD_retention_Session", "dataController": "Microsoft", "gdprUrl": "", "wildcard": "1" @@ -6302,8 +6764,8 @@ "category": "Marketing", "name": "unruly_m*", "domain": "", - "description": "Pending", - "retention": "6 days", + "description": "OCD_unruly_m__description", + "retention": "OCD_retention_6_days", "dataController": "Unrulymedia.com", "gdprUrl": "", "wildcard": "1" @@ -6315,8 +6777,8 @@ "category": "Marketing", "name": "bdswch", "domain": "outbrain.com", - "description": "Used to present the visitor with relevant content and advertisement - The service is provided by third party advertisement hubs, which facilitate real-time bidding for advertisers.", - "retention": "3 months", + "description": "OCD_bdswch_description", + "retention": "OCD_retention_3_months", "dataController": "Outbrain", "gdprUrl": "https://www.outbrain.com/legal/privacy#privacy-policy", "wildcard": "0" @@ -6328,8 +6790,8 @@ "category": "Marketing", "name": "lidid", "domain": "liadm.com", - "description": "Collects data on visitors' behaviour and interaction - This is used to make advertisement on the website more relevant. The cookie also allows the website to detect any referrals from other websites.", - "retention": "2 years", + "description": "OCD_lidid_description", + "retention": "OCD_retention_2_years", "dataController": "LiveIntent", "gdprUrl": "", "wildcard": "0" @@ -6341,8 +6803,8 @@ "category": "Marketing", "name": "_li_ss", "domain": "liadm.com", - "description": "Sets a unique ID for the visitor, that allows third party advertisers to target the visitor with relevant advertisement. This pairing service is provided by third party advertisement hubs, which facilitates real-time bidding for advertisers.", - "retention": "1 month", + "description": "OCD__li_ss_description", + "retention": "OCD_retention_1_month", "dataController": "LiveIntent", "gdprUrl": "", "wildcard": "0" @@ -6354,8 +6816,8 @@ "category": "Marketing", "name": "tluid", "domain": "3lift.com", - "description": "This cookie is used to identify the visitor and optimize ad-relevance by collecting visitor data from multiple websites – this exchange of visitor data is normally provided by a third-party data-center or ad-exchange.", - "retention": "3 months", + "description": "OCD_tluid_description", + "retention": "OCD_retention_3_months", "dataController": "TripleLift", "gdprUrl": "https://triplelift.com/advertising-technology-platform-cookie-notice/", "wildcard": "0" @@ -6367,8 +6829,8 @@ "category": "Marketing", "name": "tluidp", "domain": "3lift.com", - "description": "This cookie is used to identify the visitor and optimize ad-relevance by collecting visitor data from multiple websites with – this exchange of visitor data is normally provided by a third-party data-center or ad-exchange.", - "retention": "3 months", + "description": "OCD_tluidp_description", + "retention": "OCD_retention_3_months", "dataController": "TripleLift", "gdprUrl": "https://triplelift.com/advertising-technology-platform-cookie-notice/", "wildcard": "0" @@ -6380,11 +6842,22 @@ "category": "Marketing", "name": "optout", "domain": "3lift.com", - "description": "This cookie is used to determine whether the visitor has accepted the cookie consent box.", - "retention": "5 years", + "description": "OCD_optout_description", + "retention": "OCD_retention_5_years", "dataController": "TripleLift", "gdprUrl": "https://triplelift.com/advertising-technology-platform-cookie-notice/", "wildcard": "0" + }, + { + "platform": "Yahoo", + "category": "Marketing", + "name": "optout", + "domain": ".yahoo.com", + "description": "This cookie collect and store data of ads from user opted out", + "retention": "2 years", + "dataController": "Yahoo", + "gdprUrl": "https://legal.yahoo.com/ie/en/yahoo/privacy/cookies/index.html", + "wildcard": "0" } ], "sync": [ @@ -6393,8 +6866,8 @@ "category": "Marketing", "name": "sync", "domain": "3lift.com", - "description": "This cookie is used in order to transact in digital advertising, TripleLift exchanges (or syncs) identifiers with other companies. This cookie keeps track of which companies have recently been synced in order to avoid syncing with the same companies repetitively.", - "retention": "3 months", + "description": "OCD_sync_description", + "retention": "OCD_retention_3_months", "dataController": "TripleLift", "gdprUrl": "https://triplelift.com/advertising-technology-platform-cookie-notice/", "wildcard": "0" @@ -6406,8 +6879,8 @@ "category": "Marketing", "name": "t_gid", "domain": "taboola.com", - "description": "This Partitioned cookie gives a user who interacts with Taboola Widget a User ID allowing us to target advertisements and content to this specific user ID.", - "retention": "13 months", + "description": "OCD_t_gid_description", + "retention": "OCD_retention_13_months", "dataController": "taboola.com", "gdprUrl": "", "wildcard": "0" @@ -6419,8 +6892,8 @@ "category": "Functional", "name": "t_pt_gid", "domain": ".taboola.com", - "description": "Assigns a unique User ID that Taboola uses for attribution and reporting purposes, and to tailor recommendations to this specific user.", - "retention": "1 Year", + "description": "OCD_t_pt_gid_description", + "retention": "OCD_retention_1_Year", "dataController": "taboola.com", "gdprUrl": "https://www.taboola.com/policies/cookie-policy", "wildcard": "0" @@ -6432,8 +6905,123 @@ "category": "Marketing", "name": "taboola_session_id", "domain": ".taboola.com", - "description": "Creates a temporary session ID to avoid the display of duplicate recommendations on the page.", - "retention": "Session", + "description": "OCD_taboola_session_id_description", + "retention": "OCD_retention_Session", + "dataController": "taboola.com", + "gdprUrl": "https://www.taboola.com/policies/cookie-policy", + "wildcard": "0" + } + ], + "taboola_select": [ + { + "platform": "Taboola", + "category": "Functional", + "name": "taboola_select", + "domain": ".taboola.com", + "description": "OCD_taboola_select_description", + "retention": "OCD_retention_1_year", + "dataController": "taboola.com", + "gdprUrl": "https://www.taboola.com/policies/cookie-policy", + "wildcard": "0" + } + ], + "taboola_fp_td_user_id": [ + { + "platform": "Taboola", + "category": "Functional", + "name": "taboola_fp_td_user_id", + "domain": ".taboola.com", + "description": "OCD_taboola_fp_td_user_id_description", + "retention": "OCD_retention_1_year", + "dataController": "taboola.com", + "gdprUrl": "https://www.taboola.com/policies/cookie-policy", + "wildcard": "0" + } + ], + "trc_cookie_storage": [ + { + "platform": "Taboola", + "category": "Functional", + "name": "trc_cookie_storage", + "domain": ".taboola.com", + "description": "OCD_trc_cookie_storage_description", + "retention": "OCD_retention_1_year", + "dataController": "taboola.com", + "gdprUrl": "https://www.taboola.com/policies/cookie-policy", + "wildcard": "0" + }, + { + "platform": "Yahoo", + "category": "Marketing", + "name": "trc_cookie_storage", + "domain": ".yahoo.com", + "description": "This cookie is used to collect information on a visitor. This information will become an ID string with information on a specific visitor – ID information strings can be used to target groups with similar preferences, or can be used by third-party domains or ad-exchanges.", + "retention": "1 year", + "dataController": "Yahoo", + "gdprUrl": "https://legal.yahoo.com/ie/en/yahoo/privacy/cookies/index.html", + "wildcard": "0" + } + ], + "_tb_sess_r": [ + { + "platform": "Taboola", + "category": "Functional", + "name": "_tb_sess_r", + "domain": ".taboola.com", + "description": "OCD__tb_sess_r_description", + "retention": "OCD_retention_30_minutes", + "dataController": "taboola.com", + "gdprUrl": "https://www.taboola.com/policies/cookie-policy", + "wildcard": "0" + } + ], + "_tb_t_ppg": [ + { + "platform": "Taboola", + "category": "Marketing", + "name": "_tb_t_ppg", + "domain": ".taboola.com", + "description": "OCD__tb_t_ppg_description", + "retention": "OCD_retention_30_minutes", + "dataController": "taboola.com", + "gdprUrl": "https://www.taboola.com/policies/cookie-policy", + "wildcard": "0" + } + ], + "abLdr": [ + { + "platform": "Taboola", + "category": "Analytics", + "name": "abLdr", + "domain": ".taboola.com", + "description": "OCD_abLdr_description", + "retention": "OCD_retention_3_hours", + "dataController": "taboola.com", + "gdprUrl": "https://www.taboola.com/policies/cookie-policy", + "wildcard": "0" + } + ], + "abMbl": [ + { + "platform": "Taboola", + "category": "Analytics", + "name": "abMbl", + "domain": ".taboola.com", + "description": "OCD_abMbl_description", + "retention": "OCD_retention_3_hours", + "dataController": "taboola.com", + "gdprUrl": "https://www.taboola.com/policies/cookie-policy", + "wildcard": "0" + } + ], + "tb_click_param": [ + { + "platform": "Taboola", + "category": "Analytics", + "name": "tb_click_param", + "domain": ".taboola.com", + "description": "OCD_tb_click_param_description", + "retention": "OCD_retention_50_seconds", "dataController": "taboola.com", "gdprUrl": "https://www.taboola.com/policies/cookie-policy", "wildcard": "0" @@ -6445,11 +7033,22 @@ "category": "Marketing", "name": "__zlcmid", "domain": "", - "description": "Live chat widget on Slack contact page (ZopIM)", - "retention": "1 year", + "description": "OCD___zlcmid_description", + "retention": "OCD_retention_1_year", "dataController": "Zendesk", "gdprUrl": "https://www.zendesk.com/company/customers-partners/#cookie-policy", "wildcard": "0" + }, + { + "platform": "Zendesk", + "category": "Functional", + "name": "__zlcmid", + "domain": ".zendesk.com", + "description": "This cookie Chat Widget offers out-of-the-box cookie consent management, see here: Enabling cookie consent for the Chat widget & Web Widget. Alternatively, these Chat Cookies respect external cookie bot functionality as well.", + "retention": "1 year", + "dataController": "Zendesk", + "gdprUrl": "https://www.zendesk.com/company/agreements-and-terms/privacy-notice/", + "wildcard": "0" } ], "i": [ @@ -6458,8 +7057,8 @@ "category": "Marketing", "name": "i", "domain": "", - "description": "Registers user data, such as IP address, geographical location, websites visited and on which advertisements the user has clicked, with the aim of optimizing the display of advertisements based on user relocation on websites that use the same advertising network.", - "retention": "1 year", + "description": "OCD_i_description", + "retention": "OCD_retention_1_year", "dataController": "OpenX", "gdprUrl": "https://www.openx.com/privacy-center/privacy-policy/", "wildcard": "0" @@ -6471,8 +7070,8 @@ "category": "Marketing", "name": "univ_id", "domain": "openx.net", - "description": "This cookie collects information about the visitor for the purpose of serving advertisements.", - "retention": "3 days", + "description": "OCD_univ_id_description", + "retention": "OCD_retention_3_days", "dataController": "OpenX", "gdprUrl": "https://www.openx.com/privacy-center/privacy-policy/", "wildcard": "0" @@ -6484,8 +7083,8 @@ "category": "Marketing", "name": "pd", "domain": "openx.net", - "description": "This cookie stores information about which other third parties the user cookie (‘i’ cookie) has been synced with to reduce the amount of user matching done on your device.", - "retention": "15 days", + "description": "OCD_pd_description", + "retention": "OCD_retention_15_days", "dataController": "OpenX", "gdprUrl": "https://www.openx.com/privacy-center/privacy-policy/", "wildcard": "0" @@ -6497,8 +7096,8 @@ "category": "Marketing", "name": "OAID", "domain": "", - "description": "This cookie is used by the ad server software to manage which ads are placed on our website, and to capture clicks on those ads. Information is collected in anonymous form, and we do not use this data to deliver specific content, advertising or otherwise, to your browser.", - "retention": "1 year", + "description": "OCD_OAID_description", + "retention": "OCD_retention_1_year", "dataController": "OpenX", "gdprUrl": "https://www.openx.com/privacy-center/privacy-policy/", "wildcard": "0" @@ -6510,8 +7109,8 @@ "category": "Marketing", "name": "OAGEO*", "domain": "", - "description": "Used to avoid the repeated display of the same ad. Contains information about the users location.", - "retention": "Session", + "description": "OCD_OAGEO__description", + "retention": "OCD_retention_Session", "dataController": "OpenX", "gdprUrl": "https://www.openx.com/privacy-center/privacy-policy/", "wildcard": "1" @@ -6523,8 +7122,8 @@ "category": "Functional", "name": "__atuvc", "domain": ".addthis.com", - "description": "This cookie is associated with the AddThis social sharing widget, it stores an updated page share count.", - "retention": "2 years", + "description": "OCD___atuvc_description", + "retention": "OCD_retention_2_years", "dataController": "AddThis", "gdprUrl": "https://www.addthis.com/privacy", "wildcard": "0" @@ -6536,8 +7135,8 @@ "category": "Functional", "name": "__atuvs", "domain": ".addthis.com", - "description": "This cookie is associated with the AddThis social sharing widget, which serves a similar purpose to other cookies set by the service.", - "retention": "2 years", + "description": "OCD___atuvs_description", + "retention": "OCD_retention_2_years", "dataController": "AddThis", "gdprUrl": "https://www.addthis.com/privacy", "wildcard": "0" @@ -6549,8 +7148,8 @@ "category": "Functional", "name": "ssc", "domain": ".addthis.com", - "description": "AddThis - Cookie related to an AddThis sharing button available on the website", - "retention": "2 years", + "description": "OCD_ssc_description", + "retention": "OCD_retention_2_years", "dataController": "AddThis", "gdprUrl": "https://www.addthis.com/privacy", "wildcard": "0" @@ -6562,8 +7161,8 @@ "category": "Functional", "name": "uvc", "domain": ".addthis.com", - "description": "AddThis - Cookie related to an AddThis sharing button available on the website", - "retention": "2 years", + "description": "OCD_uvc_description", + "retention": "OCD_retention_2_years", "dataController": "AddThis", "gdprUrl": "https://www.addthis.com/privacy", "wildcard": "0" @@ -6575,8 +7174,8 @@ "category": "Functional", "name": "loc", "domain": ".addthis.com", - "description": "AddThis - Cookie related to an AddThis sharing button available on the website", - "retention": "2 years", + "description": "OCD_loc_description", + "retention": "OCD_retention_2_years", "dataController": "AddThis", "gdprUrl": "https://www.addthis.com/privacy", "wildcard": "0" @@ -6588,8 +7187,8 @@ "category": "Functional", "name": "na_id", "domain": ".addthis.com", - "description": "AddThis - Cookie related to an AddThis sharing button available on the website", - "retention": "2 years", + "description": "OCD_na_id_description", + "retention": "OCD_retention_2_years", "dataController": "AddThis", "gdprUrl": "https://www.addthis.com/privacy", "wildcard": "0" @@ -6601,8 +7200,8 @@ "category": "Functional", "name": "na_tc", "domain": ".addthis.com", - "description": "AddThis - Cookie related to an AddThis sharing button available on the website", - "retention": "2 years", + "description": "OCD_na_tc_description", + "retention": "OCD_retention_2_years", "dataController": "AddThis", "gdprUrl": "https://www.addthis.com/privacy", "wildcard": "0" @@ -6614,8 +7213,8 @@ "category": "Functional", "name": "ouid", "domain": ".addthis.com", - "description": "AddThis - Cookie related to an AddThis sharing button available on the website", - "retention": "2 years", + "description": "OCD_ouid_description", + "retention": "OCD_retention_2_years", "dataController": "AddThis", "gdprUrl": "https://www.addthis.com/privacy", "wildcard": "0" @@ -6627,8 +7226,8 @@ "category": "Functional", "name": "na_sc_x", "domain": ".dlx.addthis.com", - "description": "Used by the social sharing platform AddThis to keep a record of parts of the site that has been visited in order to recommend other parts of the site.", - "retention": "1 month", + "description": "OCD_na_sc_x_description", + "retention": "OCD_retention_1_month", "dataController": "AddThis", "gdprUrl": "https://www.addthis.com/privacy", "wildcard": "0" @@ -6640,8 +7239,8 @@ "category": "Marketing", "name": "DG_HID", "domain": "funda.nl", - "description": "Pending", - "retention": "30 days", + "description": "OCD_DG_HID_description", + "retention": "OCD_retention_30_days", "dataController": "Funda", "gdprUrl": "https://www.funda.nl/privacybeleid/consument/", "wildcard": "0" @@ -6653,8 +7252,8 @@ "category": "Marketing", "name": "DG_IID", "domain": "funda.nl", - "description": "Pending", - "retention": "30 days", + "description": "OCD_DG_IID_description", + "retention": "OCD_retention_30_days", "dataController": "Funda", "gdprUrl": "https://www.funda.nl/privacybeleid/consument/", "wildcard": "0" @@ -6666,8 +7265,8 @@ "category": "Marketing", "name": "DG_SID", "domain": "funda.nl", - "description": "Pending", - "retention": "30 days", + "description": "OCD_DG_SID_description", + "retention": "OCD_retention_30_days", "dataController": "Funda", "gdprUrl": "https://www.funda.nl/privacybeleid/consument/", "wildcard": "0" @@ -6679,8 +7278,8 @@ "category": "Marketing", "name": "DG_UID", "domain": "funda.nl", - "description": "Pending", - "retention": "30 days", + "description": "OCD_DG_UID_description", + "retention": "OCD_retention_30_days", "dataController": "Funda", "gdprUrl": "https://www.funda.nl/privacybeleid/consument/", "wildcard": "0" @@ -6692,8 +7291,8 @@ "category": "Marketing", "name": "DG_ZID", "domain": "funda.nl", - "description": "Pending", - "retention": "30 days", + "description": "OCD_DG_ZID_description", + "retention": "OCD_retention_30_days", "dataController": "Funda", "gdprUrl": "https://www.funda.nl/privacybeleid/consument/", "wildcard": "0" @@ -6705,8 +7304,8 @@ "category": "Marketing", "name": "DG_ZUID", "domain": "funda.nl", - "description": "Pending", - "retention": "30 days", + "description": "OCD_DG_ZUID_description", + "retention": "OCD_retention_30_days", "dataController": "Funda", "gdprUrl": "https://www.funda.nl/privacybeleid/consument/", "wildcard": "0" @@ -6718,8 +7317,8 @@ "category": "Functional", "name": "fonts-loaded", "domain": "funda.nl", - "description": "This cookie checks and remembers whether you have the font used by funda. Remembering this check makes visiting the website faster.", - "retention": "30 days", + "description": "OCD_fonts_loaded_description", + "retention": "OCD_retention_30_days", "dataController": "Funda", "gdprUrl": "https://www.funda.nl/privacybeleid/consument/", "wildcard": "0" @@ -6731,8 +7330,8 @@ "category": "Functional", "name": "html-classes", "domain": "funda.nl", - "description": "Remembering how the website is displayed to adjust the appearance of the site to the environment and browser used by the user. This ensures that the site loads faster on a subsequent visit.", - "retention": "30 days", + "description": "OCD_html_classes_description", + "retention": "OCD_retention_30_days", "dataController": "Funda", "gdprUrl": "https://www.funda.nl/privacybeleid/consument/", "wildcard": "0" @@ -6744,8 +7343,8 @@ "category": "Functional", "name": "SNLB2", "domain": "funda.nl", - "description": "Pending", - "retention": "30 days", + "description": "OCD_SNLB2_description", + "retention": "OCD_retention_30_days", "dataController": "Funda", "gdprUrl": "https://www.funda.nl/privacybeleid/consument/", "wildcard": "0" @@ -6757,8 +7356,8 @@ "category": "Functional", "name": "lz_last_visit", "domain": "", - "description": "Last Visit (Timestamp), used to determine when the website visitor browsed the website the last time.", - "retention": "1 Year", + "description": "OCD_lz_last_visit_description", + "retention": "OCD_retention_1_Year", "dataController": "LiveZilla GmbH", "gdprUrl": "", "wildcard": "0" @@ -6770,8 +7369,8 @@ "category": "Functional", "name": "lz_userid", "domain": "", - "description": "Sets up a unique ID which is used to generate statistical data about the website visitor's usage of the website.", - "retention": "1 Year", + "description": "OCD_lz_userid_description", + "retention": "OCD_retention_1_Year", "dataController": "LiveZilla GmbH", "gdprUrl": "", "wildcard": "0" @@ -6783,8 +7382,8 @@ "category": "Functional", "name": "lz_visits", "domain": "", - "description": "Number of visits, is used to identify how often the website visitor already visited the website.", - "retention": "1 Year", + "description": "OCD_lz_visits_description", + "retention": "OCD_retention_1_Year", "dataController": "LiveZilla GmbH", "gdprUrl": "", "wildcard": "0" @@ -6796,8 +7395,8 @@ "category": "Functional", "name": ".secureclient", "domain": "", - "description": "Pending", - "retention": "Session", + "description": "OCD__secureclient_description", + "retention": "OCD_retention_Session", "dataController": "AFAS", "gdprUrl": "", "wildcard": "0" @@ -6809,8 +7408,8 @@ "category": "Functional", "name": ".securesession", "domain": "", - "description": "Pending", - "retention": "Session", + "description": "OCD__securesession_description", + "retention": "OCD_retention_Session", "dataController": "AFAS", "gdprUrl": "", "wildcard": "0" @@ -6822,8 +7421,8 @@ "category": "Functional", "name": ".stateflags", "domain": "", - "description": "Pending", - "retention": "Session", + "description": "OCD__stateflags_description", + "retention": "OCD_retention_Session", "dataController": "AFAS", "gdprUrl": "", "wildcard": "0" @@ -6835,8 +7434,8 @@ "category": "Functional", "name": ".auth", "domain": "", - "description": "Pending", - "retention": "Session", + "description": "OCD__auth_description", + "retention": "OCD_retention_Session", "dataController": "AFAS", "gdprUrl": "", "wildcard": "0" @@ -6848,8 +7447,8 @@ "category": "Marketing", "name": "advst_uid_11", "domain": ".adxcore.com", - "description": "Pending", - "retention": "6 months", + "description": "OCD_advst_uid_11_description", + "retention": "OCD_retention_6_months", "dataController": "Adxcore", "gdprUrl": "", "wildcard": "0" @@ -6861,8 +7460,8 @@ "category": "Marketing", "name": "DISPATCHER", "domain": "dispatcher.adxcore.com", - "description": "Pending", - "retention": "6 months", + "description": "OCD_DISPATCHER_description", + "retention": "OCD_retention_6_months", "dataController": "Adxcore", "gdprUrl": "", "wildcard": "0" @@ -6874,8 +7473,8 @@ "category": "Marketing", "name": "DSP_UID", "domain": "fidelity-media.com", - "description": "Pending", - "retention": "9 days", + "description": "OCD_DSP_UID_description", + "retention": "OCD_retention_9_days", "dataController": "Fidelity-media.com", "gdprUrl": "", "wildcard": "0" @@ -6887,8 +7486,8 @@ "category": "Analytics", "name": "picreel_tracker__visited", "domain": "", - "description": "Used for statistical purposes when counting the number of pages, the user visited", - "retention": "Unlimited", + "description": "OCD_picreel_tracker__visited_description", + "retention": "OCD_retention_Unlimited", "dataController": "Picreel", "gdprUrl": "", "wildcard": "0" @@ -6900,8 +7499,8 @@ "category": "Analytics", "name": "picreel_tracker__first_visit", "domain": "", - "description": "Used for statistical purposes, keeping the date of the first visit", - "retention": "Unlimited", + "description": "OCD_picreel_tracker__first_visit_description", + "retention": "OCD_retention_Unlimited", "dataController": "Picreel", "gdprUrl": "", "wildcard": "0" @@ -6913,8 +7512,8 @@ "category": "Analytics", "name": "picreel_tracker__page_views", "domain": "", - "description": "Pending", - "retention": "Unlimited", + "description": "OCD_picreel_tracker__page_views_description", + "retention": "OCD_retention_Unlimited", "dataController": "Picreel", "gdprUrl": "", "wildcard": "0" @@ -6926,8 +7525,8 @@ "category": "Analytics", "name": "picreel_new_price", "domain": "", - "description": "Pending", - "retention": "Unlimited", + "description": "OCD_picreel_new_price_description", + "retention": "OCD_retention_Unlimited", "dataController": "Picreel", "gdprUrl": "", "wildcard": "0" @@ -6939,8 +7538,8 @@ "category": "Analytics", "name": "__auc", "domain": ".trustpilot.com", - "description": "Used to track and report information to the Alexa analytics", - "retention": "1 year", + "description": "OCD___auc_description", + "retention": "OCD_retention_1_year", "dataController": "Trustpilot", "gdprUrl": "", "wildcard": "0" @@ -6952,8 +7551,8 @@ "category": "Analytics", "name": "ajs_user_id", "domain": ".trustpilot.com", - "description": " This cookie helps track visitor usage, events, target marketing, and can also measure application performance and stability.", - "retention": "1 year", + "description": "OCD_ajs_user_id_description", + "retention": "OCD_retention_1_year", "dataController": "Trustpilot", "gdprUrl": "", "wildcard": "0" @@ -6965,8 +7564,8 @@ "category": "Analytics", "name": "ajs_anonymous_id", "domain": ".trustpilot.com", - "description": "Used for Analytics and help count how many people visit a certain site by tracking if you have visited before", - "retention": "1 year", + "description": "OCD_ajs_anonymous_id_description", + "retention": "OCD_retention_1_year", "dataController": "Trustpilot", "gdprUrl": "", "wildcard": "0" @@ -6978,8 +7577,8 @@ "category": "Analytics", "name": "ajs_group_id", "domain": ".trustpilot.com", - "description": "Track visitor usage and events within the website", - "retention": "1 year", + "description": "OCD_ajs_group_id_description", + "retention": "OCD_retention_1_year", "dataController": "Trustpilot", "gdprUrl": "", "wildcard": "0" @@ -6991,8 +7590,8 @@ "category": "Analytics", "name": "__asc", "domain": ".trustpilot.com", - "description": "A cookie set by Trustpilot if you click the read more widget", - "retention": "1 year", + "description": "OCD___asc_description", + "retention": "OCD_retention_1_year", "dataController": "Trustpilot", "gdprUrl": "", "wildcard": "0" @@ -7004,8 +7603,8 @@ "category": "Analytics", "name": "__insp_norec_sess", "domain": "inspectlet.com", - "description": "Inspectlet uses cookies to keep track of session information. These cookies are needed to accurately understand how visitors are navigating the website.", - "retention": "1 year", + "description": "OCD___insp_norec_sess_description", + "retention": "OCD_retention_1_year", "dataController": "Inspectlet", "gdprUrl": "https://docs.inspectlet.com/hc/en-us", "wildcard": "0" @@ -7017,8 +7616,8 @@ "category": "Analytics", "name": "__insp_slim", "domain": "inspectlet.com", - "description": "Inspectlet uses cookies to keep track of session information. These cookies are needed to accurately understand how visitors are navigating the website.", - "retention": "1 year", + "description": "OCD___insp_slim_description", + "retention": "OCD_retention_1_year", "dataController": "Inspectlet", "gdprUrl": "https://docs.inspectlet.com/hc/en-us", "wildcard": "0" @@ -7030,8 +7629,8 @@ "category": "Analytics", "name": "__insp_targlpt", "domain": "inspectlet.com", - "description": "Inspectlet uses cookies to keep track of session information. These cookies are needed to accurately understand how visitors are navigating the website.", - "retention": "1 year", + "description": "OCD___insp_targlpt_description", + "retention": "OCD_retention_1_year", "dataController": "Inspectlet", "gdprUrl": "https://docs.inspectlet.com/hc/en-us", "wildcard": "0" @@ -7043,8 +7642,8 @@ "category": "Analytics", "name": "__insp_targlpu", "domain": "inspectlet.com", - "description": "Inspectlet uses cookies to keep track of session information. These cookies are needed to accurately understand how visitors are navigating the website.", - "retention": "1 year", + "description": "OCD___insp_targlpu_description", + "retention": "OCD_retention_1_year", "dataController": "Inspectlet", "gdprUrl": "https://docs.inspectlet.com/hc/en-us", "wildcard": "0" @@ -7056,8 +7655,8 @@ "category": "Functional", "name": "__RequestVerificationToken*", "domain": "", - "description": "This is an anti-forgery cookie set by web applications built using ASP.NET MVC technologies. It is designed to stop unauthorised posting of content to a website, known as Cross-Site Request Forgery.", - "retention": "session", + "description": "OCD___RequestVerificationToken__description", + "retention": "OCD_retention_session", "dataController": "Microsoft", "gdprUrl": "", "wildcard": "1" @@ -7069,8 +7668,8 @@ "category": "Marketing", "name": "__uin_mm", "domain": "sonobi.com", - "description": "These cookies are used to deliver adverts more relevant to you and your interests. They are also used to limit the number of times you see an advertisement as well as help measure the effectiveness of the advertising campaign.", - "retention": "44 days", + "description": "OCD___uin_mm_description", + "retention": "OCD_retention_44_days", "dataController": "Sonobi", "gdprUrl": "https://sonobi.com/privacy-policy/", "wildcard": "0" @@ -7082,8 +7681,8 @@ "category": "Marketing", "name": "__uir_mm", "domain": "sonobi.com", - "description": "These cookies are used to deliver adverts more relevant to you and your interests. They are also used to limit the number of times you see an advertisement as well as help measure the effectiveness of the advertising campaign.", - "retention": "14 days", + "description": "OCD___uir_mm_description", + "retention": "OCD_retention_14_days", "dataController": "Sonobi", "gdprUrl": "https://sonobi.com/privacy-policy/", "wildcard": "0" @@ -7095,8 +7694,8 @@ "category": "Marketing", "name": "_cc_aud", "domain": "crwdcntrl.net", - "description": "Collects anonymous statistical data related to the user's website visits, such as the number of visits, average time spent on the website and what pages have been loaded. The purpose is to segment the website's users according to factors such as demographics and geographical location, in order to enable media and marketing agencies to structure and understand their target groups to enable customised online advertising.", - "retention": "269 days", + "description": "OCD__cc_aud_description", + "retention": "OCD_retention_269_days", "dataController": "Lotame", "gdprUrl": "https://www.lotame.com/about-lotame/privacy/lotames-products-services-privacy-policy/", "wildcard": "0" @@ -7108,8 +7707,8 @@ "category": "Marketing", "name": "_cc_cc", "domain": "crwdcntrl.net", - "description": "Collects anonymous statistical data related to the user's website visits, such as the number of visits, average time spent on the website and what pages have been loaded. The purpose is to segment the website's users according to factors such as demographics and geographical location, in order to enable media and marketing agencies to structure and understand their target groups to enable customised online advertising.", - "retention": "session", + "description": "OCD__cc_cc_description", + "retention": "OCD_retention_session", "dataController": "Lotame", "gdprUrl": "https://www.lotame.com/about-lotame/privacy/lotames-products-services-privacy-policy/", "wildcard": "0" @@ -7121,8 +7720,8 @@ "category": "Marketing", "name": "_cc_id", "domain": "crwdcntrl.net", - "description": "Collects anonymous statistical data related to the user's website visits, such as the number of visits, average time spent on the website and what pages have been loaded. The purpose is to segment the website's users according to factors such as demographics and geographical location, in order to enable media and marketing agencies to structure and understand their target groups to enable customised online advertising.", - "retention": "269 days", + "description": "OCD__cc_id_description", + "retention": "OCD_retention_269_days", "dataController": "Lotame", "gdprUrl": "https://www.lotame.com/about-lotame/privacy/lotames-products-services-privacy-policy/", "wildcard": "0" @@ -7134,8 +7733,8 @@ "category": "Marketing", "name": "panoramaId", "domain": "", - "description": "Registers data on visitors from multiple visits and on multiple websites. This information is used to measure the efficiency of advertisement on websites.", - "retention": "session", + "description": "OCD_panoramaId_description", + "retention": "OCD_retention_session", "dataController": "Lotame", "gdprUrl": "https://www.lotame.com/about-lotame/privacy/lotames-products-services-privacy-policy/", "wildcard": "0" @@ -7147,8 +7746,8 @@ "category": "Marketing", "name": "panoramaId_expiry", "domain": "", - "description": "Registers data on visitors from multiple visits and on multiple websites. This information is used to measure the efficiency of advertisement on websites.", - "retention": "session", + "description": "OCD_panoramaId_expiry_description", + "retention": "OCD_retention_session", "dataController": "Lotame", "gdprUrl": "https://www.lotame.com/about-lotame/privacy/lotames-products-services-privacy-policy/", "wildcard": "0" @@ -7160,8 +7759,8 @@ "category": "Marketing", "name": "panoramaId_expiry_exp", "domain": "", - "description": "Contains the expiry-date for the cookie with corresponding name.", - "retention": "session", + "description": "OCD_panoramaId_expiry_exp_description", + "retention": "OCD_retention_session", "dataController": "Lotame", "gdprUrl": "https://www.lotame.com/about-lotame/privacy/lotames-products-services-privacy-policy/", "wildcard": "0" @@ -7173,8 +7772,8 @@ "category": "Marketing", "name": "_cc_domain", "domain": "", - "description": "Registers data on visitors from multiple visits and on multiple websites. This information is used to measure the efficiency of advertisement on websites.", - "retention": "session", + "description": "OCD__cc_domain_description", + "retention": "OCD_retention_session", "dataController": "Lotame", "gdprUrl": "https://www.lotame.com/about-lotame/privacy/lotames-products-services-privacy-policy/", "wildcard": "0" @@ -7186,8 +7785,8 @@ "category": "Marketing", "name": "ab", "domain": "agkn.com", - "description": "This cookie is used by the website’s operator in context with multi-variate testing. This is a tool used to combine or change content on the website. This allows the website to find the best variation/edition of the site.", - "retention": "1 year", + "description": "OCD_ab_description", + "retention": "OCD_retention_1_year", "dataController": "Neustar", "gdprUrl": "https://www.home.neustar/privacy", "wildcard": "0" @@ -7199,8 +7798,8 @@ "category": "Marketing", "name": "bkdc", "domain": "bluekai.com", - "description": "Registers anonymised user data, such as IP address, geographical location, visited websites, and what ads the user has clicked, with the purpose of optimising ad display based on the user's movement on websites that use the same ad network.", - "retention": "179 days", + "description": "OCD_bkdc_description", + "retention": "OCD_retention_179_days", "dataController": "Oracle", "gdprUrl": "https://www.oracle.com/legal/privacy/marketing-cloud-data-cloud-privacy-policy.html", "wildcard": "0" @@ -7212,8 +7811,8 @@ "category": "Marketing", "name": "bku", "domain": "bluekai.com", - "description": "Registers anonymised user data, such as IP address, geographical location, visited websites, and what ads the user has clicked, with the purpose of optimising ad display based on the user's movement on websites that use the same ad network.", - "retention": "179 days", + "description": "OCD_bku_description", + "retention": "OCD_retention_179_days", "dataController": "Oracle", "gdprUrl": "https://www.oracle.com/legal/privacy/marketing-cloud-data-cloud-privacy-policy.html", "wildcard": "0" @@ -7225,8 +7824,8 @@ "category": "Marketing", "name": "everest_g_v2", "domain": "everesttech.net", - "description": "This cookie stores the browser and surfer ID.Created after a user initially clicks a client's ad, and used to map the current and subsequent clicks with other events on the client's website", - "retention": "2 years", + "description": "OCD_everest_g_v2_description", + "retention": "OCD_retention_2_years", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -7238,8 +7837,8 @@ "category": "Marketing", "name": "everest_session_v2", "domain": "everesttech.net", - "description": "This cookie stores the session ID", - "retention": "session", + "description": "OCD_everest_session_v2_description", + "retention": "OCD_retention_session", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -7251,8 +7850,8 @@ "category": "Marketing", "name": "ev_tm", "domain": "everesttech.net", - "description": "This cookie stores the Adobe Advertising DSP (Demand Side Platform) ID. \tA third-party cookie that stores the DSP ID that corresponds to the surfer ID in the everest_g_v2 cookie", - "retention": "2 years", + "description": "OCD_ev_tm_description", + "retention": "OCD_retention_2_years", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -7264,8 +7863,8 @@ "category": "Marketing", "name": "_tmae", "domain": "everesttech.net", - "description": "This cookie stores Encoded IDs and time stamps for ad engagements using Adobe Advertising DSP tracking.A third-party cookie that stores user engagements with ads, such as 'last seen ad xyz123 on June 30, 2016'", - "retention": "1 year", + "description": "OCD__tmae_description", + "retention": "OCD_retention_1_year", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -7277,8 +7876,8 @@ "category": "Marketing", "name": "_lcc", "domain": "everesttech.net", - "description": "This cookie stores IDs and time stamps (in the format yyyymmdd) of display clicks. It is a third-party cookie used to determine if a click event on a display ad applies to an Adobe Analytics hit", - "retention": "15 minutes", + "description": "OCD__lcc_description", + "retention": "OCD_retention_15_minutes", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -7290,8 +7889,8 @@ "category": "Marketing", "name": "ev_sync_ax", "domain": "everesttech.net", - "description": "This cookie stores The date when synchronization is performed, in the format yyyymmdd. A third-party, ad exchange-specific cookie that syncs the Adobe Advertising surfer ID with the partner ad exchange. It's created for new surfers and sends a synchronization request when it's expired.", - "retention": "1 year", + "description": "OCD_ev_sync_ax_description", + "retention": "OCD_retention_1_year", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -7303,8 +7902,8 @@ "category": "Marketing", "name": "ev_sync_bk", "domain": "everesttech.net", - "description": "This cookie stores The date when synchronization is performed, in the format yyyymmdd. A third-party, ad exchange-specific cookie that syncs the Adobe Advertising surfer ID with the partner ad exchange. It's created for new surfers and sends a synchronization request when it's expired.", - "retention": "1 year", + "description": "OCD_ev_sync_bk_description", + "retention": "OCD_retention_1_year", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -7316,8 +7915,8 @@ "category": "Marketing", "name": "ev_sync_dd", "domain": "everesttech.net", - "description": "This cookie stores The date when synchronization is performed, in the format yyyymmdd. A third-party, ad exchange-specific cookie that syncs the Adobe Advertising surfer ID with the partner ad exchange. It's created for new surfers and sends a synchronization request when it's expired.", - "retention": "1 year", + "description": "OCD_ev_sync_dd_description", + "retention": "OCD_retention_1_year", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -7329,8 +7928,8 @@ "category": "Marketing", "name": "ev_sync_fs", "domain": "everesttech.net", - "description": "This cookie stores The date when synchronization is performed, in the format yyyymmdd. A third-party, ad exchange-specific cookie that syncs the Adobe Advertising surfer ID with the partner ad exchange. It's created for new surfers and sends a synchronization request when it's expired.", - "retention": "1 year", + "description": "OCD_ev_sync_fs_description", + "retention": "OCD_retention_1_year", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -7342,8 +7941,8 @@ "category": "Marketing", "name": "ev_sync_ix", "domain": "everesttech.net", - "description": "This cookie stores The date when synchronization is performed, in the format yyyymmdd. A third-party, ad exchange-specific cookie that syncs the Adobe Advertising surfer ID with the partner ad exchange. It's created for new surfers and sends a synchronization request when it's expired.", - "retention": "1 year", + "description": "OCD_ev_sync_ix_description", + "retention": "OCD_retention_1_year", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -7355,8 +7954,8 @@ "category": "Marketing", "name": "ev_sync_nx", "domain": "everesttech.net", - "description": "This cookie stores The date when synchronization is performed, in the format yyyymmdd. A third-party, ad exchange-specific cookie that syncs the Adobe Advertising surfer ID with the partner ad exchange. It's created for new surfers and sends a synchronization request when it's expired.", - "retention": "1 year", + "description": "OCD_ev_sync_nx_description", + "retention": "OCD_retention_1_year", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -7368,8 +7967,8 @@ "category": "Marketing", "name": "ev_sync_ox", "domain": "everesttech.net", - "description": "This cookie stores The date when synchronization is performed, in the format yyyymmdd. A third-party, ad exchange-specific cookie that syncs the Adobe Advertising surfer ID with the partner ad exchange. It's created for new surfers and sends a synchronization request when it's expired.", - "retention": "1 year", + "description": "OCD_ev_sync_ox_description", + "retention": "OCD_retention_1_year", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -7381,8 +7980,8 @@ "category": "Marketing", "name": "ev_sync_pm", "domain": "everesttech.net", - "description": "This cookie stores The date when synchronization is performed, in the format yyyymmdd. A third-party, ad exchange-specific cookie that syncs the Adobe Advertising surfer ID with the partner ad exchange. It's created for new surfers and sends a synchronization request when it's expired.", - "retention": "1 year", + "description": "OCD_ev_sync_pm_description", + "retention": "OCD_retention_1_year", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -7394,8 +7993,8 @@ "category": "Marketing", "name": "ev_sync_rc", "domain": "everesttech.net", - "description": "This cookie stores The date when synchronization is performed, in the format yyyymmdd. A third-party, ad exchange-specific cookie that syncs the Adobe Advertising surfer ID with the partner ad exchange. It's created for new surfers and sends a synchronization request when it's expired.", - "retention": "1 year", + "description": "OCD_ev_sync_rc_description", + "retention": "OCD_retention_1_year", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -7407,8 +8006,8 @@ "category": "Marketing", "name": "ev_sync_tm", "domain": "everesttech.net", - "description": "This cookie stores The date when synchronization is performed, in the format yyyymmdd. A third-party, ad exchange-specific cookie that syncs the Adobe Advertising surfer ID with the partner ad exchange. It's created for new surfers and sends a synchronization request when it's expired.", - "retention": "1 year", + "description": "OCD_ev_sync_tm_description", + "retention": "OCD_retention_1_year", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -7420,8 +8019,8 @@ "category": "Marketing", "name": "ev_sync_yh", "domain": "everesttech.net", - "description": "This cookie stores the date when synchronization is performed, in the format yyyymmdd. A third-party, ad exchange-specific cookie that syncs the Adobe Advertising surfer ID with the partner ad exchange. It's created for new surfers and sends a synchronization request when it's expired.", - "retention": "1 year", + "description": "OCD_ev_sync_yh_description", + "retention": "OCD_retention_1_year", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -7433,8 +8032,8 @@ "category": "Marketing", "name": "adcloud", "domain": "", - "description": "This cookie stores The timestamps of the surfer's last visit to the advertiser’s website and the surfer's last search click, and the ef_id that was created when the user clicked an ad", - "retention": "1 year", + "description": "OCD_adcloud_description", + "retention": "OCD_retention_1_year", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -7446,8 +8045,8 @@ "category": "Marketing", "name": "id_adcloud", "domain": "", - "description": "This cookie stores the surfer ID", - "retention": "91 days", + "description": "OCD_id_adcloud_description", + "retention": "OCD_retention_91_days", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -7459,8 +8058,8 @@ "category": "Marketing", "name": "mt_misc", "domain": "mathtag.com", - "description": "MediaMath uses this cookie to hold attributes about the browser for fraud prevention and other technical optimizations.", - "retention": "30 days", + "description": "OCD_mt_misc_description", + "retention": "OCD_retention_30_days", "dataController": "MediaMath", "gdprUrl": "https://www.mediamath.com/privacy-policy/", "wildcard": "0" @@ -7472,8 +8071,8 @@ "category": "Marketing", "name": "mt_mop", "domain": "mathtag.com", - "description": "MediaMath uses this cookie to synchronize the visitor ID with a limited number of trusted exchanges and data partners", - "retention": "30 days", + "description": "OCD_mt_mop_description", + "retention": "OCD_retention_30_days", "dataController": "MediaMath", "gdprUrl": "https://www.mediamath.com/privacy-policy/", "wildcard": "0" @@ -7485,8 +8084,8 @@ "category": "Marketing", "name": "pl_user_id", "domain": "powerlinks.com", - "description": "This cookie registers data on the visitor. The information is used to optimize advertisement relevance.", - "retention": "3 months", + "description": "OCD_pl_user_id_description", + "retention": "OCD_retention_3_months", "dataController": "PowerLinks", "gdprUrl": "https://www.powerlinks.com/privacy-policy/", "wildcard": "0" @@ -7498,8 +8097,8 @@ "category": "Marketing", "name": "pxrc", "domain": "rlcdn.com", - "description": "This cookie registers non-personal data on the visitor. The information is used to optimize advertisement relevance.", - "retention": "2 months", + "description": "OCD_pxrc_description", + "retention": "OCD_retention_2_months", "dataController": "Tower Data", "gdprUrl": "https://www.towerdata.com/privacy-policy", "wildcard": "0" @@ -7511,8 +8110,8 @@ "category": "Marketing", "name": "rlas3", "domain": "rlcdn.com", - "description": "Collects anonymous data related to the user's visits to the website, such as the number of visits, average time spent on the website and what pages have been loaded, with the purpose of displaying targeted ads.", - "retention": "1 year", + "description": "OCD_rlas3_description", + "retention": "OCD_retention_1_year", "dataController": "Tower Data", "gdprUrl": "https://www.towerdata.com/privacy-policy", "wildcard": "0" @@ -7524,8 +8123,8 @@ "category": "Marketing", "name": "TapAd_DID", "domain": "tapad.com", - "description": "Used to determine what type of devices (smartphones, tablets, computers, TVs etc.) is used by a user.", - "retention": "2 months", + "description": "OCD_TapAd_DID_description", + "retention": "OCD_retention_2_months", "dataController": "Tapad", "gdprUrl": "https://www.tapad.com/privacy", "wildcard": "0" @@ -7537,8 +8136,8 @@ "category": "Marketing", "name": "TapAd_TS", "domain": "tapad.com", - "description": "Used to determine what type of devices (smartphones, tablets, computers, TVs etc.) is used by a user.", - "retention": "2 months", + "description": "OCD_TapAd_TS_description", + "retention": "OCD_retention_2_months", "dataController": "Tapad", "gdprUrl": "https://www.tapad.com/privacy", "wildcard": "0" @@ -7550,8 +8149,8 @@ "category": "Marketing", "name": "TapAd_3WAY_SYNCS", "domain": "", - "description": "Used for data-synchronization with advertisement networks", - "retention": "2 months", + "description": "OCD_TapAd_3WAY_SYNCS_description", + "retention": "OCD_retention_2_months", "dataController": "Tapad", "gdprUrl": "https://www.tapad.com/privacy", "wildcard": "0" @@ -7563,8 +8162,8 @@ "category": "Marketing", "name": "TDCPM", "domain": "adsrvr.org", - "description": "Registers a unique ID that identifies a returning user's device. The ID is used for targeted ads.", - "retention": "1 year", + "description": "OCD_TDCPM_description", + "retention": "OCD_retention_1_year", "dataController": "The Tradedesk", "gdprUrl": "https://adsrvr.org/", "wildcard": "0" @@ -7576,8 +8175,8 @@ "category": "Marketing", "name": "TDID", "domain": "adsrvr.org", - "description": "Registers a unique ID that identifies a returning user's device. The ID is used for targeted ads.", - "retention": "1 year", + "description": "OCD_TDID_description", + "retention": "OCD_retention_1_year", "dataController": "The Tradedesk", "gdprUrl": "https://adsrvr.org/", "wildcard": "0" @@ -7589,8 +8188,8 @@ "category": "Marketing", "name": "uid-bp-*", "domain": "stickyadstv.com", - "description": "The uid cookie is used by FreeWheel to generate statistics to show how many people may have seen a particular ad. Whereas the other cookies recognize returning users for the purpose of presenting users with relevant advertisements.", - "retention": "2 months", + "description": "OCD_uid_bp___description", + "retention": "OCD_retention_2_months", "dataController": "FreeWheel", "gdprUrl": "https://www.freewheel.com/privacy-policy", "wildcard": "1" @@ -7602,8 +8201,8 @@ "category": "Marketing", "name": "MRM_UID", "domain": "stickyadstv.com", - "description": "Used to track the visitor across multiple devices including TV", - "retention": "1 month", + "description": "OCD_MRM_UID_description", + "retention": "OCD_retention_1_month", "dataController": "FreeWheel", "gdprUrl": "https://www.freewheel.com/privacy-policy", "wildcard": "0" @@ -7615,8 +8214,8 @@ "category": "Marketing", "name": "uuidc", "domain": "mathtag.com", - "description": "Collects data on the user's visits to the website, such as what pages have been loaded. The registered data is used for targeted ads.", - "retention": "1 year", + "description": "OCD_uuidc_description", + "retention": "OCD_retention_1_year", "dataController": "MediaMath", "gdprUrl": "https://www.mediamath.com/privacy-policy/", "wildcard": "0" @@ -7628,8 +8227,8 @@ "category": "Marketing", "name": "zc", "domain": "zeotap.com", - "description": "Registers data on visitors from multiple visits and on multiple websites. This information is used to measure the efficiency of advertisement on websites.", - "retention": "10 years", + "description": "OCD_zc_description", + "retention": "OCD_retention_10_years", "dataController": "Zeotap", "gdprUrl": "https://zeotap.com/product-privacy-policy/", "wildcard": "0" @@ -7641,8 +8240,8 @@ "category": "Marketing", "name": "zsc", "domain": "zeotap.com", - "description": "Frequency capping for cookie syncing", - "retention": "1 day", + "description": "OCD_zsc_description", + "retention": "OCD_retention_1_day", "dataController": "Zeotap", "gdprUrl": "https://zeotap.com/product-privacy-policy/", "wildcard": "0" @@ -7654,8 +8253,8 @@ "category": "Marketing", "name": "zi", "domain": "zeotap.com", - "description": "User Identification", - "retention": "1 year", + "description": "OCD_zi_description", + "retention": "OCD_retention_1_year", "dataController": "Zeotap", "gdprUrl": "https://zeotap.com/product-privacy-policy/", "wildcard": "0" @@ -7667,8 +8266,8 @@ "category": "Marketing", "name": "idp", "domain": "zeotap.com", - "description": "User Identification", - "retention": "1 year", + "description": "OCD_idp_description", + "retention": "OCD_retention_1_year", "dataController": "Zeotap", "gdprUrl": "https://zeotap.com/product-privacy-policy/", "wildcard": "0" @@ -7680,8 +8279,8 @@ "category": "Marketing", "name": "zuc", "domain": "zeotap.com", - "description": "User Identification", - "retention": "1 year", + "description": "OCD_zuc_description", + "retention": "OCD_retention_1_year", "dataController": "Zeotap", "gdprUrl": "https://zeotap.com/product-privacy-policy/", "wildcard": "0" @@ -7693,8 +8292,8 @@ "category": "Marketing", "name": "amplitude_id*", "domain": "trustpilot.com", - "description": "These cookies are used by the TrustPilot service to identify you and enable you to leave reviews of our products and services.", - "retention": "1 year", + "description": "OCD_amplitude_id__description", + "retention": "OCD_retention_1_year", "dataController": "Trustpilot", "gdprUrl": "", "wildcard": "1" @@ -7706,8 +8305,8 @@ "category": "Functional", "name": "csrf-canary", "domain": "trustpilot.com", - "description": "These cookies are used by the TrustPilot service to identify you and enable you to leave reviews of our products and services. ", - "retention": "session", + "description": "OCD_csrf_canary_description", + "retention": "OCD_retention_session", "dataController": "Trustpilot", "gdprUrl": "", "wildcard": "0" @@ -7719,8 +8318,8 @@ "category": "Marketing", "name": "3pi", "domain": "id5-sync.com", - "description": "Sets a unique ID for the visitor, that allows third party advertisers to target the visitor with relevant advertisement. This pairing service is provided by third party advertisement hubs, which facilitates real-time bidding for advertisers.", - "retention": "3 months", + "description": "OCD_3pi_description", + "retention": "OCD_retention_3_months", "dataController": "ID5", "gdprUrl": "https://www.id5.io/privacy-policy", "wildcard": "0" @@ -7732,8 +8331,8 @@ "category": "Marketing", "name": "am-uid", "domain": "admixer.net", - "description": "This cookie is used to identify the visitor and optimize ad-relevance by collecting visitor data from multiple websites – this exchange of visitor data is normally provided by a third-party data-center or ad-exchange.", - "retention": "2 years", + "description": "OCD_am_uid_description", + "retention": "OCD_retention_2_years", "dataController": "Admixer", "gdprUrl": "https://admixer.net/privacy", "wildcard": "0" @@ -7745,8 +8344,8 @@ "category": "Marketing", "name": "_cc_dc", "domain": "crwdcntrl.net", - "description": "Collects anonymous statistical data related to the user's website visits, such as the number of visits, average time spent on the website and what pages have been loaded. The purpose is to segment the website's users according to factors such as demographics and geographical location, in order to enable media and marketing agencies to structure and understand their target groups to enable customised online advertising.", - "retention": "session", + "description": "OCD__cc_dc_description", + "retention": "OCD_retention_session", "dataController": "Lotame", "gdprUrl": "https://www.lotame.com/about-lotame/privacy/lotames-products-services-privacy-policy/", "wildcard": "0" @@ -7758,8 +8357,8 @@ "category": "Marketing", "name": "bkpa", "domain": "bluekai.com", - "description": "Used to present the visitor with relevant content and advertisement - The service is provided by third party advertisement hubs, which facilitate real-time bidding for advertisers.", - "retention": "179 days", + "description": "OCD_bkpa_description", + "retention": "OCD_retention_179_days", "dataController": "Oracle", "gdprUrl": "https://www.oracle.com/legal/privacy/marketing-cloud-data-cloud-privacy-policy.html", "wildcard": "0" @@ -7771,8 +8370,8 @@ "category": "Marketing", "name": "_ljtrtb_*", "domain": "lijit.com", - "description": "These cookies are used temporarily when multiple partners pass us their ID simultaneously. To avoid technical conflicts that arise from accessing the ljtrtb cookie for multiple partners at the same time, we store each partner’s ID in a separate cookie and then consolidate these IDs into the ljtrtb cookie when it’s available.", - "retention": "1 year", + "description": "OCD__ljtrtb___description", + "retention": "OCD_retention_1_year", "dataController": "SOVRN", "gdprUrl": "https://www.sovrn.com/legal/privacy-policy/", "wildcard": "1" @@ -7784,8 +8383,8 @@ "category": "Marketing", "name": "ljtrtb", "domain": "lijit.com", - "description": "Enables us to help our advertising partners make decisions about displaying an advertisement to you. We store the ID that each partner uses to identify you and pass that information to the partner when a website requests an advertisement from us.", - "retention": "1 year", + "description": "OCD_ljtrtb_description", + "retention": "OCD_retention_1_year", "dataController": "SOVRN", "gdprUrl": "https://www.sovrn.com/legal/privacy-policy/", "wildcard": "0" @@ -7797,8 +8396,8 @@ "category": "Marketing", "name": "uuid", "domain": "mathtag.com", - "description": "Collects data on the user's visits to the website, such as what pages have been loaded. The registered data is used for targeted ads.", - "retention": "1 year", + "description": "OCD_uuid_description", + "retention": "OCD_retention_1_year", "dataController": "MediaMath", "gdprUrl": "https://www.mediamath.com/privacy-policy/", "wildcard": "0" @@ -7810,8 +8409,8 @@ "category": "Functional", "name": "_pinterest_cm", "domain": "pinterest.com", - "description": "Pinterest cookie ensures that you can share our website pages via Pinterest by means of the 'share' button", - "retention": "347 days", + "description": "OCD__pinterest_cm_description", + "retention": "OCD_retention_347_days", "dataController": "Pinterest", "gdprUrl": "https://policy.pinterest.com/en/privacy-policy", "wildcard": "0" @@ -7823,8 +8422,8 @@ "category": "Functional", "name": "_pinterest_sess", "domain": "pinterest.com", - "description": "session cookie (expires after your session) which collects anonymous data about a user's visit to the website, such as the number of visits, average time spent on the site and which pages have been loaded in order to personalise and improve the Pinterest service.", - "retention": "session", + "description": "OCD__pinterest_sess_description", + "retention": "OCD_retention_session", "dataController": "Pinterest", "gdprUrl": "https://policy.pinterest.com/en/privacy-policy", "wildcard": "0" @@ -7836,8 +8435,8 @@ "category": "Marketing", "name": "_pin_unauth", "domain": "pinterest.com", - "description": "Registers a unique ID that identifies and recognizes the user. Is used for targeted advertising.", - "retention": "1 day", + "description": "OCD__pin_unauth_description", + "retention": "OCD_retention_1_day", "dataController": "Pinterest", "gdprUrl": "https://policy.pinterest.com/en/privacy-policy", "wildcard": "0" @@ -7849,8 +8448,8 @@ "category": "Marketing", "name": "_pinterest_ct_ua", "domain": "pinterest.com", - "description": "This cookieis a third party cookie which groups actions for users who cannot be identified by Pinterest.", - "retention": "session", + "description": "OCD__pinterest_ct_ua_description", + "retention": "OCD_retention_session", "dataController": "Pinterest", "gdprUrl": "https://policy.pinterest.com/en/privacy-policy", "wildcard": "0" @@ -7862,8 +8461,8 @@ "category": "Marketing", "name": "sessionFunnelEventLogged", "domain": "pinterest.com", - "description": "A generic technical cookie used for storing user session identifier in web applications", - "retention": "1 day", + "description": "OCD_sessionFunnelEventLogged_description", + "retention": "OCD_retention_1_day", "dataController": "Pinterest", "gdprUrl": "https://policy.pinterest.com/en/privacy-policy", "wildcard": "0" @@ -7875,8 +8474,8 @@ "category": "Marketing", "name": "_routing_id", "domain": "pinterest.com", - "description": "Allows users to share pictures via Pinterest / the Pin It button. Pinterest can collect statistical information about usage of their service.", - "retention": "1 day", + "description": "OCD__routing_id_description", + "retention": "OCD_retention_1_day", "dataController": "Pinterest", "gdprUrl": "https://policy.pinterest.com/en/privacy-policy", "wildcard": "0" @@ -7888,8 +8487,8 @@ "category": "Marketing", "name": "_derived_epik", "domain": "pinterest.com", - "description": "Cookie is placed by the Pinterest tag when a match is identified when no cookies are present, such as enhanced match.", - "retention": "1 year", + "description": "OCD__derived_epik_description", + "retention": "OCD_retention_1_year", "dataController": "Pinterest", "gdprUrl": "https://policy.pinterest.com/en/privacy-policy", "wildcard": "0" @@ -7901,8 +8500,8 @@ "category": "Marketing", "name": "_pinterest_ct", "domain": "", - "description": "They contain a user ID and the timestamp at which the cookie was created.", - "retention": "1 year", + "description": "OCD__pinterest_ct_description", + "retention": "OCD_retention_1_year", "dataController": "Pinterest", "gdprUrl": "https://policy.pinterest.com/en/privacy-policy", "wildcard": "0" @@ -7914,8 +8513,8 @@ "category": "Marketing", "name": "_pinterest_ct_rt", "domain": "", - "description": "They contain a user ID and the timestamp at which the cookie was created.", - "retention": "1 year", + "description": "OCD__pinterest_ct_rt_description", + "retention": "OCD_retention_1_year", "dataController": "Pinterest", "gdprUrl": "https://policy.pinterest.com/en/privacy-policy", "wildcard": "0" @@ -7927,8 +8526,8 @@ "category": "Marketing", "name": "_epik", "domain": "pinterest.com", - "description": "Cookie is placed by the JavaScript tag based on information sent from Pinterest with promoted traffic to help identify the user.", - "retention": "1 year", + "description": "OCD__epik_description", + "retention": "OCD_retention_1_year", "dataController": "Pinterest", "gdprUrl": "https://policy.pinterest.com/en/privacy-policy", "wildcard": "0" @@ -7940,8 +8539,8 @@ "category": "Functional", "name": "Nop.customer", "domain": "", - "description": "Customer cookie. Used to identifier guest customers.", - "retention": "1 month", + "description": "OCD_Nop_customer_description", + "retention": "OCD_retention_1_month", "dataController": "nopCommerce", "gdprUrl": "https://www.nopcommerce.com/privacy-policy", "wildcard": "0" @@ -7953,8 +8552,8 @@ "category": "Functional", "name": "NopCommerce.RecentlyViewedProducts", "domain": "", - "description": "Recently viewed products cookie. Stores a list of the recently viewed products", - "retention": "10 days", + "description": "OCD_NopCommerce_RecentlyViewedProducts_description", + "retention": "OCD_retention_10_days", "dataController": "nopCommerce", "gdprUrl": "https://www.nopcommerce.com/privacy-policy", "wildcard": "0" @@ -7966,8 +8565,8 @@ "category": "Functional", "name": "NOPCOMMERCE.AUTH", "domain": "", - "description": "Forms authentication cookie. Used for authenticating registered customers.", - "retention": "session", + "description": "OCD_NOPCOMMERCE_AUTH_description", + "retention": "OCD_retention_session", "dataController": "nopCommerce", "gdprUrl": "https://www.nopcommerce.com/privacy-policy", "wildcard": "0" @@ -7979,8 +8578,8 @@ "category": "Functional", "name": "tsrvid", "domain": "", - "description": "Feedback company review cookie", - "retention": "1 year", + "description": "OCD_tsrvid_description", + "retention": "OCD_retention_1_year", "dataController": "FeedbackCompany", "gdprUrl": "https://www.feedbackcompany.com/nl-nl/privacy-statement/", "wildcard": "0" @@ -7992,8 +8591,8 @@ "category": "Functional", "name": "form_key", "domain": "", - "description": "A security measure that appends a random string to all form submissions to protect the data from Cross-Site Request Forgery (CSRF).", - "retention": "session", + "description": "OCD_form_key_description", + "retention": "OCD_retention_session", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -8005,8 +8604,8 @@ "category": "Functional", "name": "product_data_storage*", "domain": "", - "description": "Stores configuration for product data related to Recently Viewed / Compared Products.", - "retention": "session", + "description": "OCD_product_data_storage__description", + "retention": "OCD_retention_session", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "1" @@ -8018,8 +8617,8 @@ "category": "Functional", "name": "mage-cache-sessid", "domain": "", - "description": "The value of this cookie triggers the cleanup of local cache storage. When the cookie is removed by the backend application, the Admin cleans up local storage, and sets the cookie value to true.", - "retention": "session", + "description": "OCD_mage_cache_sessid_description", + "retention": "OCD_retention_session", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -8031,8 +8630,8 @@ "category": "Functional", "name": "mage-cache-storage*", "domain": "", - "description": "Local storage of visitor-specific content that enables ecommerce functions.", - "retention": "session", + "description": "OCD_mage_cache_storage__description", + "retention": "OCD_retention_session", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "1" @@ -8044,8 +8643,8 @@ "category": "Functional", "name": "mage-cache-storage-section-invalidation*", "domain": "", - "description": "Forces local storage of specific content sections that should be invalidated.", - "retention": "session", + "description": "OCD_mage_cache_storage_section_invalidation__description", + "retention": "OCD_retention_session", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "1" @@ -8057,8 +8656,8 @@ "category": "Functional", "name": "mage-cache-timeout*", "domain": "", - "description": "This cookie is necessary for the cache function. A cache is used by the website to optimize the response time between the visitor and the website. The cache is usually stored on the visitor's browser.", - "retention": "session", + "description": "OCD_mage_cache_timeout__description", + "retention": "OCD_retention_session", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "1" @@ -8070,8 +8669,8 @@ "category": "Functional", "name": "mage-messages", "domain": "", - "description": "Tracks error messages and other notifications that are shown to the user, such as the cookie consent message, and various error messages. The message is deleted from the cookie after it is shown to the shopper.", - "retention": "1 year", + "description": "OCD_mage_messages_description", + "retention": "OCD_retention_1_year", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -8083,8 +8682,8 @@ "category": "Functional", "name": "mage-translation-file-version", "domain": "", - "description": "Tracks the version of translations in local storage. Used when Translation Strategy is configured as Dictionary (Translation on Storefront side).", - "retention": "session", + "description": "OCD_mage_translation_file_version_description", + "retention": "OCD_retention_session", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -8096,8 +8695,8 @@ "category": "Functional", "name": "mage-translation-storage", "domain": "", - "description": "Stores translated content when requested by the shopper. Used when Translation Strategy is configured as Dictionary (Translation on Storefront side).", - "retention": "session", + "description": "OCD_mage_translation_storage_description", + "retention": "OCD_retention_session", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -8109,8 +8708,8 @@ "category": "Functional", "name": "product_data_storage", "domain": "", - "description": "Stores configuration for product data related to Recently Viewed / Compared Products.", - "retention": "session", + "description": "OCD_product_data_storage_description", + "retention": "OCD_retention_session", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -8122,8 +8721,8 @@ "category": "Functional", "name": "recently_compared_product*", "domain": "", - "description": "Stores product IDs of recently compared products.", - "retention": "session", + "description": "OCD_recently_compared_product__description", + "retention": "OCD_retention_session", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "1" @@ -8135,8 +8734,8 @@ "category": "Functional", "name": "recently_compared_product_previous*", "domain": "", - "description": "Stores product IDs of previously compared products for easy navigation.", - "retention": "session", + "description": "OCD_recently_compared_product_previous__description", + "retention": "OCD_retention_session", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "1" @@ -8148,8 +8747,8 @@ "category": "Functional", "name": "recently_viewed_product*", "domain": "", - "description": "Stores product IDs of recently viewed products for easy navigation.", - "retention": "session", + "description": "OCD_recently_viewed_product__description", + "retention": "OCD_retention_session", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "1" @@ -8161,8 +8760,8 @@ "category": "Functional", "name": "recently_viewed_product_previous*", "domain": "", - "description": "Stores product IDs of recently previously viewed products for easy navigation.", - "retention": "session", + "description": "OCD_recently_viewed_product_previous__description", + "retention": "OCD_retention_session", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "1" @@ -8174,8 +8773,8 @@ "category": "Functional", "name": "user_allowed_save_cookie", "domain": "", - "description": "Indicates if a customer is allowed to use cookies.", - "retention": "session", + "description": "OCD_user_allowed_save_cookie_description", + "retention": "OCD_retention_session", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -8187,8 +8786,8 @@ "category": "Functional", "name": "external_no_cache", "domain": "", - "description": "A flag that indicates if caching is disabled.", - "retention": "session", + "description": "OCD_external_no_cache_description", + "retention": "OCD_retention_session", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -8200,8 +8799,8 @@ "category": "Functional", "name": "persistent_shopping_cart", "domain": "", - "description": "Stores the key (ID) of persistent cart to make it possible to restore the cart for an anonymous shopper.", - "retention": "session", + "description": "OCD_persistent_shopping_cart_description", + "retention": "OCD_retention_session", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -8213,8 +8812,8 @@ "category": "Functional", "name": "stf", "domain": "", - "description": "Records the time messages are sent by the SendFriend (Email a Friend) module.", - "retention": "session", + "description": "OCD_stf_description", + "retention": "OCD_retention_session", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -8226,8 +8825,8 @@ "category": "Functional", "name": "pollN", "domain": "", - "description": "A poll ID that indicates if a vote has occurred.", - "retention": "session", + "description": "OCD_pollN_description", + "retention": "OCD_retention_session", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -8239,8 +8838,8 @@ "category": "Functional", "name": "frontend", "domain": "", - "description": "Session ID", - "retention": "session", + "description": "OCD_frontend_description", + "retention": "OCD_retention_session", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -8252,8 +8851,8 @@ "category": "Functional", "name": "guest-view", "domain": "", - "description": "Allows guests to edit their orders.", - "retention": "session", + "description": "OCD_guest_view_description", + "retention": "OCD_retention_session", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -8265,8 +8864,8 @@ "category": "Functional", "name": "mage-banners-cache-storage", "domain": "", - "description": "Stores banner content locally to improve performance.", - "retention": "1 hour", + "description": "OCD_mage_banners_cache_storage_description", + "retention": "OCD_retention_1_hour", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -8278,8 +8877,8 @@ "category": "Functional", "name": "searchReport-log", "domain": "", - "description": "Magento, used to log information about searching", - "retention": "1 year", + "description": "OCD_searchReport_log_description", + "retention": "OCD_retention_1_year", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -8291,8 +8890,8 @@ "category": "Functional", "name": "private_content_version", "domain": "", - "description": "Appends a random, unique number and time to pages with customer content to prevent them from being cached on the server.", - "retention": "1 year", + "description": "OCD_private_content_version_description", + "retention": "OCD_retention_1_year", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -8304,8 +8903,8 @@ "category": "Functional", "name": "X-Magento-Vary", "domain": "", - "description": "X-Magento-Vary cookie is used by Magento 2 system to highlight that version of a page requested by a user has been changed. It allows having different versions of the same page stored in cache e.g. Varnish.", - "retention": "session", + "description": "OCD_X_Magento_Vary_description", + "retention": "OCD_retention_session", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -8317,8 +8916,8 @@ "category": "Functional", "name": "section_data_ids", "domain": "", - "description": "Stores customer-specific information related to shopper-initiated actions such as display wish list, checkout information, etc.", - "retention": "session", + "description": "OCD_section_data_ids_description", + "retention": "OCD_retention_session", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -8330,8 +8929,8 @@ "category": "Functional", "name": "section_data_clean", "domain": "", - "description": "Determines which products the user has viewed, allowing the website to promote related products.", - "retention": "1 day", + "description": "OCD_section_data_clean_description", + "retention": "OCD_retention_1_day", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -8343,8 +8942,8 @@ "category": "Functional", "name": "last_visited_store", "domain": "", - "description": "This cookie keeps track of the last website you visited. This is necessary to enable the correct language on the website.", - "retention": "1 day", + "description": "OCD_last_visited_store_description", + "retention": "OCD_retention_1_day", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -8356,8 +8955,8 @@ "category": "Functional", "name": "store", "domain": "", - "description": "This cookie keeps track of the last website you visited. This is necessary to enable the correct language on the website.", - "retention": "1 day", + "description": "OCD_store_description", + "retention": "OCD_retention_1_day", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -8369,8 +8968,8 @@ "category": "Functional", "name": "login_redirect", "domain": "", - "description": "Preserves the destination page that was loading before the customer was directed to log in.", - "retention": "session", + "description": "OCD_login_redirect_description", + "retention": "OCD_retention_session", "dataController": "Adobe", "gdprUrl": "https://www.adobe.com/privacy.html", "wildcard": "0" @@ -8382,8 +8981,8 @@ "category": "Marketing", "name": "dsps:*", "domain": "px.powerlinks.com", - "description": "Service to display targeted advertising to visitors.", - "retention": "90 days", + "description": "OCD_dsps___description", + "retention": "OCD_retention_90_days", "dataController": "PowerLinks Media Limited", "gdprUrl": "https://www.powerlinks.com/privacy-policy/", "wildcard": "1" @@ -8395,8 +8994,8 @@ "category": "Marketing", "name": "_mb", "domain": "vuble.tv", - "description": "Used in context with video-advertisement. The cookie limits the number of times a visitor is shown the same advertisement-content. The cookie is also used to ensure relevance of the video-advertisement to the specific visitor.", - "retention": "session", + "description": "OCD__mb_description", + "retention": "OCD_retention_session", "dataController": "Vuble", "gdprUrl": "https://www.vuble.tv/privacy", "wildcard": "0" @@ -8408,8 +9007,8 @@ "category": "Functional", "name": "wordpress_test_cookie", "domain": "", - "description": "Cookie set by WordPress to check if the cookies are enabled on the browser to provide appropriate user experience to the users", - "retention": "session", + "description": "OCD_wordpress_test_cookie_description", + "retention": "OCD_retention_session", "dataController": "Wordpress", "gdprUrl": "", "wildcard": "0" @@ -8421,8 +9020,8 @@ "category": "Functional", "name": "componentType", "domain": "", - "description": "componentType is a session cookie, used for correct recording the type of the page (frontpage, single page, blog etc)", - "retention": "session", + "description": "OCD_componentType_description", + "retention": "OCD_retention_session", "dataController": "Joomla!", "gdprUrl": "", "wildcard": "0" @@ -8434,8 +9033,8 @@ "category": "Functional", "name": "componentStyle", "domain": "", - "description": "componentStyle is a session cookie, used for setting the proper template in compliance with visited type of the page", - "retention": "session", + "description": "OCD_componentStyle_description", + "retention": "OCD_retention_session", "dataController": "Joomla!", "gdprUrl": "", "wildcard": "0" @@ -8447,8 +9046,8 @@ "category": "Functional", "name": "nrid", "domain": "", - "description": "This cookie is used to remember a user's choice about cookies on the website. Where users have previously indicated a preference, that user’s preference will be stored in this cookie.", - "retention": "2 years", + "description": "OCD_nrid_description", + "retention": "OCD_retention_2_years", "dataController": "Joomla!", "gdprUrl": "", "wildcard": "0" @@ -8460,8 +9059,8 @@ "category": "Marketing", "name": "koitk", "domain": ".marketingautomation.services", - "description": "Collects data on visitors behavior and interaction - This is used to optimize the website and make advertisement on the website more relevant.", - "retention": "3 years", + "description": "OCD_koitk_description", + "retention": "OCD_retention_3_years", "dataController": "Constant Contact", "gdprUrl": "https://www.constantcontact.com/legal/privacy-notice", "wildcard": "0" @@ -8473,8 +9072,8 @@ "category": "Marketing", "name": "__ss_referrer", "domain": "", - "description": "This cookie contains information about where the visitor came from, called the source for the visit.", - "retention": "6 hours", + "description": "OCD___ss_referrer_description", + "retention": "OCD_retention_6_hours", "dataController": "Constant Contact", "gdprUrl": "https://www.constantcontact.com/legal/privacy-notice", "wildcard": "0" @@ -8486,8 +9085,8 @@ "category": "Marketing", "name": "__ss_tk", "domain": "", - "description": "This is Sharspring’s token cookie which enables user tracking. It ensures that the visit to website is connected to the user independent of the session and the source.", - "retention": "25 years", + "description": "OCD___ss_tk_description", + "retention": "OCD_retention_25_years", "dataController": "Constant Contact", "gdprUrl": "https://www.constantcontact.com/legal/privacy-notice", "wildcard": "0" @@ -8499,8 +9098,8 @@ "category": "Marketing", "name": "__ss", "domain": "", - "description": "This cookie is storing the session ID for your visit. It is used in combination with _ss_tk to group website visits in reports for a single user.", - "retention": "30 minutes", + "description": "OCD___ss_description", + "retention": "OCD_retention_30_minutes", "dataController": "Constant Contact", "gdprUrl": "https://www.constantcontact.com/legal/privacy-notice", "wildcard": "0" @@ -8512,8 +9111,8 @@ "category": "Analytics", "name": "_pk_id*", "domain": "", - "description": "Used to store a few details about the user such as the unique visitor ID", - "retention": "13 months", + "description": "OCD__pk_id__description", + "retention": "OCD_retention_13_months", "dataController": "Matomo", "gdprUrl": "https://matomo.org/privacy-policy/", "wildcard": "1" @@ -8525,8 +9124,8 @@ "category": "Analytics", "name": "_pk_ref*", "domain": "", - "description": "Used to store the attribution information, the referrer initially used to visit the website", - "retention": "6 months", + "description": "OCD__pk_ref__description", + "retention": "OCD_retention_6_months", "dataController": "Matomo", "gdprUrl": "https://matomo.org/privacy-policy/", "wildcard": "1" @@ -8538,8 +9137,8 @@ "category": "Analytics", "name": "_pk_ses*", "domain": "", - "description": "Short lived cookies used to temporarily store data for the visit", - "retention": "30 minutes", + "description": "OCD__pk_ses__description", + "retention": "OCD_retention_30_minutes", "dataController": "Matomo", "gdprUrl": "https://matomo.org/privacy-policy/", "wildcard": "1" @@ -8551,8 +9150,8 @@ "category": "Analytics", "name": "_pk_cvar", "domain": "", - "description": "Short lcts data on visitors behavior and interaction - This is used to optimize the website and make advertisement on the website more relevant.", - "retention": "3 years", + "description": "OCD__pk_cvar_description", + "retention": "OCD_retention_3_years", "dataController": "Sharpspring", "gdprUrl": "https://sharpspring.com/legal/privacy/", "wildcard": "0" @@ -8564,8 +9163,8 @@ "category": "Analytics", "name": "_pk_hsr*", "domain": "", - "description": "Short lived cookies used to temporarily store data for the visit", - "retention": "30 minutes", + "description": "OCD__pk_hsr__description", + "retention": "OCD_retention_30_minutes", "dataController": "Matomo", "gdprUrl": "https://matomo.org/privacy-policy/", "wildcard": "1" @@ -8577,8 +9176,8 @@ "category": "Analytics", "name": "_pk_testcookie*", "domain": "", - "description": "Cookie is created and should be then directly deleted (used to check whether the visitor’s browser supports cookies)", - "retention": "session", + "description": "OCD__pk_testcookie__description", + "retention": "OCD_retention_session", "dataController": "Matomo", "gdprUrl": "https://matomo.org/privacy-policy/", "wildcard": "1" @@ -8590,8 +9189,8 @@ "category": "Analytics", "name": "mtm_consent", "domain": "", - "description": "Cookie is created with no expiry date to forever remember that consent was given by the user.", - "retention": "forever", + "description": "OCD_mtm_consent_description", + "retention": "OCD_retention_forever", "dataController": "Matomo", "gdprUrl": "https://matomo.org/privacy-policy/", "wildcard": "0" @@ -8603,8 +9202,8 @@ "category": "Marketing", "name": "datatrics_optin", "domain": "", - "description": "Saving opt-in preferences.", - "retention": "undefined", + "description": "OCD_datatrics_optin_description", + "retention": "OCD_retention_undefined", "dataController": "Datatrics", "gdprUrl": "https://www.datatrics.com/privacy", "wildcard": "0" @@ -8616,8 +9215,8 @@ "category": "Marketing", "name": "datatricsDebugger", "domain": "", - "description": "Saving Datatrics debugger preferences.", - "retention": "undefined", + "description": "OCD_datatricsDebugger_description", + "retention": "OCD_retention_undefined", "dataController": "Datatrics", "gdprUrl": "https://www.datatrics.com/privacy", "wildcard": "0" @@ -8629,8 +9228,8 @@ "category": "Marketing", "name": "datatrics_customData", "domain": "", - "description": "Saving defined custom data.", - "retention": "undefined", + "description": "OCD_datatrics_customData_description", + "retention": "OCD_retention_undefined", "dataController": "Datatrics", "gdprUrl": "https://www.datatrics.com/privacy", "wildcard": "0" @@ -8642,8 +9241,8 @@ "category": "Analytics", "name": "_wepublishGa", "domain": "", - "description": "ID used to identify users", - "retention": "2 years", + "description": "OCD__wepublishGa_description", + "retention": "OCD_retention_2_years", "dataController": "WePublish", "gdprUrl": "https://www.wepublish.com/privacy-statement.html", "wildcard": "0" @@ -8655,8 +9254,8 @@ "category": "Analytics", "name": "_wepublishGa_gid", "domain": "", - "description": "ID used to identify users for 24 hours after last activity 24 hours", - "retention": "24 hours", + "description": "OCD__wepublishGa_gid_description", + "retention": "OCD_retention_24_hours", "dataController": "WePublish", "gdprUrl": "https://www.wepublish.com/privacy-statement.html", "wildcard": "0" @@ -8668,8 +9267,8 @@ "category": "Functional", "name": "OptanonConsent", "domain": "", - "description": "This cookie is set by the cookie compliance solution from OneTrust. It stores information about the categories of cookies the site uses and whether visitors have given or withdrawn consent for the use of each category. This enables site owners to prevent cookies in each category from being set in the user’s browser, when consent is not given. The cookie has a normal lifespan of one year, so that returning visitors to the site will have their preferences remembered. It contains no information that can identify the site visitor.", - "retention": "1 year", + "description": "OCD_OptanonConsent_description", + "retention": "OCD_retention_1_year", "dataController": "OneTrust", "gdprUrl": "https://www.onetrust.com/privacy-notice/", "wildcard": "0" @@ -8681,8 +9280,8 @@ "category": "Functional", "name": "OptanonAlertBoxClosed", "domain": "", - "description": "This cookie is set by the cookie compliance solution from OneTrust. It stores information about the categories of cookies the site uses and whether visitors have given or withdrawn consent for the use of each category. This enables site owners to prevent cookies in each category from being set in the users browser, when consent is not given. The cookie has a normal lifespan of one year, so that returning visitors to the site will have their preferences remembered. It contains no information that can identify the site visitor.", - "retention": "1 year", + "description": "OCD_OptanonAlertBoxClosed_description", + "retention": "OCD_retention_1_year", "dataController": "OneTrust", "gdprUrl": "https://www.onetrust.com/privacy/", "wildcard": "0" @@ -8694,24 +9293,61 @@ "category": "Functional", "name": "OptanonControl", "domain": "", - "description": "This cookie is set by the cookie compliance solution from OneTrust. It stores information about the categories of cookies the site uses and whether visitors have given or withdrawn consent for the use of each category. This enables site owners to prevent cookies in each category from being set in the user’s browser, when consent is not given. The cookie has a normal lifespan of one year, so that returning visitors to the site will have their preferences remembered. It contains no information that can identify the site visitor.", - "retention": "1 year", + "description": "OCD_OptanonControl_description", + "retention": "OCD_retention_1_year", "dataController": "OneTrust", "gdprUrl": "https://www.onetrust.com/privacy-notice/", "wildcard": "0" } ], - "optimizelyEndUserId": [ + "OneTrustWPCCPAGoogleOptOut": [ { - "platform": "Optimizely", - "category": "Marketing", - "name": "optimizelyEndUserId", + "platform": "OneTrust", + "category": "Functional", + "name": "OneTrustWPCCPAGoogleOptOut", + "domain": "", + "description": "OCD_OneTrustWPCCPAGoogleOptOut_description", + "retention": "OCD_retention_365_days", + "dataController": "OneTrust", + "gdprUrl": "https://www.onetrust.com/privacy-notice/", + "wildcard": "0" + } + ], + "FunctionalCookie": [ + { + "platform": "OneTrust", + "category": "Functional", + "name": "FunctionalCookie", + "domain": "", + "description": "OCD_FunctionalCookie_description", + "retention": "OCD_retention_0_days", + "dataController": "OneTrust", + "gdprUrl": "https://www.onetrust.com/privacy-notice/", + "wildcard": "0" + } + ], + "optimizelyEndUserId": [ + { + "platform": "Optimizely", + "category": "Marketing", + "name": "optimizelyEndUserId", "domain": "", - "description": "Stores a visitor's unique Optimizely identifier. It's a combination of a timestamp and random number. No other information about you or your visitors is stored inside.", - "retention": "6 months", + "description": "OCD_optimizelyEndUserId_description", + "retention": "OCD_retention_6_months", "dataController": "Optimizely", "gdprUrl": "https://www.optimizely.com/privacy/", "wildcard": "0" + }, + { + "platform": "Wistia", + "category": "Functional", + "name": "optimizelyEndUserId", + "domain": "", + "description": "Set by Wistia. Stores a visitor’s unique Optimizely identifier. It’s a combination of a timestamp and random number. No other information about you or your visitors is stored inside.", + "retention": "10 Years", + "dataController": "Wistia", + "gdprUrl": "https://wistia.com/privacy", + "wildcard": "0" } ], "optimizelyRedirectData": [ @@ -8720,8 +9356,8 @@ "category": "Marketing", "name": "optimizelyRedirectData", "domain": "", - "description": "After Optimizely has executed a redirect experiment, stores various data from the original page so that Optimizely still has access to it on the new page.", - "retention": "5 seconds", + "description": "OCD_optimizelyRedirectData_description", + "retention": "OCD_retention_5_seconds", "dataController": "Optimizely", "gdprUrl": "https://www.optimizely.com/privacy/", "wildcard": "0" @@ -8733,8 +9369,8 @@ "category": "Marketing", "name": "optimizelyDomainTestCookie", "domain": "", - "description": "When Optimizely loads a URL, the snippet places the cookie to get the current domain, for the purpose of whether cross-domain syncing is possible. If successful, the cookie is immediately removed.", - "retention": "6 months", + "description": "OCD_optimizelyDomainTestCookie_description", + "retention": "OCD_retention_6_months", "dataController": "Optimizely", "gdprUrl": "https://www.optimizely.com/privacy/", "wildcard": "0" @@ -8746,8 +9382,8 @@ "category": "Marketing", "name": "optimizelyOptOut", "domain": "", - "description": "Stores a boolean indicating whether the visitor has opted out of participating in Optimizely-powered experimentation.", - "retention": "10 years", + "description": "OCD_optimizelyOptOut_description", + "retention": "OCD_retention_10_years", "dataController": "Optimizely", "gdprUrl": "https://www.optimizely.com/privacy/", "wildcard": "0" @@ -8759,8 +9395,8 @@ "category": "Functional", "name": "wwwchannelme_z_sid", "domain": "", - "description": "The cookie is used when using the co-browsing feature.", - "retention": "session", + "description": "OCD_wwwchannelme_z_sid_description", + "retention": "OCD_retention_session", "dataController": "Channel.me", "gdprUrl": "https://channel.me/privacy", "wildcard": "0" @@ -8772,8 +9408,8 @@ "category": "Marketing", "name": "app_ts", "domain": "adscience.nl", - "description": "Used by adscience.nl to display remarketing campaigns.", - "retention": "1 year", + "description": "OCD_app_ts_description", + "retention": "OCD_retention_1_year", "dataController": "Ortec", "gdprUrl": "https://www.ortecadscience.com/privacy-policy/", "wildcard": "0" @@ -8785,8 +9421,8 @@ "category": "Marketing", "name": "viewer", "domain": "adscience.nl", - "description": "Used by adscience.nl to measure visitor numbers and information and use it to optimize marketing campaigns.", - "retention": "1 year", + "description": "OCD_viewer_description", + "retention": "OCD_retention_1_year", "dataController": "Ortec", "gdprUrl": "https://www.ortecadscience.com/privacy-policy/", "wildcard": "0" @@ -8798,8 +9434,8 @@ "category": "Marketing", "name": "spx_ts", "domain": "adscience.nl", - "description": "These cookies ensure that relevant advertisements are displayed on external websites.", - "retention": "1 year", + "description": "OCD_spx_ts_description", + "retention": "OCD_retention_1_year", "dataController": "Ortec", "gdprUrl": "https://www.ortecadscience.com/privacy-policy/", "wildcard": "0" @@ -8811,8 +9447,8 @@ "category": "Marketing", "name": "adx_ts", "domain": "adscience.nl", - "description": "These cookies ensure that relevant advertisements are displayed on external websites.", - "retention": "1 year", + "description": "OCD_adx_ts_description", + "retention": "OCD_retention_1_year", "dataController": "Ortec", "gdprUrl": "https://www.ortecadscience.com/privacy-policy/", "wildcard": "0" @@ -8824,8 +9460,8 @@ "category": "Marketing", "name": "id_ts", "domain": "adscience.nl", - "description": "These cookies ensure that relevant advertisements are displayed on external websites.", - "retention": "1 year", + "description": "OCD_id_ts_description", + "retention": "OCD_retention_1_year", "dataController": "Ortec", "gdprUrl": "https://www.ortecadscience.com/privacy-policy/", "wildcard": "0" @@ -8837,8 +9473,8 @@ "category": "Marketing", "name": "euconsent", "domain": "faktor.io", - "description": "Cookie compliance check", - "retention": "1 year", + "description": "OCD_euconsent_description", + "retention": "OCD_retention_1_year", "dataController": "LiveRamp", "gdprUrl": "https://liveramp.com/privacy/", "wildcard": "0" @@ -8850,8 +9486,8 @@ "category": "Functional", "name": "SSR-caching", "domain": "wix.com", - "description": "Indicates how a site was rendered", - "retention": "session", + "description": "OCD_SSR_caching_description", + "retention": "OCD_retention_session", "dataController": "Wix.com", "gdprUrl": "https://support.wix.com/erticle/cookies-and-your-wix-site", "wildcard": "0" @@ -8863,8 +9499,8 @@ "category": "Functional", "name": "smSession", "domain": "wix.com", - "description": "Identifies logged in site members", - "retention": "2 weeks", + "description": "OCD_smSession_description", + "retention": "OCD_retention_2_weeks", "dataController": "Wix.com", "gdprUrl": "https://support.wix.com/erticle/cookies-and-your-wix-site", "wildcard": "0" @@ -8876,8 +9512,8 @@ "category": "Marketing", "name": "svSession", "domain": "wix.com", - "description": "Identifies unique visitors and tracks a visitor’s sessions on a site", - "retention": "2 years", + "description": "OCD_svSession_description", + "retention": "OCD_retention_2_years", "dataController": "Wix.com", "gdprUrl": "https://support.wix.com/erticle/cookies-and-your-wix-site", "wildcard": "0" @@ -8889,8 +9525,8 @@ "category": "Functional", "name": "ForceFlashSite", "domain": "wix.com", - "description": "When viewing a mobile site (old mobile under m.domain.com) it will force the server to display the non-mobile version and avoid redirecting to the mobile site", - "retention": "session", + "description": "OCD_ForceFlashSite_description", + "retention": "OCD_retention_session", "dataController": "Wix.com", "gdprUrl": "https://support.wix.com/erticle/cookies-and-your-wix-site", "wildcard": "0" @@ -8902,8 +9538,8 @@ "category": "Functional", "name": "hs", "domain": "wix.com", - "description": "Security", - "retention": "session", + "description": "OCD_hs_description", + "retention": "OCD_retention_session", "dataController": "Wix.com", "gdprUrl": "https://support.wix.com/erticle/cookies-and-your-wix-site", "wildcard": "0" @@ -8915,8 +9551,8 @@ "category": "Functional", "name": "bSession", "domain": "", - "description": "Used for system effectiveness measurement", - "retention": "30 minutes", + "description": "OCD_bSession_description", + "retention": "OCD_retention_30_minutes", "dataController": "Wix.com", "gdprUrl": "https://support.wix.com/erticle/cookies-and-your-wix-site", "wildcard": "0" @@ -8928,8 +9564,8 @@ "category": "Functional", "name": "TS01*", "domain": "", - "description": "Used for security and anti-fraud reasons", - "retention": "session", + "description": "OCD_TS01__description", + "retention": "OCD_retention_session", "dataController": "Wix.com", "gdprUrl": "https://support.wix.com/erticle/cookies-and-your-wix-site", "wildcard": "1" @@ -8941,8 +9577,8 @@ "category": "Functional", "name": "fedops.logger.sessionId", "domain": "", - "description": "Used for stability/effectiveness measurement", - "retention": "12 months", + "description": "OCD_fedops_logger_sessionId_description", + "retention": "OCD_retention_12_months", "dataController": "Wix.com", "gdprUrl": "https://support.wix.com/erticle/cookies-and-your-wix-site", "wildcard": "0" @@ -8954,8 +9590,8 @@ "category": "Functional", "name": "wixLanguage", "domain": "", - "description": "Used on multilingual websites to save user language preference", - "retention": "12 months", + "description": "OCD_wixLanguage_description", + "retention": "OCD_retention_12_months", "dataController": "Wix.com", "gdprUrl": "https://support.wix.com/erticle/cookies-and-your-wix-site", "wildcard": "0" @@ -8967,8 +9603,8 @@ "category": "Functional", "name": "_wixCIDX", "domain": "", - "description": "Used for system monitoring/debugging", - "retention": "3 months", + "description": "OCD__wixCIDX_description", + "retention": "OCD_retention_3_months", "dataController": "Wix.com", "gdprUrl": "https://support.wix.com/erticle/cookies-and-your-wix-site", "wildcard": "0" @@ -8980,8 +9616,8 @@ "category": "Functional", "name": "_wix_browser_sess", "domain": "", - "description": "Used for system monitoring/debugging", - "retention": "session", + "description": "OCD__wix_browser_sess_description", + "retention": "OCD_retention_session", "dataController": "Wix.com", "gdprUrl": "https://support.wix.com/erticle/cookies-and-your-wix-site", "wildcard": "0" @@ -8993,8 +9629,8 @@ "category": "Functional", "name": "consent-policy", "domain": "", - "description": "Used for cookie banner parameters", - "retention": "12 months", + "description": "OCD_consent_policy_description", + "retention": "OCD_retention_12_months", "dataController": "Wix.com", "gdprUrl": "https://support.wix.com/erticle/cookies-and-your-wix-site", "wildcard": "0" @@ -9006,8 +9642,8 @@ "category": "Functional", "name": "_ab", "domain": "shopify.com", - "description": "Used in connection with access to admin.", - "retention": "session", + "description": "OCD__ab_description", + "retention": "OCD_retention_session", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9019,8 +9655,8 @@ "category": "Functional", "name": "_secure_session_id", "domain": "shopify.com", - "description": "Used in connection with navigation through a storefront.", - "retention": "session", + "description": "OCD__secure_session_id_description", + "retention": "OCD_retention_session", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9032,8 +9668,8 @@ "category": "Functional", "name": "Cart", "domain": "shopify.com", - "description": "Used in connection with shopping cart.", - "retention": "14 days", + "description": "OCD_Cart_description", + "retention": "OCD_retention_14_days", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9045,8 +9681,8 @@ "category": "Functional", "name": "cart_sig", "domain": "shopify.com", - "description": "Used in connection with shopping cart.", - "retention": "14 days", + "description": "OCD_cart_sig_description", + "retention": "OCD_retention_14_days", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9058,8 +9694,8 @@ "category": "Functional", "name": "cart_ts", "domain": "shopify.com", - "description": "Used in connection with checkout.", - "retention": "14 days", + "description": "OCD_cart_ts_description", + "retention": "OCD_retention_14_days", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9071,8 +9707,8 @@ "category": "Functional", "name": "checkout_token", "domain": "shopify.com", - "description": "Used in connection with checkout.", - "retention": "14 days", + "description": "OCD_checkout_token_description", + "retention": "OCD_retention_14_days", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9084,8 +9720,8 @@ "category": "Functional", "name": "Secret", "domain": "shopify.com", - "description": "Used in connection with checkout.", - "retention": "14 days", + "description": "OCD_Secret_description", + "retention": "OCD_retention_14_days", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9097,8 +9733,8 @@ "category": "Functional", "name": "Secure_customer_sig", "domain": "shopify.com", - "description": "Used in connection with customer login.", - "retention": "2 years", + "description": "OCD_Secure_customer_sig_description", + "retention": "OCD_retention_2_years", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9110,8 +9746,8 @@ "category": "Functional", "name": "storefront_digest", "domain": "shopify.com", - "description": "Used in connection with customer login.", - "retention": "2 years", + "description": "OCD_storefront_digest_description", + "retention": "OCD_retention_2_years", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9123,8 +9759,8 @@ "category": "Functional", "name": "_shopify_u", "domain": "shopify.com", - "description": "Used to facilitate updating customer account information.", - "retention": "2 years", + "description": "OCD__shopify_u_description", + "retention": "OCD_retention_2_years", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9136,8 +9772,8 @@ "category": "Marketing", "name": "_tracking_consent", "domain": "shopify.com", - "description": "Tracking preferences.", - "retention": "2 years", + "description": "OCD__tracking_consent_description", + "retention": "OCD_retention_2_years", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9149,8 +9785,8 @@ "category": "Marketing", "name": "_landing_page", "domain": "shopify.com", - "description": "Track landing pages.", - "retention": "2 years", + "description": "OCD__landing_page_description", + "retention": "OCD_retention_2_years", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9162,8 +9798,8 @@ "category": "Marketing", "name": "_orig_referrer", "domain": "shopify.com", - "description": "Track landing pages.", - "retention": "2 years", + "description": "OCD__orig_referrer_description", + "retention": "OCD_retention_2_years", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9175,8 +9811,8 @@ "category": "Analytics", "name": "_s", "domain": "shopify.com", - "description": "Shopify analytics.", - "retention": "2 years", + "description": "OCD__s_description", + "retention": "OCD_retention_2_years", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9188,8 +9824,8 @@ "category": "Analytics", "name": "_shopify_fs", "domain": "shopify.com", - "description": "Shopify analytics.", - "retention": "2 years", + "description": "OCD__shopify_fs_description", + "retention": "OCD_retention_2_years", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9201,8 +9837,8 @@ "category": "Analytics", "name": "_shopify_s", "domain": "shopify.com", - "description": "Shopify analytics.", - "retention": "2 years", + "description": "OCD__shopify_s_description", + "retention": "OCD_retention_2_years", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9214,8 +9850,8 @@ "category": "Marketing", "name": "_shopify_sa_p", "domain": "shopify.com", - "description": "Shopify analytics relating to marketing & referrals.", - "retention": "2 years", + "description": "OCD__shopify_sa_p_description", + "retention": "OCD_retention_2_years", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9227,8 +9863,8 @@ "category": "Marketing", "name": "_shopify_sa_t", "domain": "shopify.com", - "description": "Shopify analytics relating to marketing & referrals.", - "retention": "2 years", + "description": "OCD__shopify_sa_t_description", + "retention": "OCD_retention_2_years", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9240,8 +9876,8 @@ "category": "Analytics", "name": "_shopify_uniq", "domain": "shopify.com", - "description": "Shopify analytics.", - "retention": "2 years", + "description": "OCD__shopify_uniq_description", + "retention": "OCD_retention_2_years", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9253,8 +9889,8 @@ "category": "Analytics", "name": "_shopify_visit", "domain": "shopify.com", - "description": "Shopify analytics.", - "retention": "2 years", + "description": "OCD__shopify_visit_description", + "retention": "OCD_retention_2_years", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9266,8 +9902,8 @@ "category": "Analytics", "name": "_shopify_y", "domain": "shopify.com", - "description": "Shopify analytics.", - "retention": "2 years", + "description": "OCD__shopify_y_description", + "retention": "OCD_retention_2_years", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9279,8 +9915,8 @@ "category": "Analytics", "name": "_y", "domain": "shopify.com", - "description": "Shopify analytics.", - "retention": "2 years", + "description": "OCD__y_description", + "retention": "OCD_retention_2_years", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9292,8 +9928,8 @@ "category": "Analytics", "name": "tracked_start_checkout", "domain": "shopify.com", - "description": "Shopify analytics relating to checkout.", - "retention": "2 years", + "description": "OCD_tracked_start_checkout_description", + "retention": "OCD_retention_2_years", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9305,8 +9941,8 @@ "category": "Analytics", "name": "ki_r", "domain": "shopify.com", - "description": "Shopify analytics.", - "retention": "2 years", + "description": "OCD_ki_r_description", + "retention": "OCD_retention_2_years", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9329,8 +9965,8 @@ "category": "Analytics", "name": "ki_t", "domain": "shopify.com", - "description": "Shopify analytics.", - "retention": "2 years", + "description": "OCD_ki_t_description", + "retention": "OCD_retention_2_years", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9353,8 +9989,8 @@ "category": "Analytics", "name": "_Brochure_session", "domain": "shopify.com", - "description": "Used in connection with browsing through site.", - "retention": "2 years", + "description": "OCD__Brochure_session_description", + "retention": "OCD_retention_2_years", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9366,8 +10002,8 @@ "category": "Functional", "name": "shopify_pay_redirect", "domain": "shopify.com", - "description": "Used in connection with checkout.", - "retention": "30 minutes, 3w or 1y depending on value", + "description": "OCD_shopify_pay_redirect_description", + "retention": "OCD_retention_30_minutes__3w_or_1y_depending_on_value", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9379,8 +10015,8 @@ "category": "Functional", "name": "cart_currency", "domain": "shopify.com", - "description": "Set after a checkout is completed to ensure that new carts are in the same currency as the last checkout.", - "retention": "14 days", + "description": "OCD_cart_currency_description", + "retention": "OCD_retention_14_days", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9392,8 +10028,8 @@ "category": "Functional", "name": "dynamic_checkout_shown_on_cart", "domain": "shopify.com", - "description": "Used in connection with checkout.", - "retention": "30 minutes", + "description": "OCD_dynamic_checkout_shown_on_cart_description", + "retention": "OCD_retention_30_minutes", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9405,8 +10041,8 @@ "category": "Functional", "name": "keep_alive", "domain": "shopify.com", - "description": "Used in connection with buyer localization.", - "retention": "14 weeks", + "description": "OCD_keep_alive_description", + "retention": "OCD_retention_14_weeks", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9418,8 +10054,8 @@ "category": "Functional", "name": "checkout_session_token*", "domain": "", - "description": "Used in connection with checkout.", - "retention": "3 weeks", + "description": "OCD_checkout_session_token__description", + "retention": "OCD_retention_3_weeks", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "1" @@ -9431,8 +10067,8 @@ "category": "Functional", "name": "checkout_session_lookup", "domain": "", - "description": "Used in connection with checkout.", - "retention": "3 weeks", + "description": "OCD_checkout_session_lookup_description", + "retention": "OCD_retention_3_weeks", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9444,8 +10080,8 @@ "category": "Functional", "name": "cart_ver", "domain": "", - "description": "Used in connection with shopping cart.", - "retention": "2 weeks", + "description": "OCD_cart_ver_description", + "retention": "OCD_retention_2_weeks", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9457,8 +10093,8 @@ "category": "Functional", "name": "localization", "domain": "", - "description": "Used in connection with checkout.", - "retention": "2 weeks", + "description": "OCD_localization_description", + "retention": "OCD_retention_2_weeks", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9470,8 +10106,8 @@ "category": "Functional", "name": "locale_bar_accepted", "domain": "", - "description": "This cookie is provided by app (BEST Currency Converter) and is used to secure currency chosen by the customer.", - "retention": "session", + "description": "OCD_locale_bar_accepted_description", + "retention": "OCD_retention_session", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9483,8 +10119,8 @@ "category": "Functional", "name": "_cmp_a", "domain": "", - "description": "Used for managing customer privacy settings.", - "retention": "1 day", + "description": "OCD__cmp_a_description", + "retention": "OCD_retention_1_day", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9496,8 +10132,8 @@ "category": "Functional", "name": "_shopify_country", "domain": "", - "description": "For shops where pricing currency/country set from GeoIP, that cookie stores the country we've detected. This cookie helps avoid doing GeoIP lookups after the first request.", - "retention": "session", + "description": "OCD__shopify_country_description", + "retention": "OCD_retention_session", "dataController": "Shopify.com", "gdprUrl": "https://www.shopify.com/legal/cookies", "wildcard": "0" @@ -9509,8 +10145,8 @@ "category": "Functional", "name": "__hs_opt_out", "domain": "hubspot.com", - "description": "This cookie is used by the opt-in privacy policy to remember not to ask the visitor to accept cookies again.", - "retention": "13 months", + "description": "OCD___hs_opt_out_description", + "retention": "OCD_retention_13_months", "dataController": "HubSpot", "gdprUrl": "https://legal.hubspot.com/privacy-policy", "wildcard": "0" @@ -9522,8 +10158,8 @@ "category": "Functional", "name": "__hs_do_not_track", "domain": "hubspot.com", - "description": "This cookie can be set to prevent the tracking code from sending any information to HubSpot.", - "retention": "13 months", + "description": "OCD___hs_do_not_track_description", + "retention": "OCD_retention_13_months", "dataController": "HubSpot", "gdprUrl": "https://legal.hubspot.com/privacy-policy", "wildcard": "0" @@ -9535,8 +10171,8 @@ "category": "Functional", "name": "__hs_initial_opt_in", "domain": "hubspot.com", - "description": "This cookie is used to prevent the banner from always displaying when visitors are browsing in strict mode.", - "retention": "7 days", + "description": "OCD___hs_initial_opt_in_description", + "retention": "OCD_retention_7_days", "dataController": "HubSpot", "gdprUrl": "https://legal.hubspot.com/privacy-policy", "wildcard": "0" @@ -9548,8 +10184,8 @@ "category": "Functional", "name": "hs_ab_test", "domain": "hubspot.com", - "description": "This cookie is used to consistently serve visitors the same version of an A/B test page they’ve seen before.", - "retention": "session", + "description": "OCD_hs_ab_test_description", + "retention": "OCD_retention_session", "dataController": "HubSpot", "gdprUrl": "https://legal.hubspot.com/privacy-policy", "wildcard": "0" @@ -9561,8 +10197,8 @@ "category": "Functional", "name": "hs-messages-is-open", "domain": "hubspot.com", - "description": "This cookie is used to determine and save whether the chat widget is open for future visits.", - "retention": "30 minutes", + "description": "OCD_hs_messages_is_open_description", + "retention": "OCD_retention_30_minutes", "dataController": "HubSpot", "gdprUrl": "https://legal.hubspot.com/privacy-policy", "wildcard": "0" @@ -9574,8 +10210,8 @@ "category": "Functional", "name": "hs-messages-hide-welcome-message", "domain": "hubspot.com", - "description": "This cookie is used to prevent the chat widget welcome message from appearing again for one day after it is dismissed.", - "retention": "1 day", + "description": "OCD_hs_messages_hide_welcome_message_description", + "retention": "OCD_retention_1_day", "dataController": "HubSpot", "gdprUrl": "https://legal.hubspot.com/privacy-policy", "wildcard": "0" @@ -9587,8 +10223,8 @@ "category": "Functional", "name": "__hsmem", "domain": "hubspot.com", - "description": "This cookie is set when visitors log in to a HubSpot-hosted site.", - "retention": "1 year", + "description": "OCD___hsmem_description", + "retention": "OCD_retention_1_year", "dataController": "HubSpot", "gdprUrl": "https://legal.hubspot.com/privacy-policy", "wildcard": "0" @@ -9600,8 +10236,8 @@ "category": "Functional", "name": "hs-membership-csrf", "domain": "hubspot.com", - "description": "This cookie is used to ensure that content membership logins cannot be forged.", - "retention": "session", + "description": "OCD_hs_membership_csrf_description", + "retention": "OCD_retention_session", "dataController": "HubSpot", "gdprUrl": "https://legal.hubspot.com/privacy-policy", "wildcard": "0" @@ -9613,8 +10249,8 @@ "category": "Functional", "name": "hs_langswitcher_choice", "domain": "hubspot.com", - "description": "This cookie is used to save the visitor's selected language choice when viewing pages in multiple languages.", - "retention": "2 years", + "description": "OCD_hs_langswitcher_choice_description", + "retention": "OCD_retention_2_years", "dataController": "HubSpot", "gdprUrl": "https://legal.hubspot.com/privacy-policy", "wildcard": "0" @@ -9626,8 +10262,8 @@ "category": "Marketing", "name": "__hstc", "domain": "hubspot.com", - "description": "The main cookie for tracking visitors.", - "retention": "13 months", + "description": "OCD___hstc_description", + "retention": "OCD_retention_13_months", "dataController": "HubSpot", "gdprUrl": "https://legal.hubspot.com/privacy-policy", "wildcard": "0" @@ -9639,8 +10275,8 @@ "category": "Marketing", "name": "hubspotutk", "domain": "hubspot.com", - "description": "This cookie keeps track of a visitor's identity. It is passed to HubSpot on form submission and used when deduplicating contacts.", - "retention": "13 months", + "description": "OCD_hubspotutk_description", + "retention": "OCD_retention_13_months", "dataController": "HubSpot", "gdprUrl": "https://legal.hubspot.com/privacy-policy", "wildcard": "0" @@ -9652,8 +10288,8 @@ "category": "Marketing", "name": "__hssc", "domain": "hubspot.com", - "description": "This cookie keeps track of sessions.", - "retention": "30 minutes", + "description": "OCD___hssc_description", + "retention": "OCD_retention_30_minutes", "dataController": "HubSpot", "gdprUrl": "https://legal.hubspot.com/privacy-policy", "wildcard": "0" @@ -9665,8 +10301,8 @@ "category": "Marketing", "name": "__hssrc", "domain": "hubspot.com", - "description": "Whenever HubSpot changes the session cookie, this cookie is also set to determine if the visitor has restarted their browser.", - "retention": "session", + "description": "OCD___hssrc_description", + "retention": "OCD_retention_session", "dataController": "HubSpot", "gdprUrl": "https://legal.hubspot.com/privacy-policy", "wildcard": "0" @@ -9678,8 +10314,8 @@ "category": "Marketing", "name": "messagesUtk", "domain": "hubspot.com", - "description": "This cookie is used to recognize visitors who chat with you via the chatflows tool. If the visitor leaves your site before they're added as a contact, they will have this cookie associated with their browser.", - "retention": "13 months", + "description": "OCD_messagesUtk_description", + "retention": "OCD_retention_13_months", "dataController": "HubSpot", "gdprUrl": "https://legal.hubspot.com/privacy-policy", "wildcard": "0" @@ -9691,8 +10327,8 @@ "category": "Marketing", "name": "hubspotapi", "domain": "hubspot.com", - "description": "This cookie allows the user to access the app with the correct permissions.", - "retention": "7 days", + "description": "OCD_hubspotapi_description", + "retention": "OCD_retention_7_days", "dataController": "HubSpot", "gdprUrl": "https://legal.hubspot.com/privacy-policy", "wildcard": "0" @@ -9704,8 +10340,8 @@ "category": "Functional", "name": "hubspotapi-prefs", "domain": "hubspot.com", - "description": "This is used with the hubspotapi cookie to remember whether the user checked the 'remember me' box (controls the expiration of the main cookie's authentication).", - "retention": "1 Year", + "description": "OCD_hubspotapi_prefs_description", + "retention": "OCD_retention_1_Year", "dataController": "HubSpot", "gdprUrl": "https://legal.hubspot.com/privacy-policy", "wildcard": "0" @@ -9717,8 +10353,47 @@ "category": "Functional", "name": "hubspotapi-csrf", "domain": "hubspot.com", - "description": "This is used for CSRF prevention - preventing third party websites from accessing your data. Expires after a year.", - "retention": "1 year", + "description": "OCD_hubspotapi_csrf_description", + "retention": "OCD_retention_1_year", + "dataController": "HubSpot", + "gdprUrl": "https://legal.hubspot.com/privacy-policy", + "wildcard": "0" + } + ], + "__hs_cookie_cat_pref": [ + { + "platform": "HubSpot", + "category": "Functional", + "name": "__hs_cookie_cat_pref", + "domain": "", + "description": "OCD___hs_cookie_cat_pref_description", + "retention": "OCD_retention_6_months", + "dataController": "HubSpot", + "gdprUrl": "https://legal.hubspot.com/privacy-policy", + "wildcard": "0" + } + ], + "__hs_gpc_banner_dismiss": [ + { + "platform": "HubSpot", + "category": "Functional", + "name": "__hs_gpc_banner_dismiss", + "domain": "", + "description": "OCD___hs_gpc_banner_dismiss_description", + "retention": "OCD_retention_180_days", + "dataController": "HubSpot", + "gdprUrl": "https://legal.hubspot.com/privacy-policy", + "wildcard": "0" + } + ], + "__hs_notify_banner_dismiss": [ + { + "platform": "HubSpot", + "category": "Functional", + "name": "__hs_notify_banner_dismiss", + "domain": "", + "description": "OCD___hs_notify_banner_dismiss_description", + "retention": "OCD_retention_180_days", "dataController": "HubSpot", "gdprUrl": "https://legal.hubspot.com/privacy-policy", "wildcard": "0" @@ -9730,8 +10405,8 @@ "category": "Analytics", "name": "vuid", "domain": "vimeo.com", - "description": "This first party cookie created by Vimeo is used to assign a Vimeo Analytics unique id.", - "retention": "1 minute", + "description": "OCD_vuid_description", + "retention": "OCD_retention_1_minute", "dataController": "Vimeo", "gdprUrl": "https://vimeo.com/cookie_policy", "wildcard": "0" @@ -9743,8 +10418,8 @@ "category": "Functional", "name": "Player", "domain": "vimeo.com", - "description": "This first party cookie created by Vimeo is used to remember user’s player mode preferences.", - "retention": "1 minute", + "description": "OCD_Player_description", + "retention": "OCD_retention_1_minute", "dataController": "Vimeo", "gdprUrl": "https://vimeo.com/cookie_policy", "wildcard": "0" @@ -9756,8 +10431,8 @@ "category": "Functional", "name": "continuous_play_v3", "domain": "vimeo.com", - "description": "Used to keep track of whether continuous play is on or not for a user", - "retention": "2 years", + "description": "OCD_continuous_play_v3_description", + "retention": "OCD_retention_2_years", "dataController": "Vimeo", "gdprUrl": "https://vimeo.com/cookie_policy", "wildcard": "0" @@ -9769,8 +10444,8 @@ "category": "Analytics", "name": "sd_identity", "domain": "vimeo.com", - "description": "Collects analytical tracking information about videos and enables the player to function properly.", - "retention": "2 years", + "description": "OCD_sd_identity_description", + "retention": "OCD_retention_2_years", "dataController": "Vimeo", "gdprUrl": "https://vimeo.com/cookie_policy", "wildcard": "0" @@ -9782,8 +10457,8 @@ "category": "Analytics", "name": "sd_client_id", "domain": "vimeo.com", - "description": "Collects analytical tracking information about videos and enables the player to function properly.", - "retention": "2 years", + "description": "OCD_sd_client_id_description", + "retention": "OCD_retention_2_years", "dataController": "Vimeo", "gdprUrl": "https://vimeo.com/cookie_policy", "wildcard": "0" @@ -9795,8 +10470,8 @@ "category": "Functional", "name": "__stripe_mid", "domain": "stripe.com", - "description": "Fraud prevention and detection", - "retention": "1 year", + "description": "OCD___stripe_mid_description", + "retention": "OCD_retention_1_year", "dataController": "Stripe", "gdprUrl": "https://stripe.com/en-nl/privacy", "wildcard": "0" @@ -9808,8 +10483,8 @@ "category": "Functional", "name": "__stripe_sid", "domain": "stripe.com", - "description": "Fraud prevention and detection", - "retention": "30 minutes", + "description": "OCD___stripe_sid_description", + "retention": "OCD_retention_30_minutes", "dataController": "Stripe", "gdprUrl": "https://stripe.com/en-nl/privacy", "wildcard": "0" @@ -9821,8 +10496,8 @@ "category": "Functional", "name": "m", "domain": "m.stripe.com", - "description": "Set by payment provider stripe.com to process payments", - "retention": "10 years", + "description": "OCD_m_description", + "retention": "OCD_retention_10_years", "dataController": "Stripe", "gdprUrl": "https://stripe.com/en-nl/privacy", "wildcard": "0" @@ -9834,8 +10509,8 @@ "category": "Functional", "name": "_gat_pro", "domain": "snapwidget.com", - "description": "Allows Snapwidget to offer anonymous analytics about how the visitors are using your widgets", - "retention": "24 hours", + "description": "OCD__gat_pro_description", + "retention": "OCD_retention_24_hours", "dataController": "Snapwidget", "gdprUrl": "https://snapwidget.com/privacy", "wildcard": "0" @@ -9847,8 +10522,8 @@ "category": "Functional", "name": "woocommerce_cart_hash", "domain": "woocommerce.com", - "description": "Helps WooCommerce determine when cart contents/data changes.", - "retention": "session", + "description": "OCD_woocommerce_cart_hash_description", + "retention": "OCD_retention_session", "dataController": "WooCommerce", "gdprUrl": "https://automattic.com/privacy/", "wildcard": "0" @@ -9860,8 +10535,8 @@ "category": "Functional", "name": "woocommerce_items_in_cart", "domain": "woocommerce.com", - "description": "Helps WooCommerce determine when cart contents/data changes.", - "retention": "session", + "description": "OCD_woocommerce_items_in_cart_description", + "retention": "OCD_retention_session", "dataController": "WooCommerce", "gdprUrl": "https://automattic.com/privacy/", "wildcard": "0" @@ -9873,8 +10548,8 @@ "category": "Functional", "name": "wp_woocommerce_session_*", "domain": "woocommerce.com", - "description": "Contains a unique code for each customer so that it knows where to find the cart data in the database for each customer.", - "retention": "2 days", + "description": "OCD_wp_woocommerce_session___description", + "retention": "OCD_retention_2_days", "dataController": "WooCommerce", "gdprUrl": "https://automattic.com/privacy/", "wildcard": "1" @@ -9886,8 +10561,8 @@ "category": "Functional", "name": "woocommerce_recently_viewed", "domain": "woocommerce.com", - "description": "Powers the Recent Viewed Products widget", - "retention": "session", + "description": "OCD_woocommerce_recently_viewed_description", + "retention": "OCD_retention_session", "dataController": "WooCommerce", "gdprUrl": "https://automattic.com/privacy/", "wildcard": "0" @@ -9899,8 +10574,8 @@ "category": "Functional", "name": "store_notice*", "domain": "", - "description": "Allows customers to dismiss the Store Notice.", - "retention": "session", + "description": "OCD_store_notice__description", + "retention": "OCD_retention_session", "dataController": "WooCommerce", "gdprUrl": "https://automattic.com/privacy/", "wildcard": "1" @@ -9912,8 +10587,8 @@ "category": "Functional", "name": "woocommerce_snooze_suggestions__*", "domain": "", - "description": "Allows dashboard users to dismiss Marketplace suggestions, if enabled.", - "retention": "2 days", + "description": "OCD_woocommerce_snooze_suggestions____description", + "retention": "OCD_retention_2_days", "dataController": "WooCommerce", "gdprUrl": "https://automattic.com/privacy/", "wildcard": "1" @@ -9925,8 +10600,8 @@ "category": "Functional", "name": "woocommerce_dismissed_suggestions__*", "domain": "", - "description": "Count of suggestion dismissals, if enabled.", - "retention": "1 month", + "description": "OCD_woocommerce_dismissed_suggestions____description", + "retention": "OCD_retention_1_month", "dataController": "WooCommerce", "gdprUrl": "https://automattic.com/privacy/", "wildcard": "1" @@ -9938,8 +10613,8 @@ "category": "Functional", "name": "tk_ai", "domain": "", - "description": "Stores a randomly-generated anonymous ID. This is only used within the dashboard (/wp-admin) area and is used for usage tracking, if enabled.", - "retention": "session", + "description": "OCD_tk_ai_description", + "retention": "OCD_retention_session", "dataController": "WooCommerce / Jetpack", "gdprUrl": "https://automattic.com/privacy/", "wildcard": "0" @@ -9951,8 +10626,8 @@ "category": "Analytics", "name": "sbjs_session", "domain": "", - "description": "The number of page views in this session and the current page path", - "retention": "30 minutes", + "description": "OCD_sbjs_session_description", + "retention": "OCD_retention_30_minutes", "dataController": "WooCommerce", "gdprUrl": "https://automattic.com/privacy/", "wildcard": "0" @@ -9964,8 +10639,8 @@ "category": "Analytics", "name": "sbjs_udata", "domain": "", - "description": "Information about the visitor’s user agent, such as IP, the browser, and the device type", - "retention": "session", + "description": "OCD_sbjs_udata_description", + "retention": "OCD_retention_session", "dataController": "WooCommerce", "gdprUrl": "https://automattic.com/privacy/", "wildcard": "0" @@ -9977,8 +10652,8 @@ "category": "Analytics", "name": "sbjs_first", "domain": "", - "description": "Traffic origin information for the visitor’s first visit to your store (only applicable if the visitor returns before the session expires)", - "retention": "session", + "description": "OCD_sbjs_first_description", + "retention": "OCD_retention_session", "dataController": "WooCommerce", "gdprUrl": "https://automattic.com/privacy/", "wildcard": "0" @@ -9990,8 +10665,8 @@ "category": "Analytics", "name": "sbjs_current", "domain": "", - "description": "Traffic origin information for the visitor’s current visit to your store", - "retention": "session", + "description": "OCD_sbjs_current_description", + "retention": "OCD_retention_session", "dataController": "WooCommerce", "gdprUrl": "https://automattic.com/privacy/", "wildcard": "0" @@ -10003,8 +10678,8 @@ "category": "Analytics", "name": "sbjs_first_add", "domain": "", - "description": "Timestamp, referring URL, and entry page for your visitor’s first visit to your store (only applicable if the visitor returns before the session expires)", - "retention": "session", + "description": "OCD_sbjs_first_add_description", + "retention": "OCD_retention_session", "dataController": "WooCommerce", "gdprUrl": "https://automattic.com/privacy/", "wildcard": "0" @@ -10016,8 +10691,8 @@ "category": "Analytics", "name": "sbjs_current_add", "domain": "", - "description": "Timestamp, referring URL, and entry page for your visitor’s current visit to your store", - "retention": "session", + "description": "OCD_sbjs_current_add_description", + "retention": "OCD_retention_session", "dataController": "WooCommerce", "gdprUrl": "https://automattic.com/privacy/", "wildcard": "0" @@ -10029,8 +10704,8 @@ "category": "Analytics", "name": "sbjs_migrations", "domain": "", - "description": "Technical data to help with migrations between different versions of the tracking feature", - "retention": "session", + "description": "OCD_sbjs_migrations_description", + "retention": "OCD_retention_session", "dataController": "WooCommerce", "gdprUrl": "https://automattic.com/privacy/", "wildcard": "0" @@ -10042,8 +10717,8 @@ "category": "Marketing", "name": "edgebucket", "domain": "reddit.com", - "description": "Used by Reddit to deliver advertising", - "retention": "2 years", + "description": "OCD_edgebucket_description", + "retention": "OCD_retention_2_years", "dataController": "Reddit", "gdprUrl": "https://www.redditinc.com/policies/privacy-policy", "wildcard": "0" @@ -10055,8 +10730,8 @@ "category": "Marketing", "name": "initref", "domain": "reddit.com", - "description": "Used by Reddit to deliver advertising", - "retention": "session", + "description": "OCD_initref_description", + "retention": "OCD_retention_session", "dataController": "Reddit", "gdprUrl": "https://www.redditinc.com/policies/privacy-policy", "wildcard": "0" @@ -10068,8 +10743,8 @@ "category": "Functional", "name": "incap_ses_*", "domain": "", - "description": "This cookie is set to allow a visitor to receive site content from one out of multiple servers as the visitor browses the site. This allows the visitor's session to be maintained.", - "retention": "session", + "description": "OCD_incap_ses___description", + "retention": "OCD_retention_session", "dataController": "Imperva", "gdprUrl": "https://www.imperva.com/legal/privacy-policy/", "wildcard": "1" @@ -10081,8 +10756,8 @@ "category": "Functional", "name": "nlbi_*", "domain": "", - "description": "Incapsula DDoS Protection and Web Application Firewall: Load balancing cookie. To ensure requests by a client are sent to the same origin server.", - "retention": "session", + "description": "OCD_nlbi___description", + "retention": "OCD_retention_session", "dataController": "Imperva", "gdprUrl": "https://www.imperva.com/legal/privacy-policy/", "wildcard": "1" @@ -10094,8 +10769,8 @@ "category": "Functional", "name": "visid_incap_*", "domain": "", - "description": "This cookie is from the incapsula CDN and helps us with reliability, security and the performance of our site.", - "retention": "1 year", + "description": "OCD_visid_incap___description", + "retention": "OCD_retention_1_year", "dataController": "Imperva", "gdprUrl": "https://www.imperva.com/legal/privacy-policy/", "wildcard": "1" @@ -10107,8 +10782,8 @@ "category": "Functional", "name": "sp_t", "domain": "spotify.com", - "description": "Required to ensure the functionality of the integrated Spotify plugin. This does not result in any cross-site functionality.", - "retention": "2 months", + "description": "OCD_sp_t_description", + "retention": "OCD_retention_2_months", "dataController": "Spotify", "gdprUrl": "https://www.spotify.com/us/privacy/", "wildcard": "0" @@ -10120,8 +10795,8 @@ "category": "Functional", "name": "sp_landing", "domain": "spotify.com", - "description": "Required to ensure the functionality of the integrated Spotify plugin. This does not result in any cross-site functionality.", - "retention": "1 day", + "description": "OCD_sp_landing_description", + "retention": "OCD_retention_1_day", "dataController": "Spotify", "gdprUrl": "https://www.spotify.com/us/privacy/", "wildcard": "0" @@ -10133,8 +10808,8 @@ "category": "Marketing", "name": "anj", "domain": "adnxs.com", - "description": "The anj cookie contains data denoting whether a cookie ID is synced with our partners. ID syncing enables our partners to use their data from outside the Platform on the Platform. ", - "retention": "90 days", + "description": "OCD_anj_description", + "retention": "OCD_retention_90_days", "dataController": "Xandr", "gdprUrl": "https://about.ads.microsoft.com/en-us/solutions/xandr/platform-privacy-policy", "wildcard": "0" @@ -10146,8 +10821,8 @@ "category": "Marketing", "name": "uuid2", "domain": "adnxs.com", - "description": "This cookie contains a unique randomly-generated value that enables the Platform to distinguish browsers and devices.", - "retention": "90 days", + "description": "OCD_uuid2_description", + "retention": "OCD_retention_90_days", "dataController": "Xandr", "gdprUrl": "https://about.ads.microsoft.com/en-us/solutions/xandr/platform-privacy-policy", "wildcard": "0" @@ -10159,8 +10834,8 @@ "category": "Analytics", "name": "usersync", "domain": ".adnxs.com", - "description": "This cookie contains data denoting whether a cookie ID is synced with our partners. ID syncing enables our partners to use their data from outside the Platform on the Platform.", - "retention": "90 days", + "description": "OCD_usersync_description", + "retention": "OCD_retention_90_days", "dataController": "Xandr", "gdprUrl": "https://about.ads.microsoft.com/en-us/solutions/xandr/platform-privacy-policy", "wildcard": "0" @@ -10172,8 +10847,8 @@ "category": "Marketing", "name": "icu", "domain": ".adnxs.com", - "description": "This cookie is used to select ads and limit the number of times a user sees a particular ad. It contains information such as the number of times an ad has been shown, how recently an ad has been shown, or how many total ads have been shown.", - "retention": "90 days", + "description": "OCD_icu_description", + "retention": "OCD_retention_90_days", "dataController": "Xandr", "gdprUrl": "https://about.ads.microsoft.com/en-us/solutions/xandr/platform-privacy-policy", "wildcard": "0" @@ -10185,8 +10860,8 @@ "category": "Analytics", "name": "pses", "domain": "", - "description": "This cookie is used to measure the time a user spends on a site.", - "retention": "Session", + "description": "OCD_pses_description", + "retention": "OCD_retention_Session", "dataController": "Xandr", "gdprUrl": "https://about.ads.microsoft.com/en-us/solutions/xandr/platform-privacy-policy", "wildcard": "0" @@ -10198,8 +10873,8 @@ "category": "Functional", "name": "sess", "domain": ".adnxs.com", - "description": "The sess cookie contains a single non-unique value: “1”.It is used by the Platform to test whether a browser is configured to accept cookies from Xandr.", - "retention": "Session", + "description": "OCD_sess_description", + "retention": "OCD_retention_Session", "dataController": "Xandr", "gdprUrl": "https://about.ads.microsoft.com/en-us/solutions/xandr/platform-privacy-policy", "wildcard": "0" @@ -10211,8 +10886,8 @@ "category": "Marketing", "name": "XANDR_PANID", "domain": ".adnxs.com", - "description": "This cookie registers data on the visitor. The information is used to optimize advertisement relevance.", - "retention": "400 days", + "description": "OCD_XANDR_PANID_description", + "retention": "OCD_retention_400_days", "dataController": "Xandr", "gdprUrl": "https://about.ads.microsoft.com/en-us/solutions/xandr/platform-privacy-policy", "wildcard": "0" @@ -10224,8 +10899,8 @@ "category": "Functional", "name": "cc-*", "domain": "", - "description": "References a cart for anonymous users", - "retention": "session", + "description": "OCD_cc___description", + "retention": "OCD_retention_session", "dataController": "Intershop", "gdprUrl": "https://www.intershop.com/en/privacy-policy", "wildcard": "1" @@ -10237,8 +10912,8 @@ "category": "Functional", "name": "pgid-org-*", "domain": "", - "description": "Hash of personalization information. Used to cache pages or snippets for users with same personalization information", - "retention": "session", + "description": "OCD_pgid_org___description", + "retention": "OCD_retention_session", "dataController": "Intershop", "gdprUrl": "https://www.intershop.com/en/privacy-policy", "wildcard": "1" @@ -10250,8 +10925,8 @@ "category": "Functional", "name": "SecureSessionID-*", "domain": "", - "description": "Reference to authenticated user", - "retention": "session", + "description": "OCD_SecureSessionID___description", + "retention": "OCD_retention_session", "dataController": "Intershop", "gdprUrl": "https://www.intershop.com/en/privacy-policy", "wildcard": "1" @@ -10263,8 +10938,8 @@ "category": "Functional", "name": "CMSCsrfCookie", "domain": "", - "description": "Store's a security token that the system uses to validate all form data submitted via POST requests. Helps protect against Cross site request forgery.", - "retention": "session", + "description": "OCD_CMSCsrfCookie_description", + "retention": "OCD_retention_session", "dataController": "Kentico", "gdprUrl": "https://xperience.io/policies/privacy-policy", "wildcard": "0" @@ -10276,8 +10951,8 @@ "category": "Functional", "name": "CMSCookieLevel", "domain": "", - "description": "Specifies which cookies are allowed by the visitor.", - "retention": "1 year", + "description": "OCD_CMSCookieLevel_description", + "retention": "OCD_retention_1_year", "dataController": "Kentico", "gdprUrl": "https://xperience.io/policies/privacy-policy", "wildcard": "0" @@ -10289,8 +10964,8 @@ "category": "Analytics", "name": "CMSLandingPageLoaded", "domain": "", - "description": "Indicates that the landing page has already been visited and the Landing page activity is not logged again for the current visitor. Expires after 20 minutes and the expiration period of the key is renewed every time the website is accessed again.", - "retention": "20 minutes", + "description": "OCD_CMSLandingPageLoaded_description", + "retention": "OCD_retention_20_minutes", "dataController": "Kentico", "gdprUrl": "https://xperience.io/policies/privacy-policy", "wildcard": "0" @@ -10302,8 +10977,8 @@ "category": "Functional", "name": "CMSPreferredCulture", "domain": "", - "description": "Stores the visitor's preferred content culture.", - "retention": "1 year", + "description": "OCD_CMSPreferredCulture_description", + "retention": "OCD_retention_1_year", "dataController": "Kentico", "gdprUrl": "https://xperience.io/policies/privacy-policy", "wildcard": "0" @@ -10315,8 +10990,8 @@ "category": "Analytics", "name": "CMSUserPage", "domain": "", - "description": "Stores the IDs (DocumentID, NodeID) of the last visited page. Used for logging landing and exit page web analytics and activities.", - "retention": "20 minutes", + "description": "OCD_CMSUserPage_description", + "retention": "OCD_retention_20_minutes", "dataController": "Kentico", "gdprUrl": "https://xperience.io/policies/privacy-policy", "wildcard": "0" @@ -10328,8 +11003,8 @@ "category": "Analytics", "name": "CurrentContact", "domain": "", - "description": "Stores the GUID of the contact related to the current site visitor. Used to track activities on the website.", - "retention": "50 years", + "description": "OCD_CurrentContact_description", + "retention": "OCD_retention_50_years", "dataController": "Kentico", "gdprUrl": "https://xperience.io/policies/privacy-policy", "wildcard": "0" @@ -10341,8 +11016,8 @@ "category": "Analytics", "name": "VisitorStatus", "domain": "", - "description": "Indicates if the visitor is new or returning. Used for tracking the visitors statistic in Web analytics.", - "retention": "20 years", + "description": "OCD_VisitorStatus_description", + "retention": "OCD_retention_20_years", "dataController": "Kentico", "gdprUrl": "https://xperience.io/policies/privacy-policy", "wildcard": "0" @@ -10354,8 +11029,8 @@ "category": "Marketing", "name": "sc_at", "domain": "snapchat.com", - "description": "Used to identify a visitor across multiple domains.", - "retention": "1 year", + "description": "OCD_sc_at_description", + "retention": "OCD_retention_1_year", "dataController": "Snapchat", "gdprUrl": "https://www.snap.com/en-US/privacy/privacy-policy/", "wildcard": "0" @@ -10367,8 +11042,8 @@ "category": "Marketing", "name": "sc-a-nonce", "domain": "snapchat.com", - "description": "Nonce control. Used to encrypt session data.", - "retention": "1 year", + "description": "OCD_sc_a_nonce_description", + "retention": "OCD_retention_1_year", "dataController": "Snapchat", "gdprUrl": "https://www.snap.com/en-US/privacy/privacy-policy/", "wildcard": "0" @@ -10380,8 +11055,8 @@ "category": "Marketing", "name": "_scid", "domain": "", - "description": "Used to help identify a visitor.", - "retention": "1 year", + "description": "OCD__scid_description", + "retention": "OCD_retention_1_year", "dataController": "Snapchat", "gdprUrl": "https://www.snap.com/en-US/privacy/privacy-policy/", "wildcard": "0" @@ -10393,8 +11068,8 @@ "category": "Marketing", "name": "_schn", "domain": "", - "description": "This cookies come from the Snapchat retargeting pixel. This pixel is used to retarget and attribute traffic coming from the social network.", - "retention": "1 day", + "description": "OCD__schn_description", + "retention": "OCD_retention_1_day", "dataController": "Snapchat", "gdprUrl": "https://www.snap.com/en-US/privacy/privacy-policy/", "wildcard": "0" @@ -10406,8 +11081,8 @@ "category": "Functional", "name": "X-AB", "domain": "", - "description": "This cookie is used by the website’s operator in context with multi-variate testing. This is a tool used to combine or change content on the website. This allows the website to find the best variation/edition of the site.", - "retention": "1 day", + "description": "OCD_X_AB_description", + "retention": "OCD_retention_1_day", "dataController": "Snapchat", "gdprUrl": "", "wildcard": "0" @@ -10419,8 +11094,8 @@ "category": "Marketing", "name": "_scid_r", "domain": "", - "description": "Sets a unique ID for the visitor, that allows third party advertisers to target the visitor with relevant advertisement. This pairing service is provided by third party advertisement hubs, which facilitates real-time bidding for advertisers.", - "retention": "13 months", + "description": "OCD__scid_r_description", + "retention": "OCD_retention_13_months", "dataController": "Snapchat", "gdprUrl": "https://www.snap.com/en-US/privacy/privacy-policy/", "wildcard": "0" @@ -10432,8 +11107,8 @@ "category": "Functional", "name": "_vwo_uuid_v2", "domain": "", - "description": "Used to track visitor movements anonymously.", - "retention": "1 year", + "description": "OCD__vwo_uuid_v2_description", + "retention": "OCD_retention_1_year", "dataController": "Visual Website Optimizer", "gdprUrl": "https://vwo.com/privacy-policy/", "wildcard": "0" @@ -10445,8 +11120,8 @@ "category": "Functional", "name": "_vwo_uuid", "domain": "", - "description": "Used to track visitor movements anonymously.", - "retention": "1 year", + "description": "OCD__vwo_uuid_description", + "retention": "OCD_retention_1_year", "dataController": "Visual Website Optimizer", "gdprUrl": "https://vwo.com/privacy-policy/", "wildcard": "0" @@ -10458,8 +11133,8 @@ "category": "Functional", "name": "_vis_opt_s", "domain": "", - "description": "This cookie detects if you are new or returning to a particular test.", - "retention": "100 days", + "description": "OCD__vis_opt_s_description", + "retention": "OCD_retention_100_days", "dataController": "Visual Website Optimizer", "gdprUrl": "https://vwo.com/privacy-policy/", "wildcard": "0" @@ -10471,8 +11146,8 @@ "category": "Functional", "name": "_vis_opt_test_cookie", "domain": "", - "description": "This is a temporary session cookie generated to detect if the cookies are enabled on the user browser or not.", - "retention": "100 days", + "description": "OCD__vis_opt_test_cookie_description", + "retention": "OCD_retention_100_days", "dataController": "Visual Website Optimizer", "gdprUrl": "https://vwo.com/privacy-policy/", "wildcard": "0" @@ -10484,8 +11159,8 @@ "category": "Functional", "name": "_vis_opt_exp_*", "domain": "", - "description": "This cookie is generated when a goal is created.", - "retention": "100 days", + "description": "OCD__vis_opt_exp___description", + "retention": "OCD_retention_100_days", "dataController": "Visual Website Optimizer", "gdprUrl": "https://vwo.com/privacy-policy/", "wildcard": "1" @@ -10497,8 +11172,8 @@ "category": "Analytics", "name": "_vwo_sn", "domain": "", - "description": "Collects statistics on the visitor's visits to the website, such as the number of visits, average time spent on the website and what pages have been read.", - "retention": "1 day", + "description": "OCD__vwo_sn_description", + "retention": "OCD_retention_1_day", "dataController": "Visual Website Optimizer", "gdprUrl": "https://vwo.com/privacy-policy/", "wildcard": "0" @@ -10510,8 +11185,8 @@ "category": "Analytics", "name": "_vwo_ds", "domain": "", - "description": "Collects data on the user's visits to the website, such as the number of visits, average time spent on the website and what pages have been loaded with the purpose of generating reports for optimising the website content.", - "retention": "2 months", + "description": "OCD__vwo_ds_description", + "retention": "OCD_retention_2_months", "dataController": "Visual Website Optimizer", "gdprUrl": "https://vwo.com/privacy-policy/", "wildcard": "0" @@ -10523,8 +11198,8 @@ "category": "Analytics", "name": "_vwo_referrer", "domain": "", - "description": "Registers data on visitors' website-behaviour. This is used for internal analysis and website optimization.", - "retention": "session", + "description": "OCD__vwo_referrer_description", + "retention": "OCD_retention_session", "dataController": "Visual Website Optimizer", "gdprUrl": "https://vwo.com/privacy-policy/", "wildcard": "0" @@ -10536,8 +11211,8 @@ "category": "Functional", "name": "_vwo_ssm", "domain": "dev.visualwebsiteoptimizer.com", - "description": "This cookie is used for testing and is created only on sites that use the HTTP protocol. This is used to check if VWO can create cookies on them, post which this cookie is deleted.", - "retention": "10 years", + "description": "OCD__vwo_ssm_description", + "retention": "OCD_retention_10_years", "dataController": "Visual Website Optimizer", "gdprUrl": "https://vwo.com/privacy-policy/", "wildcard": "0" @@ -10549,8 +11224,8 @@ "category": "Functional", "name": "zc_consent", "domain": "", - "description": "Determines whether the user has accepted the cookie consent box.", - "retention": "1 year", + "description": "OCD_zc_consent_description", + "retention": "OCD_retention_1_year", "dataController": "ZOHO", "gdprUrl": "https://www.zoho.com/privacy.html", "wildcard": "0" @@ -10562,8 +11237,8 @@ "category": "Functional", "name": "ZCAMPAIGN_CSRF_TOKEN", "domain": "", - "description": "This cookie is used to distinguish between humans and bots.", - "retention": "session", + "description": "OCD_ZCAMPAIGN_CSRF_TOKEN_description", + "retention": "OCD_retention_session", "dataController": "ZOHO", "gdprUrl": "https://www.zoho.com/privacy.html", "wildcard": "0" @@ -10575,8 +11250,8 @@ "category": "Marketing", "name": "zc_show", "domain": "", - "description": "Collects data on visitors' preferences and behaviour on the website - This information is used make content and advertisement more relevant to the specific visitor.", - "retention": "1 year", + "description": "OCD_zc_show_description", + "retention": "OCD_retention_1_year", "dataController": "ZOHO", "gdprUrl": "https://www.zoho.com/privacy.html", "wildcard": "0" @@ -10588,8 +11263,8 @@ "category": "Functional", "name": "zc_cu_exp", "domain": "", - "description": "Contains the expiration date for the cookie with its name.", - "retention": "1 year", + "description": "OCD_zc_cu_exp_description", + "retention": "OCD_retention_1_year", "dataController": "ZOHO", "gdprUrl": "https://www.zoho.com/privacy.html", "wildcard": "0" @@ -10601,8 +11276,8 @@ "category": "Marketing", "name": "zc_loc", "domain": "", - "description": "Collects information on user preferences and/or interaction with web-campaign content - This is used on CRM-campaign-platform used by website owners for promoting events or products.", - "retention": "session", + "description": "OCD_zc_loc_description", + "retention": "OCD_retention_session", "dataController": "ZOHO", "gdprUrl": "https://www.zoho.com/privacy.html", "wildcard": "0" @@ -10614,8 +11289,8 @@ "category": "Functional", "name": "uesign", "domain": "salesiq.zoho.com", - "description": "This cookie is used to manage the security of the applications.", - "retention": "1 month", + "description": "OCD_uesign_description", + "retention": "OCD_retention_1_month", "dataController": "ZOHO", "gdprUrl": "https://www.zoho.com/privacy.html", "wildcard": "0" @@ -10627,8 +11302,8 @@ "category": "Functional", "name": "wa_ul", "domain": "whatsapp.com", - "description": "Used to access the service it provides.", - "retention": "session", + "description": "OCD_wa_ul_description", + "retention": "OCD_retention_session", "dataController": "WhatsApp", "gdprUrl": "https://www.whatsapp.com/privacy/", "wildcard": "0" @@ -10640,8 +11315,8 @@ "category": "Functional", "name": "wa_lang_pref", "domain": "whatsapp.com", - "description": "Used by WhatsApp to save language preferences", - "retention": "6 days", + "description": "OCD_wa_lang_pref_description", + "retention": "OCD_retention_6_days", "dataController": "WhatsApp", "gdprUrl": "https://www.whatsapp.com/privacy/", "wildcard": "0" @@ -10653,8 +11328,8 @@ "category": "Marketing", "name": "pa_rubicon_ts", "domain": "prfct.co", - "description": "This cookie is set by Perfect Audience and is used for advertising purposes based on user behavior data.", - "retention": "2 years", + "description": "OCD_pa_rubicon_ts_description", + "retention": "OCD_retention_2_years", "dataController": "Perfect Audience", "gdprUrl": "https://www.perfectaudience.com/privacy/", "wildcard": "0" @@ -10666,8 +11341,8 @@ "category": "Marketing", "name": "pa_google_ts", "domain": "prfct.co", - "description": "This cookie is set by Perfect Audience and is used for advertising purposes based on user behavior data.", - "retention": "2 years", + "description": "OCD_pa_google_ts_description", + "retention": "OCD_retention_2_years", "dataController": "Perfect Audience", "gdprUrl": "https://www.perfectaudience.com/privacy/", "wildcard": "0" @@ -10679,8 +11354,8 @@ "category": "Marketing", "name": "pa_twitter_ts", "domain": "prfct.co", - "description": "This cookie is set by Perfect Audience and is used for advertising purposes based on user behavior data.", - "retention": "2 years", + "description": "OCD_pa_twitter_ts_description", + "retention": "OCD_retention_2_years", "dataController": "Perfect Audience", "gdprUrl": "https://www.perfectaudience.com/privacy/", "wildcard": "0" @@ -10692,8 +11367,8 @@ "category": "Marketing", "name": "pa_yahoo_ts", "domain": "prfct.co", - "description": "This cookie is set by Perfect Audience and is used for advertising purposes based on user behavior data.", - "retention": "2 years", + "description": "OCD_pa_yahoo_ts_description", + "retention": "OCD_retention_2_years", "dataController": "Perfect Audience", "gdprUrl": "https://www.perfectaudience.com/privacy/", "wildcard": "0" @@ -10705,8 +11380,8 @@ "category": "Marketing", "name": "pa_openx_ts", "domain": "prfct.co", - "description": "This cookie is set by Perfect Audience and is used for advertising purposes based on user behavior data.", - "retention": "2 years", + "description": "OCD_pa_openx_ts_description", + "retention": "OCD_retention_2_years", "dataController": "Perfect Audience", "gdprUrl": "https://www.perfectaudience.com/privacy/", "wildcard": "0" @@ -10718,8 +11393,8 @@ "category": "Marketing", "name": "pa_uid", "domain": "prfct.co", - "description": "This cookie is set by Perfect Audience and is used for advertising purposes based on user behavior data.", - "retention": "2 years", + "description": "OCD_pa_uid_description", + "retention": "OCD_retention_2_years", "dataController": "Perfect Audience", "gdprUrl": "https://www.perfectaudience.com/privacy/", "wildcard": "0" @@ -10731,8 +11406,8 @@ "category": "Marketing", "name": "mailmunch_second_pageview", "domain": "", - "description": "Used for tracking by the Mailmunch mailing list software", - "retention": "1 year", + "description": "OCD_mailmunch_second_pageview_description", + "retention": "OCD_retention_1_year", "dataController": "MailMunch", "gdprUrl": "https://legal.mailmunch.com/privacy/", "wildcard": "0" @@ -10744,8 +11419,8 @@ "category": "Marketing", "name": "_mailmunch_visitor_id", "domain": "", - "description": "This cookie is set by MailMunch which is email collection and email marketing platform.", - "retention": "1 year", + "description": "OCD__mailmunch_visitor_id_description", + "retention": "OCD_retention_1_year", "dataController": "MailMunch", "gdprUrl": "https://legal.mailmunch.com/privacy/", "wildcard": "0" @@ -10757,8 +11432,8 @@ "category": "Marketing", "name": "tPHG-PS", "domain": "prf.hn", - "description": "Partnerize’s tracking cookie, deployed either upon a user’s clicking of a link on a partner website, or upon the loading of a customer's image to a partner website.", - "retention": "1 year", + "description": "OCD_tPHG_PS_description", + "retention": "OCD_retention_1_year", "dataController": "Partnerize", "gdprUrl": "https://partnerize.com/privacy-policy/", "wildcard": "0" @@ -10770,8 +11445,8 @@ "category": "Marketing", "name": "digitalAudience", "domain": "digitalaudience.io", - "description": "Digital Audience uses cookies to improve the effectiveness of digital platforms, thanks to online recognition mechanisms.", - "retention": "Unlimited", + "description": "OCD_digitalAudience_description", + "retention": "OCD_retention_Unlimited", "dataController": "Digital Audience", "gdprUrl": "https://digitalaudience.io/legal/", "wildcard": "0" @@ -10783,8 +11458,8 @@ "category": "Functional", "name": "has_js", "domain": "", - "description": "Drupal uses this cookie to indicate whether or not the visitors browser has JavaScript enabled.", - "retention": "session", + "description": "OCD_has_js_description", + "retention": "OCD_retention_session", "dataController": "Drupal CMS", "gdprUrl": "https://www.drupal.org/privacy", "wildcard": "0" @@ -10796,8 +11471,8 @@ "category": "Marketing", "name": "_omappvs", "domain": "", - "description": "Cookie is used to identify returning visitors", - "retention": "1 day", + "description": "OCD__omappvs_description", + "retention": "OCD_retention_1_day", "dataController": "Optinmonster", "gdprUrl": "https://optinmonster.com/privacy/", "wildcard": "0" @@ -10809,8 +11484,8 @@ "category": "Marketing", "name": "_omappvp", "domain": "", - "description": "Cookie is used to identify returning visitors", - "retention": "1 day", + "description": "OCD__omappvp_description", + "retention": "OCD_retention_1_day", "dataController": "Optinmonster", "gdprUrl": "https://optinmonster.com/privacy/", "wildcard": "0" @@ -10822,8 +11497,8 @@ "category": "Marketing", "name": "_lfa", "domain": "", - "description": "Leadfeeder cookie collects the behavioral data of all website visitors. This includes; pages viewed, visitor source and time spent on the site", - "retention": "2 years", + "description": "OCD__lfa_description", + "retention": "OCD_retention_2_years", "dataController": "Leadfeeder", "gdprUrl": "https://www.leadfeeder.com/privacy/", "wildcard": "0" @@ -10835,8 +11510,8 @@ "category": "Marketing", "name": "SnapABugHistory", "domain": "", - "description": "This cookie is associated with live chat software from SnapEngage. It identifies a visitor to enable a history of engagement to be recorded.", - "retention": "1 year", + "description": "OCD_SnapABugHistory_description", + "retention": "OCD_retention_1_year", "dataController": "SnapEngage", "gdprUrl": "https://snapengage.com/privacy-policy/", "wildcard": "0" @@ -10848,8 +11523,8 @@ "category": "Functional", "name": "SnapABugUserAlias", "domain": "", - "description": "Stores a unique ID string for each chat-box session. This allows the website-support to see previous issues and reconnect with the previous supporter.", - "retention": "1 year", + "description": "OCD_SnapABugUserAlias_description", + "retention": "OCD_retention_1_year", "dataController": "SnapEngage", "gdprUrl": "https://snapengage.com/privacy-policy/", "wildcard": "0" @@ -10861,8 +11536,8 @@ "category": "Functional", "name": "SnapABugVisit", "domain": "", - "description": "This cookie is associated with live chat software from SnapEngage. It identifies a new user session.", - "retention": "1 year", + "description": "OCD_SnapABugVisit_description", + "retention": "OCD_retention_1_year", "dataController": "SnapEngage", "gdprUrl": "https://snapengage.com/privacy-policy/", "wildcard": "0" @@ -10874,8 +11549,8 @@ "category": "Marketing", "name": "SnapABugRef", "domain": "", - "description": "This cookie is associated with live chat software from SnapEngage. It records the landing page and origin of a visitor.", - "retention": "1 year", + "description": "OCD_SnapABugRef_description", + "retention": "OCD_retention_1_year", "dataController": "SnapEngage", "gdprUrl": "https://snapengage.com/privacy-policy/", "wildcard": "0" @@ -10887,8 +11562,8 @@ "category": "Marketing", "name": "audience", "domain": "spotxchange.com", - "description": "Sync audience data between buyers and sellers.", - "retention": "1 year", + "description": "OCD_audience_description", + "retention": "OCD_retention_1_year", "dataController": "SpotX", "gdprUrl": "https://www.spotx.tv/privacy-policy/", "wildcard": "0" @@ -10900,8 +11575,8 @@ "category": "Functional", "name": "yith_wcwl_session_", "domain": "", - "description": "YITH WooCommerce Wishlist plugin uses cookies in order to correctly store user wishlists", - "retention": "29 days", + "description": "OCD_yith_wcwl_session__description", + "retention": "OCD_retention_29_days", "dataController": "Yithemes.com", "gdprUrl": "https://yithemes.com/", "wildcard": "0" @@ -10912,9 +11587,9 @@ "platform": "Yahoo", "category": "Marketing", "name": "A3", - "domain": "yahoo.com", - "description": "Ads targeting cookie for Yahoo", - "retention": "1 year", + "domain": ".yahoo.com", + "description": "OCD_A3_description", + "retention": "OCD_retention_1_year", "dataController": "Yahoo", "gdprUrl": "https://yahoo.com/privacy/", "wildcard": "0" @@ -10926,8 +11601,8 @@ "category": "Marketing", "name": "APID", "domain": "advertising.com", - "description": "Collects information on visitor behaviour on multiple websites. This information is used on the website, in order to optimize the relevance of advertisement.", - "retention": "1 month", + "description": "OCD_APID_description", + "retention": "OCD_retention_1_month", "dataController": "Yahoo", "gdprUrl": "https://yahoo.com/privacy/", "wildcard": "0" @@ -10938,9 +11613,9 @@ "platform": "Yahoo", "category": "Marketing", "name": "APIDTS", - "domain": "yahoo.com", - "description": "This is a Yahoo! Cookie used in the targeting of relevant adverts and content on the Yahoo! platform.", - "retention": "1 day", + "domain": ".yahoo.com", + "description": "OCD_APIDTS_description", + "retention": "OCD_retention_1_day", "dataController": "Yahoo", "gdprUrl": "https://yahoo.com/privacy/", "wildcard": "0" @@ -10951,9 +11626,9 @@ "platform": "Yahoo", "category": "Marketing", "name": "IDSYNC", - "domain": "yahoo.com", - "description": "Identifies if the cookie-data needs to be updated in the visitor's browser - This is determined through third-party ad-serving-companies.", - "retention": "1 year", + "domain": ".yahoo.com", + "description": "OCD_IDSYNC_description", + "retention": "OCD_retention_1_year", "dataController": "Yahoo", "gdprUrl": "https://yahoo.com/privacy/", "wildcard": "0" @@ -10965,8 +11640,8 @@ "category": "Marketing", "name": "A1", "domain": "", - "description": "Ads targeting cookie for Yahoo", - "retention": "1 year", + "description": "OCD_A1_description", + "retention": "OCD_retention_1_year", "dataController": "Yahoo", "gdprUrl": "https://yahoo.com/privacy/", "wildcard": "0" @@ -10978,21 +11653,177 @@ "category": "Marketing", "name": "A1S", "domain": "", - "description": "Ads targeting cookie for Yahoo", - "retention": "1 year", + "description": "OCD_A1S_description", + "retention": "OCD_retention_1_year", "dataController": "Yahoo", "gdprUrl": "https://yahoo.com/privacy/", "wildcard": "0" } ], + "adaptv_unique_user_cookie": [ + { + "platform": "Yahoo", + "category": "Marketing", + "name": "adaptv_unique_user_cookie", + "domain": ".yahoo.com", + "description": "OCD_adaptv_unique_user_cookie_description", + "retention": "OCD_retention_1_year", + "dataController": "Yahoo", + "gdprUrl": "https://legal.yahoo.com/ie/en/yahoo/privacy/cookies/index.html", + "wildcard": "0" + } + ], + "cmp": [ + { + "platform": "Yahoo", + "category": "Marketing", + "name": "cmp", + "domain": ".yahoo.com", + "description": "OCD_cmp_description", + "retention": "OCD_retention_1_year", + "dataController": "Yahoo", + "gdprUrl": "https://legal.yahoo.com/ie/en/yahoo/privacy/cookies/index.html", + "wildcard": "0" + } + ], + "adsrcvw1": [ + { + "platform": "Yahoo", + "category": "Marketing", + "name": "adsrcvw1", + "domain": ".yahoo.com", + "description": "OCD_adsrcvw1_description", + "retention": "OCD_retention_1_year", + "dataController": "Yahoo", + "gdprUrl": "https://legal.yahoo.com/ie/en/yahoo/privacy/cookies/index.html", + "wildcard": "0" + } + ], + "migrated2y": [ + { + "platform": "Yahoo", + "category": "Marketing", + "name": "migrated2y", + "domain": ".yahoo.com", + "description": "OCD_migrated2y_description", + "retention": "OCD_retention_1_year", + "dataController": "Yahoo", + "gdprUrl": "https://legal.yahoo.com/ie/en/yahoo/privacy/cookies/index.html", + "wildcard": "0" + } + ], + "OTH": [ + { + "platform": "Yahoo", + "category": "Marketing", + "name": "OTH", + "domain": ".yahoo.com", + "description": "OCD_OTH_description", + "retention": "OCD_retention_1_year", + "dataController": "Yahoo", + "gdprUrl": "https://legal.yahoo.com/ie/en/yahoo/privacy/cookies/index.html", + "wildcard": "0" + } + ], + "rtbData0": [ + { + "platform": "Yahoo", + "category": "Marketing", + "name": "rtbData0", + "domain": ".yahoo.com", + "description": "OCD_rtbData0_description", + "retention": "OCD_retention_2_years", + "dataController": "Yahoo", + "gdprUrl": "https://legal.yahoo.com/ie/en/yahoo/privacy/cookies/index.html", + "wildcard": "0" + } + ], + "rxx": [ + { + "platform": "Yahoo", + "category": "Marketing", + "name": "rxx", + "domain": ".yahoo.com", + "description": "OCD_rxx_description", + "retention": "OCD_retention_1_year", + "dataController": "Yahoo", + "gdprUrl": "https://legal.yahoo.com/ie/en/yahoo/privacy/cookies/index.html", + "wildcard": "0" + } + ], + "tearsheet": [ + { + "platform": "Yahoo", + "category": "Marketing", + "name": "tearsheet", + "domain": ".yahoo.com", + "description": "OCD_tearsheet_description", + "retention": "OCD_retention_30_minutes", + "dataController": "Yahoo", + "gdprUrl": "https://legal.yahoo.com/ie/en/yahoo/privacy/cookies/index.html", + "wildcard": "0" + } + ], + "unique_ad_source_impression": [ + { + "platform": "Yahoo", + "category": "Marketing", + "name": "unique_ad_source_impression", + "domain": ".yahoo.com", + "description": "OCD_unique_ad_source_impression_description", + "retention": "OCD_retention_30_days", + "dataController": "Yahoo", + "gdprUrl": "https://legal.yahoo.com/ie/en/yahoo/privacy/cookies/index.html", + "wildcard": "0" + } + ], + "axids": [ + { + "platform": "Yahoo", + "category": "Marketing", + "name": "axids", + "domain": ".yahoo.com", + "description": "OCD_axids_description", + "retention": "OCD_retention_1_year", + "dataController": "Yahoo", + "gdprUrl": "https://legal.yahoo.com/ie/en/yahoo/privacy/cookies/index.html", + "wildcard": "0" + } + ], + "GUC": [ + { + "platform": "Yahoo", + "category": "Marketing", + "name": "GUC", + "domain": ".yahoo.com", + "description": "OCD_GUC_description", + "retention": "OCD_retention_1_year", + "dataController": "Yahoo", + "gdprUrl": "https://legal.yahoo.com/ie/en/yahoo/privacy/cookies/index.html", + "wildcard": "0" + } + ], + "tbla_id": [ + { + "platform": "Yahoo", + "category": "Marketing", + "name": "tbla_id", + "domain": ".yahoo.com", + "description": "OCD_tbla_id_description", + "retention": "OCD_retention_1_year", + "dataController": "Yahoo", + "gdprUrl": "https://legal.yahoo.com/ie/en/yahoo/privacy/cookies/index.html", + "wildcard": "0" + } + ], "INGRESSCOOKIE": [ { "platform": "NGINX Ingresss", "category": "Functional", "name": "INGRESSCOOKIE", "domain": "", - "description": "Registers which server-cluster is serving the visitor. This is used in context with load balancing, in order to optimize user experience.", - "retention": "session", + "description": "OCD_INGRESSCOOKIE_description", + "retention": "OCD_retention_session", "dataController": "NGINX", "gdprUrl": "", "wildcard": "0" @@ -11004,8 +11835,8 @@ "category": "Functional", "name": "AlteonP", "domain": "", - "description": "This cookie is set by the load balancers and allows us to evenly balance the number of users across the web servers that we use.", - "retention": "session", + "description": "OCD_AlteonP_description", + "retention": "OCD_retention_session", "dataController": "Radware", "gdprUrl": "https://www.radware.com/privacypolicy.aspx/", "wildcard": "0" @@ -11017,8 +11848,8 @@ "category": "Marketing", "name": "cref", "domain": "quantserve.com", - "description": "Contains data on user navigation, interaction and time spent on the website and its sub-pages – This data is used to optimise the relevance of advertisements and for statistical purposes.", - "retention": "13 months", + "description": "OCD_cref_description", + "retention": "OCD_retention_13_months", "dataController": "Quantcast", "gdprUrl": "https://www.quantcast.com/privacy/", "wildcard": "0" @@ -11030,8 +11861,8 @@ "category": "Marketing", "name": "mc", "domain": "quantserve.com", - "description": "Tracking of users and measure and improve performance and supports personalisation", - "retention": "13 months", + "description": "OCD_mc_description", + "retention": "OCD_retention_13_months", "dataController": "Quantcast", "gdprUrl": "https://www.quantcast.com/privacy/", "wildcard": "0" @@ -11043,8 +11874,8 @@ "category": "Marketing", "name": "d", "domain": "quantserve.com", - "description": "Tracking of users and measure and improve performance and supports personalisation", - "retention": "13 months", + "description": "OCD_d_description", + "retention": "OCD_retention_13_months", "dataController": "Quantcast", "gdprUrl": "https://www.quantcast.com/privacy/", "wildcard": "0" @@ -11056,8 +11887,8 @@ "category": "Marketing", "name": "iutk", "domain": "issuu.com", - "description": "Recognises the user's device and what Issuu documents have been read.", - "retention": "10 years", + "description": "OCD_iutk_description", + "retention": "OCD_retention_10_years", "dataController": "Issuu", "gdprUrl": "https://issuu.com/legal/privacy", "wildcard": "0" @@ -11069,8 +11900,8 @@ "category": "Functional", "name": "cookielawinfo-checkbox-necessary", "domain": "", - "description": "This cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category 'Necessary'.", - "retention": "11 months", + "description": "OCD_cookielawinfo_checkbox_necessary_description", + "retention": "OCD_retention_11_months", "dataController": "CookieYes", "gdprUrl": "https://www.cookielawinfo.com/privacy-policy/", "wildcard": "0" @@ -11082,8 +11913,8 @@ "category": "Functional", "name": "cookielawinfo-checkbox-non-necessary", "domain": "", - "description": "This cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category 'Non Necessary'.", - "retention": "11 months", + "description": "OCD_cookielawinfo_checkbox_non_necessary_description", + "retention": "OCD_retention_11_months", "dataController": "CookieYes", "gdprUrl": "https://www.cookielawinfo.com/privacy-policy/", "wildcard": "0" @@ -11095,8 +11926,8 @@ "category": "Functional", "name": "viewed_cookie_policy", "domain": "", - "description": "The cookie is set by the GDPR Cookie Consent plugin and is used to store whether or not user has consented to the use of cookies. It does not store any personal data.", - "retention": "11 months", + "description": "OCD_viewed_cookie_policy_description", + "retention": "OCD_retention_11_months", "dataController": "CookieYes", "gdprUrl": "https://www.cookielawinfo.com/privacy-policy/", "wildcard": "0" @@ -11108,8 +11939,8 @@ "category": "Functional", "name": "cookielawinfo-checkbox-marketing", "domain": "", - "description": "This cookie is set by GDPR Cookie Consent plugin. The purpose of this cookie is to check whether or not the user has given the consent to the usage of cookies under the category 'Marketing'.", - "retention": "11 months", + "description": "OCD_cookielawinfo_checkbox_marketing_description", + "retention": "OCD_retention_11_months", "dataController": "CookieYes", "gdprUrl": "https://www.cookielawinfo.com/privacy-policy/", "wildcard": "0" @@ -11121,8 +11952,8 @@ "category": "Functional", "name": "cookielawinfo-checkbox-analytics", "domain": "", - "description": "This cookie is set by GDPR Cookie Consent plugin. The purpose of this cookie is to check whether or not the user has given the consent to the usage of cookies under the category 'Analytics'.", - "retention": "11 months", + "description": "OCD_cookielawinfo_checkbox_analytics_description", + "retention": "OCD_retention_11_months", "dataController": "CookieYes", "gdprUrl": "https://www.cookielawinfo.com/privacy-policy/", "wildcard": "0" @@ -11134,8 +11965,8 @@ "category": "Functional", "name": "cookielawinfo-checkbox-performance", "domain": "", - "description": "This cookie is set by GDPR Cookie Consent plugin. The purpose of this cookie is to check whether or not the user has given the consent to the usage of cookies under the category 'Performance'.", - "retention": "11 months", + "description": "OCD_cookielawinfo_checkbox_performance_description", + "retention": "OCD_retention_11_months", "dataController": "CookieYes", "gdprUrl": "https://www.cookielawinfo.com/privacy-policy/", "wildcard": "0" @@ -11147,8 +11978,8 @@ "category": "Functional", "name": "cookielawinfo-checkbox-others", "domain": "", - "description": "This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category 'Other'.", - "retention": "11 months", + "description": "OCD_cookielawinfo_checkbox_others_description", + "retention": "OCD_retention_11_months", "dataController": "CookieYes", "gdprUrl": "https://www.cookielawinfo.com/privacy-policy/", "wildcard": "0" @@ -11160,8 +11991,8 @@ "category": "Functional", "name": "cookielawinfo-checkbox-functional", "domain": "", - "description": "This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category 'Functional'.", - "retention": "11 months", + "description": "OCD_cookielawinfo_checkbox_functional_description", + "retention": "OCD_retention_11_months", "dataController": "CookieYes", "gdprUrl": "https://www.cookielawinfo.com/privacy-policy/", "wildcard": "0" @@ -11173,8 +12004,8 @@ "category": "Functional", "name": "cookielawinfo-checkbox-advertisement", "domain": "", - "description": "This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category 'Advertisement'.", - "retention": "11 months", + "description": "OCD_cookielawinfo_checkbox_advertisement_description", + "retention": "OCD_retention_11_months", "dataController": "CookieYes", "gdprUrl": "https://www.webtoffee.com/privacy-policy/", "wildcard": "0" @@ -11186,8 +12017,8 @@ "category": "Functional", "name": "cli_user_preference", "domain": "", - "description": "The cookie is set by the GDPR Cookie Consent plugin and is used to store whether or not user has consented to the use of cookies. It does not store any personal data.", - "retention": "1 year", + "description": "OCD_cli_user_preference_description", + "retention": "OCD_retention_1_year", "dataController": "CookieYes", "gdprUrl": "https://www.cookielawinfo.com/privacy-policy/", "wildcard": "0" @@ -11199,8 +12030,8 @@ "category": "Functional", "name": "cookielawinfo-checkbox-preferences", "domain": "", - "description": "This cookie is set by GDPR Cookie Consent plugin. The purpose of this cookie is to check whether or not the user has given the consent to the usage of cookies under the category 'Preferences'.", - "retention": "1 year", + "description": "OCD_cookielawinfo_checkbox_preferences_description", + "retention": "OCD_retention_1_year", "dataController": "CookieYes", "gdprUrl": "https://www.cookielawinfo.com/privacy-policy/", "wildcard": "0" @@ -11212,8 +12043,8 @@ "category": "Functional", "name": "CookieLawInfoConsent", "domain": "", - "description": "The cookie is set by the GDPR Cookie Consent plugin and is used to store whether or not user has consented to the use of cookies. It does not store any personal data.", - "retention": "1 year", + "description": "OCD_CookieLawInfoConsent_description", + "retention": "OCD_retention_1_year", "dataController": "CookieYes", "gdprUrl": "https://www.cookielawinfo.com/privacy-policy/", "wildcard": "0" @@ -11225,8 +12056,8 @@ "category": "Marketing", "name": "__qca", "domain": "", - "description": "This cookie is set by Quantcast, who present targeted advertising. Stores browser and HTTP request information.", - "retention": "1 year", + "description": "OCD___qca_description", + "retention": "OCD_retention_1_year", "dataController": "Quantcast", "gdprUrl": "https://www.quantcast.com/privacy/", "wildcard": "0" @@ -11238,8 +12069,8 @@ "category": "Analytics", "name": "stg_traffic_source_priority", "domain": "", - "description": "Stores the type of traffic source that explains how the visitor reached your website.", - "retention": "30 minutes", + "description": "OCD_stg_traffic_source_priority_description", + "retention": "OCD_retention_30_minutes", "dataController": "Piwik", "gdprUrl": "https://piwik.pro/privacy-policy/", "wildcard": "0" @@ -11251,8 +12082,8 @@ "category": "Analytics", "name": "stg_last_interaction", "domain": "", - "description": "Determines whether the last visitor's session is still in progress or a new session has started.", - "retention": "365 days", + "description": "OCD_stg_last_interaction_description", + "retention": "OCD_retention_365_days", "dataController": "Piwik", "gdprUrl": "https://piwik.pro/privacy-policy/", "wildcard": "0" @@ -11264,8 +12095,8 @@ "category": "Analytics", "name": "stg_returning_visitor", "domain": "", - "description": "Determines if the visitor has already been to your website — they are returning visitors.", - "retention": "365 days", + "description": "OCD_stg_returning_visitor_description", + "retention": "OCD_retention_365_days", "dataController": "Piwik", "gdprUrl": "https://piwik.pro/privacy-policy/", "wildcard": "0" @@ -11277,8 +12108,8 @@ "category": "Analytics", "name": "stg_externalReferrer", "domain": "", - "description": "Stores an URL of a website that referred a visitor to your website.", - "retention": "session", + "description": "OCD_stg_externalReferrer_description", + "retention": "OCD_retention_session", "dataController": "Piwik", "gdprUrl": "https://piwik.pro/privacy-policy/", "wildcard": "0" @@ -11290,8 +12121,8 @@ "category": "Functional", "name": "__sqra", "domain": "", - "description": "Tracks the user's interaction with the website's search-bar-function. This data can be used to present the user with relevant products or services.", - "retention": "2 years", + "description": "OCD___sqra_description", + "retention": "OCD_retention_2_years", "dataController": "Sooqr", "gdprUrl": "https://www.sooqr.com/privacy-policy", "wildcard": "0" @@ -11303,8 +12134,8 @@ "category": "Functional", "name": "__sqrb", "domain": "", - "description": "Tracks the user's interaction with the website's search-bar-function. This data can be used to present the user with relevant products or services.", - "retention": "2 years", + "description": "OCD___sqrb_description", + "retention": "OCD_retention_2_years", "dataController": "Sooqr", "gdprUrl": "https://www.sooqr.com/privacy-policy", "wildcard": "0" @@ -11316,8 +12147,8 @@ "category": "Functional", "name": "__sqrc", "domain": "", - "description": "Tracks the user's interaction with the website's search-bar-function. This data can be used to present the user with relevant products or services.", - "retention": "2 years", + "description": "OCD___sqrc_description", + "retention": "OCD_retention_2_years", "dataController": "Sooqr", "gdprUrl": "https://www.sooqr.com/privacy-policy", "wildcard": "0" @@ -11329,8 +12160,8 @@ "category": "Analytics", "name": "nmstat", "domain": "", - "description": "This cookie is used to help record the visitor's use of the website. It is used to collect statistics about site usage such as when the visitor last visited the site. This information is then used to improve the user experience on the website. This Siteimprove Analytics cookie contains a randomly generated ID used to recognize the browser when a visitor reads a page. The cookie contains no personal information and is used only for web analytics. It is also used to track the sequence of pages a visitor looks at during a visit to the site. This information can be used to reduce user journeys, and enable visitors to find relevant information quicker.", - "retention": "3 years", + "description": "OCD_nmstat_description", + "retention": "OCD_retention_3_years", "dataController": "Siteimprove", "gdprUrl": "https://siteimprove.com/en/privacy/", "wildcard": "0" @@ -11342,8 +12173,8 @@ "category": "Analytics", "name": "sp", "domain": "", - "description": "Stores a server-side collector generated unique identifier for a user that is sent with all subsequent tracking event events. Can be used as a first party cookie is the collector is on the same domain as the site.", - "retention": "1 year", + "description": "OCD_sp_description", + "retention": "OCD_retention_1_year", "dataController": "Snowplow", "gdprUrl": "https://snowplowanalytics.com/privacy-policy/", "wildcard": "0" @@ -11355,11 +12186,22 @@ "category": "Analytics", "name": "_sp_id.*", "domain": "", - "description": "Stores user information that is created when a user first visits a site and updated on subsequent visits. It is used to identify users and track the users activity across a domain. This cookie stores a unique identifier for each user, a unique identifier for the users current session, the number of visits a user has made to the site, the timestamp of the users first visit, the timestamp of their previous visit and the timestamp of their current visit.", - "retention": "2 years", + "description": "OCD__sp_id___description", + "retention": "OCD_retention_2_years", "dataController": "Snowplow", "gdprUrl": "https://snowplowanalytics.com/privacy-policy/", "wildcard": "1" + }, + { + "platform": "Wistia", + "category": "Functional", + "name": "_sp_id.*", + "domain": "", + "description": "This cookie is used by the Wistia video player to remember where you are in a video so that if playback is interrupted (for example, by losing your internet connection) then you can get right back to where you left off.", + "retention": "1 Year", + "dataController": "Wistia", + "gdprUrl": "https://wistia.com/privacy", + "wildcard": "1" } ], "_sp_ses.*": [ @@ -11368,8 +12210,8 @@ "category": "Analytics", "name": "_sp_ses.*", "domain": "", - "description": "Used to identify if the user is in an active session on a site or if this is a new session for a user (i.e. cookie doesn’t exist or has expired).", - "retention": "30 minutes", + "description": "OCD__sp_ses___description", + "retention": "OCD_retention_30_minutes", "dataController": "Snowplow", "gdprUrl": "https://snowplowanalytics.com/privacy-policy/", "wildcard": "1" @@ -11381,8 +12223,8 @@ "category": "Functional", "name": "AUTH_SESSION_ID", "domain": "", - "description": "ID of current authentication session.", - "retention": "session", + "description": "OCD_AUTH_SESSION_ID_description", + "retention": "OCD_retention_session", "dataController": "Keycloak", "gdprUrl": "", "wildcard": "0" @@ -11394,8 +12236,8 @@ "category": "Functional", "name": "KC_RESTART", "domain": "", - "description": "Internal cookie from Keycloak.", - "retention": "session", + "description": "OCD_KC_RESTART_description", + "retention": "OCD_retention_session", "dataController": "Keycloak", "gdprUrl": "", "wildcard": "0" @@ -11407,8 +12249,8 @@ "category": "Functional", "name": "KC_START", "domain": "", - "description": "Internal cookie from Keycloak.", - "retention": "session", + "description": "OCD_KC_START_description", + "retention": "OCD_retention_session", "dataController": "Keycloak", "gdprUrl": "", "wildcard": "0" @@ -11420,8 +12262,8 @@ "category": "Functional", "name": "KEYCLOAK_IDENTITY", "domain": "", - "description": "ID of the current user.", - "retention": "session", + "description": "OCD_KEYCLOAK_IDENTITY_description", + "retention": "OCD_retention_session", "dataController": "Keycloak", "gdprUrl": "", "wildcard": "0" @@ -11433,8 +12275,8 @@ "category": "Functional", "name": "KEYCLOAK_LOCALE", "domain": "", - "description": "Language of the interface.", - "retention": "session", + "description": "OCD_KEYCLOAK_LOCALE_description", + "retention": "OCD_retention_session", "dataController": "Keycloak", "gdprUrl": "", "wildcard": "0" @@ -11446,8 +12288,8 @@ "category": "Functional", "name": "KEYCLOAK_REMEMBER_ME", "domain": "", - "description": "Internal cookie from Keycloak.", - "retention": "1 year", + "description": "OCD_KEYCLOAK_REMEMBER_ME_description", + "retention": "OCD_retention_1_year", "dataController": "Keycloak", "gdprUrl": "", "wildcard": "0" @@ -11459,8 +12301,8 @@ "category": "Functional", "name": "KEYCLOAK_SESSION", "domain": "", - "description": "ID of the current browser session", - "retention": "session", + "description": "OCD_KEYCLOAK_SESSION_description", + "retention": "OCD_retention_session", "dataController": "Keycloak", "gdprUrl": "", "wildcard": "0" @@ -11472,8 +12314,8 @@ "category": "Functional", "name": "_abck", "domain": "", - "description": "This cookie is used to analyse traffic to determine if it is automated traffic generated by IT systems or a human user", - "retention": "session", + "description": "OCD__abck_description", + "retention": "OCD_retention_session", "dataController": "Akamai", "gdprUrl": "https://www.akamai.com/us/en/privacy-policies/", "wildcard": "0" @@ -11485,8 +12327,8 @@ "category": "Functional", "name": "AKA_A2", "domain": "", - "description": "Used for Akamai's Advanced Acceleration feature, intended to improve web performance", - "retention": "1 hour or longer", + "description": "OCD_AKA_A2_description", + "retention": "OCD_retention_1_hour_or_longer", "dataController": "Akamai", "gdprUrl": "", "wildcard": "0" @@ -11498,8 +12340,8 @@ "category": "Functional", "name": "ak_bmsc", "domain": "", - "description": "Cookie used to optimize performance, and to improve the user experience, on Akamai websites", - "retention": "1 hour or longer", + "description": "OCD_ak_bmsc_description", + "retention": "OCD_retention_1_hour_or_longer", "dataController": "Akamai", "gdprUrl": "", "wildcard": "0" @@ -11511,8 +12353,8 @@ "category": "Functional", "name": "bm_sv", "domain": "", - "description": "Used by Akamai Botman Manager to help differentiate between web requests generated by humans and web requests generated by bots or other automated processes", - "retention": "1 hour or longer", + "description": "OCD_bm_sv_description", + "retention": "OCD_retention_1_hour_or_longer", "dataController": "Akamai", "gdprUrl": "", "wildcard": "0" @@ -11524,8 +12366,8 @@ "category": "Functional", "name": "CRAFT_CSRF_TOKEN", "domain": "", - "description": "Facilitates protection against cross-site request forgeries. This helps to safeguard data as it is submitted through forms on the website.", - "retention": "session", + "description": "OCD_CRAFT_CSRF_TOKEN_description", + "retention": "OCD_retention_session", "dataController": "CraftCMS", "gdprUrl": "https://craftcms.com/privacy", "wildcard": "0" @@ -11537,8 +12379,8 @@ "category": "Functional", "name": "CraftSessionId", "domain": "", - "description": "Craft relies on PHP sessions to maintain sessions across web requests. That is done via the PHP session cookie. Craft names that cookie 'CraftSessionId' by default. This cookie will expire as soon as the session expires.", - "retention": "session", + "description": "OCD_CraftSessionId_description", + "retention": "OCD_retention_session", "dataController": "CraftCMS", "gdprUrl": "https://craftcms.com/privacy", "wildcard": "0" @@ -11550,8 +12392,8 @@ "category": "Functional", "name": "ci_session", "domain": "", - "description": "Cookie to track the users logged in state and access level to restricted pages.", - "retention": "session", + "description": "OCD_ci_session_description", + "retention": "OCD_retention_session", "dataController": "CodeIgniter", "gdprUrl": "https://codeigniter.com/help", "wildcard": "0" @@ -11563,8 +12405,8 @@ "category": "Functional", "name": "__lc_cid", "domain": "livechatinc.com", - "description": "Necessary for the functionality of the website's chat-box function.", - "retention": "3 years", + "description": "OCD___lc_cid_description", + "retention": "OCD_retention_3_years", "dataController": "Livechat", "gdprUrl": "https://www.livechat.com/legal/privacy-policy/", "wildcard": "0" @@ -11576,8 +12418,8 @@ "category": "Functional", "name": "__lc_cst", "domain": "livechatinc.com", - "description": "Necessary for the functionality of the website's chat-box function.", - "retention": "3 years", + "description": "OCD___lc_cst_description", + "retention": "OCD_retention_3_years", "dataController": "Livechat", "gdprUrl": "https://www.livechat.com/legal/privacy-policy/", "wildcard": "0" @@ -11589,8 +12431,8 @@ "category": "Functional", "name": "__lc2_cid", "domain": "livechatinc.com", - "description": "Stores a unique ID string for each chat-box session. This allows the website-support to see previous issues and reconnect with the previous supporter.", - "retention": "3 years", + "description": "OCD___lc2_cid_description", + "retention": "OCD_retention_3_years", "dataController": "Livechat", "gdprUrl": "https://www.livechat.com/legal/privacy-policy/", "wildcard": "0" @@ -11602,8 +12444,8 @@ "category": "Functional", "name": "__lc2_cst", "domain": "livechatinc.com", - "description": "Stores a unique ID string for each chat-box session. This allows the website-support to see previous issues and reconnect with the previous supporter.", - "retention": "3 years", + "description": "OCD___lc2_cst_description", + "retention": "OCD_retention_3_years", "dataController": "Livechat", "gdprUrl": "https://www.livechat.com/legal/privacy-policy/", "wildcard": "0" @@ -11615,8 +12457,8 @@ "category": "Functional", "name": "__livechat", "domain": "livechatinc.com", - "description": "Used to hide the user's personal customisation of LiveChat.", - "retention": "3 years", + "description": "OCD___livechat_description", + "retention": "OCD_retention_3_years", "dataController": "Livechat", "gdprUrl": "https://www.livechat.com/legal/privacy-policy/", "wildcard": "0" @@ -11628,8 +12470,8 @@ "category": "Analytics", "name": "BVBRANDID", "domain": "network.bazaarvoice.com", - "description": "BVBRANDID is a persistent cookie that allows Bazaarvoice to track website analytics data such as how often you visit the site and allocate it to the same website visitor.", - "retention": "20 years", + "description": "OCD_BVBRANDID_description", + "retention": "OCD_retention_20_years", "dataController": "Bazaar Voice", "gdprUrl": "https://www.bazaarvoice.com/legal/privacy-policy/", "wildcard": "0" @@ -11641,8 +12483,8 @@ "category": "Analytics", "name": "BVBRANDSID", "domain": "network.bazaarvoice.com", - "description": "This cookie allows internal Bazaarvoice web analytics to be correlated to the same user browsing session for interactions within a particular client domain.", - "retention": "20 years", + "description": "OCD_BVBRANDSID_description", + "retention": "OCD_retention_20_years", "dataController": "Bazaar Voice", "gdprUrl": "https://www.bazaarvoice.com/legal/privacy-policy/", "wildcard": "0" @@ -11654,8 +12496,8 @@ "category": "Marketing", "name": "BVID", "domain": "network.bazaarvoice.com", - "description": "Allows internal Bazaarvoice web analytics to be correlated to the same user for interactions across the Bazaarvoice network.", - "retention": "365 days", + "description": "OCD_BVID_description", + "retention": "OCD_retention_365_days", "dataController": "Bazaar Voice", "gdprUrl": "https://www.bazaarvoice.com/legal/privacy-policy/", "wildcard": "0" @@ -11667,8 +12509,8 @@ "category": "Marketing", "name": "BVSID", "domain": "network.bazaarvoice.com", - "description": "Allows internal Bazaarvoice web analytics to be correlated to the same user browsing session for interactions across the Bazaarvoice network.", - "retention": "session", + "description": "OCD_BVSID_description", + "retention": "OCD_retention_session", "dataController": "Bazaar Voice", "gdprUrl": "https://www.bazaarvoice.com/legal/privacy-policy/", "wildcard": "0" @@ -11680,8 +12522,8 @@ "category": "Marketing", "name": "_li_id*", "domain": "", - "description": "These cookies enable us to get insights about the business use of our website, based on IP addresses of the website visitors.", - "retention": "1 year", + "description": "OCD__li_id__description", + "retention": "OCD_retention_1_year", "dataController": "Leadinfo", "gdprUrl": "https://www.leadinfo.com/en/privacy/", "wildcard": "1" @@ -11693,8 +12535,8 @@ "category": "Marketing", "name": "_li_ses*", "domain": "", - "description": "These cookies enable us to get insights about the business use of our website, based on IP addresses of the website visitors.", - "retention": "1 year", + "description": "OCD__li_ses__description", + "retention": "OCD_retention_1_year", "dataController": "Leadinfo", "gdprUrl": "https://www.leadinfo.com/en/privacy/", "wildcard": "1" @@ -11706,8 +12548,8 @@ "category": "Functional", "name": "CAKEPHP", "domain": "", - "description": "A cookie controller used to manage other Cookies", - "retention": "1 hour", + "description": "OCD_CAKEPHP_description", + "retention": "OCD_retention_1_hour", "dataController": "CakePHP", "gdprUrl": "https://cakephp.org/privacy", "wildcard": "0" @@ -11719,8 +12561,8 @@ "category": "Functional", "name": "wp-wpml_current_language", "domain": "", - "description": "This cookie is used to track the language preference fo the user", - "retention": "session", + "description": "OCD_wp_wpml_current_language_description", + "retention": "OCD_retention_session", "dataController": "WPML", "gdprUrl": "", "wildcard": "0" @@ -11732,8 +12574,8 @@ "category": "Functional", "name": "_flowbox", "domain": "", - "description": "Used to differentiate between users and sessions and collecting statistics on the viewing behaviour for Instagram posts displayed on the website.", - "retention": "1 year", + "description": "OCD__flowbox_description", + "retention": "OCD_retention_1_year", "dataController": "Flowbox", "gdprUrl": "https://getflowbox.com/privacy/", "wildcard": "0" @@ -11745,8 +12587,8 @@ "category": "Marketing", "name": "acalltracker", "domain": "", - "description": "Adcalls call tracking: ID, phone number", - "retention": "30 days", + "description": "OCD_acalltracker_description", + "retention": "OCD_retention_30_days", "dataController": "Adcalls", "gdprUrl": "https://adcalls.com/privacy-statement/", "wildcard": "0" @@ -11758,8 +12600,8 @@ "category": "Functional", "name": "acalltrackersession", "domain": "", - "description": "This cookie stores a unique identifier, so that it can be tracked which session the visitor is in.", - "retention": "session", + "description": "OCD_acalltrackersession_description", + "retention": "OCD_retention_session", "dataController": "Adcalls", "gdprUrl": "https://adcalls.com/privacy-statement/", "wildcard": "0" @@ -11771,8 +12613,8 @@ "category": "Analytics", "name": "acalltrackerreferrer", "domain": "", - "description": "This cookie is set as soon as the AdCalls JavaScript is loaded. The cookie is used to store the referrer of the visitor as quickly as possible, so that it cannot be lost. As soon as the JavaScript has been executed, this cookie is immediately deleted.", - "retention": "60 minutes", + "description": "OCD_acalltrackerreferrer_description", + "retention": "OCD_retention_60_minutes", "dataController": "Adcalls", "gdprUrl": "https://adcalls.com/privacy-statement/", "wildcard": "0" @@ -11784,8 +12626,8 @@ "category": "Functional", "name": "excludecalltracking", "domain": "", - "description": "This cookie is set as soon as the visitor - for whatever reason - is not measured, so that we do not take any further actions.", - "retention": "session", + "description": "OCD_excludecalltracking_description", + "retention": "OCD_retention_session", "dataController": "Adcalls", "gdprUrl": "https://adcalls.com/privacy-statement/", "wildcard": "0" @@ -11797,8 +12639,8 @@ "category": "Marketing", "name": "acalltrackernumber", "domain": "", - "description": "This cookie stores the phone number for the session that is active.", - "retention": "30 minutes", + "description": "OCD_acalltrackernumber_description", + "retention": "OCD_retention_30_minutes", "dataController": "Adcalls", "gdprUrl": "https://adcalls.com/privacy-statement/", "wildcard": "0" @@ -11810,8 +12652,8 @@ "category": "Functional", "name": "wordpress_google_apps_login", "domain": "", - "description": "Used for secure log in to the web site with a Google account.", - "retention": "session", + "description": "OCD_wordpress_google_apps_login_description", + "retention": "OCD_retention_session", "dataController": "WP-Glogin", "gdprUrl": "https://wp-glogin.com/privacy-policy/", "wildcard": "0" @@ -11823,8 +12665,8 @@ "category": "Functional", "name": "ckies_functional", "domain": "", - "description": "Opt-out for functional cookies", - "retention": "1 year", + "description": "OCD_ckies_functional_description", + "retention": "OCD_retention_1_year", "dataController": "Jimdo", "gdprUrl": "https://www.jimdo.com/info/privacy/", "wildcard": "0" @@ -11836,8 +12678,8 @@ "category": "Functional", "name": "ckies_necessary", "domain": "", - "description": "Confirms that other necessary cookies get set", - "retention": "1 year", + "description": "OCD_ckies_necessary_description", + "retention": "OCD_retention_1_year", "dataController": "Jimdo", "gdprUrl": "https://www.jimdo.com/info/privacy/", "wildcard": "0" @@ -11849,8 +12691,8 @@ "category": "Functional", "name": "ckies_performance", "domain": "", - "description": "Opt-out for performance cookies", - "retention": "1 year", + "description": "OCD_ckies_performance_description", + "retention": "OCD_retention_1_year", "dataController": "Jimdo", "gdprUrl": "https://www.jimdo.com/info/privacy/", "wildcard": "0" @@ -11862,8 +12704,8 @@ "category": "Functional", "name": "ckies_marketing", "domain": "", - "description": "Opt-out for marketing/third party/consent based cookies", - "retention": "1 year", + "description": "OCD_ckies_marketing_description", + "retention": "OCD_retention_1_year", "dataController": "Jimdo", "gdprUrl": "https://www.jimdo.com/info/privacy/", "wildcard": "0" @@ -11875,8 +12717,8 @@ "category": "Functional", "name": "ClickAndChange", "domain": "", - "description": "Session Cookie for Creator CMS", - "retention": "session", + "description": "OCD_ClickAndChange_description", + "retention": "OCD_retention_session", "dataController": "Jimdo", "gdprUrl": "https://www.jimdo.com/info/privacy/", "wildcard": "0" @@ -11888,8 +12730,8 @@ "category": "Functional", "name": "pll_language", "domain": "", - "description": "Saves the chosen language.", - "retention": "1 year", + "description": "OCD_pll_language_description", + "retention": "OCD_retention_1_year", "dataController": "Polylang", "gdprUrl": "https://polylang.pro/privacy-policy/", "wildcard": "0" @@ -11901,8 +12743,8 @@ "category": "Functional", "name": "browserupdateorg", "domain": "", - "description": "Stores information if user dismissed notification about outdated browser", - "retention": "30 days", + "description": "OCD_browserupdateorg_description", + "retention": "OCD_retention_30_days", "dataController": "Browser-Update.org", "gdprUrl": "", "wildcard": "0" @@ -11914,8 +12756,8 @@ "category": "Functional", "name": "TawkConnectionTime", "domain": "", - "description": "This cookie is used to determine the connection duration of tawk sessions.", - "retention": "session", + "description": "OCD_TawkConnectionTime_description", + "retention": "OCD_retention_session", "dataController": "Tawk.to Chat", "gdprUrl": "https://www.tawk.to/privacy-policy/", "wildcard": "0" @@ -11927,8 +12769,8 @@ "category": "Analytics", "name": "tawkUUID", "domain": "va.tawk.to", - "description": "This cookie is used to collect information about how the visitor handles the live chat function on the website.", - "retention": "10 years", + "description": "OCD_tawkUUID_description", + "retention": "OCD_retention_10_years", "dataController": "Tawk.to Chat", "gdprUrl": "https://www.tawk.to/privacy-policy/", "wildcard": "0" @@ -11940,8 +12782,8 @@ "category": "Functional", "name": "TawkCookie", "domain": "", - "description": "Main Tawk.to cookie.", - "retention": "session", + "description": "OCD_TawkCookie_description", + "retention": "OCD_retention_session", "dataController": "Tawk.to Chat", "gdprUrl": "https://www.tawk.to/privacy-policy/", "wildcard": "0" @@ -11953,8 +12795,8 @@ "category": "Functional", "name": "__tawkuuid", "domain": "", - "description": "Tawk.to cookie used to distinguish users.", - "retention": "10 years", + "description": "OCD___tawkuuid_description", + "retention": "OCD_retention_10_years", "dataController": "Tawk.to Chat", "gdprUrl": "https://www.tawk.to/privacy-policy/", "wildcard": "0" @@ -11966,8 +12808,8 @@ "category": "Marketing", "name": "stx_user_id", "domain": "sharethrough.com", - "description": "Delivering targeted and relevant content", - "retention": "1 year", + "description": "OCD_stx_user_id_description", + "retention": "OCD_retention_1_year", "dataController": "Sharethrough", "gdprUrl": "https://platform-cdn.sharethrough.com/privacy-policy", "wildcard": "0" @@ -11979,11 +12821,22 @@ "category": "Marketing", "name": "dc", "domain": "betweendigital.com", - "description": "This cookie is used for advertising purposes", - "retention": "10 years", + "description": "OCD_dc_description", + "retention": "OCD_retention_10_years", "dataController": "BetweenDigital", "gdprUrl": "", "wildcard": "0" + }, + { + "platform": "Tailtarget", + "category": "Functional", + "name": "dc", + "domain": ".t.tailtarget.com", + "description": "This cookie is used for sync with Google services", + "retention": "30 days", + "dataController": "Totvs", + "gdprUrl": "https://www.totvs.com/protecao-e-privacidade-de-dados/", + "wildcard": "0" } ], "sqzl_abs": [ @@ -11992,8 +12845,8 @@ "category": "Marketing", "name": "sqzl_abs", "domain": "", - "description": "A cookie used by Squeezely", - "retention": "1 month", + "description": "OCD_sqzl_abs_description", + "retention": "OCD_retention_1_month", "dataController": "Squeezely", "gdprUrl": "https://squeezely.tech/privacy", "wildcard": "0" @@ -12005,8 +12858,8 @@ "category": "Functional", "name": "sqzl_consent", "domain": "", - "description": "Stores the user's cookie consent state for the current domain", - "retention": "1 year", + "description": "OCD_sqzl_consent_description", + "retention": "OCD_retention_1_year", "dataController": "Squeezely", "gdprUrl": "https://squeezely.tech/privacy", "wildcard": "0" @@ -12018,8 +12871,8 @@ "category": "Marketing", "name": "sqzl_session_id", "domain": "", - "description": "A cookie used by Squeezely", - "retention": "session", + "description": "OCD_sqzl_session_id_description", + "retention": "OCD_retention_session", "dataController": "Squeezely", "gdprUrl": "https://squeezely.tech/privacy", "wildcard": "0" @@ -12031,8 +12884,8 @@ "category": "Marketing", "name": "sqzl_vw", "domain": "", - "description": "A cookie used by Squeezely", - "retention": "1 year", + "description": "OCD_sqzl_vw_description", + "retention": "OCD_retention_1_year", "dataController": "Squeezely", "gdprUrl": "https://squeezely.tech/privacy", "wildcard": "0" @@ -12044,8 +12897,8 @@ "category": "Marketing", "name": "sqzllocal", "domain": "", - "description": "This is a cookie from the service Squeezely. It helps us with registering which pages you have visited and with sending you personalized ads", - "retention": "1 year", + "description": "OCD_sqzllocal_description", + "retention": "OCD_retention_1_year", "dataController": "Squeezely", "gdprUrl": "https://squeezely.tech/privacy", "wildcard": "0" @@ -12057,8 +12910,8 @@ "category": "Functional", "name": "belco-anonymous-id", "domain": "", - "description": "This cookie enables you to make use of the chat-function of our customer service-tool, so we can help you anytime.", - "retention": "1 year", + "description": "OCD_belco_anonymous_id_description", + "retention": "OCD_retention_1_year", "dataController": "Belco", "gdprUrl": "https://www.belco.nl/privacy-policy", "wildcard": "0" @@ -12070,8 +12923,8 @@ "category": "Functional", "name": "belco-cookies", "domain": "", - "description": "This cookie enables you to make use of the chat-function of our customer service-tool, so we can help you anytime.", - "retention": "1 year", + "description": "OCD_belco_cookies_description", + "retention": "OCD_retention_1_year", "dataController": "Belco", "gdprUrl": "https://www.belco.nl/privacy-policy", "wildcard": "0" @@ -12083,8 +12936,8 @@ "category": "Analytics", "name": "ABTasty", "domain": "", - "description": "This cookie sends all test data (visitorID, test and variant IDs, timestamps).", - "retention": "13 months", + "description": "OCD_ABTasty_description", + "retention": "OCD_retention_13_months", "dataController": "ABTasty", "gdprUrl": "https://www.abtasty.com/terms-of-use/", "wildcard": "0" @@ -12096,8 +12949,8 @@ "category": "Analytics", "name": "ABTastySession", "domain": "", - "description": "This cookie allows us to identify a unique session. It allows us to determine that a new session has begun for a given user.", - "retention": "session", + "description": "OCD_ABTastySession_description", + "retention": "OCD_retention_session", "dataController": "ABTasty", "gdprUrl": "https://www.abtasty.com/terms-of-use/", "wildcard": "0" @@ -12109,8 +12962,8 @@ "category": "Marketing", "name": "BCSessionID", "domain": "", - "description": "Unique identifier for the BlueConic profile.", - "retention": "1 year", + "description": "OCD_BCSessionID_description", + "retention": "OCD_retention_1_year", "dataController": "Blueconic.com", "gdprUrl": "https://www.blueconic.com/privacy-policy", "wildcard": "0" @@ -12122,8 +12975,8 @@ "category": "Marketing", "name": "BCTempID", "domain": "blueconic.net", - "description": "Temporary unique identifier for the BlueConic profile, removed after BCSessionID is created.", - "retention": "10 minutes", + "description": "OCD_BCTempID_description", + "retention": "OCD_retention_10_minutes", "dataController": "Blueconic.com", "gdprUrl": "https://www.blueconic.com/privacy-policy", "wildcard": "0" @@ -12135,8 +12988,8 @@ "category": "Functional", "name": "BCPermissionLevel", "domain": "", - "description": "Opt-in level (PERSONAL|ANONYMOUS|DO_NOT_TRACK)", - "retention": "1 year", + "description": "OCD_BCPermissionLevel_description", + "retention": "OCD_retention_1_year", "dataController": "Blueconic.com", "gdprUrl": "https://www.blueconic.com/privacy-policy", "wildcard": "0" @@ -12148,8 +13001,8 @@ "category": "Marketing", "name": "BCReferrerOverrule", "domain": "blueconic.net", - "description": "Stores a custom bcChannelIdentifier as referrer. For these channels the actual referrer points to the website and not the overrule. The overrule would be lost if not stored in this cookie.", - "retention": "1 year", + "description": "OCD_BCReferrerOverrule_description", + "retention": "OCD_retention_1_year", "dataController": "Blueconic.com", "gdprUrl": "https://www.blueconic.com/privacy-policy", "wildcard": "0" @@ -12161,8 +13014,8 @@ "category": "Marketing", "name": "BCRefusedObjectives", "domain": "blueconic.net", - "description": "Used to store the identifiers of BlueConic Objectives that were explicitly refused.", - "retention": "1 year", + "description": "OCD_BCRefusedObjectives_description", + "retention": "OCD_retention_1_year", "dataController": "Blueconic.com", "gdprUrl": "https://www.blueconic.com/privacy-policy", "wildcard": "0" @@ -12174,8 +13027,8 @@ "category": "Marketing", "name": "BCRevision", "domain": "", - "description": "Used to store requests that are sent to BlueConic, but haven't returned yet. On the next page view, if BCRevision still contains values, those requests are sent again, to prevent data loss. This information is now stored in localStorage; when this fails, the cookie solution is used as fallback.", - "retention": "1 year", + "description": "OCD_BCRevision_description", + "retention": "OCD_retention_1_year", "dataController": "Blueconic.com", "gdprUrl": "https://www.blueconic.com/privacy-policy", "wildcard": "0" @@ -12187,8 +13040,8 @@ "category": "Marketing", "name": "BCTracking", "domain": "", - "description": "Used for tracking the channel of an external tracker.", - "retention": "10 seconds", + "description": "OCD_BCTracking_description", + "retention": "OCD_retention_10_seconds", "dataController": "Blueconic.com", "gdprUrl": "https://www.blueconic.com/privacy-policy", "wildcard": "0" @@ -12200,8 +13053,8 @@ "category": "Marketing", "name": "bc_tstgrp", "domain": "", - "description": "Gathers information on the user’s behavior, preferences and other personal data, which is sent to a third-party marketing and analysis service, for optimization of the website’s advertisement, analysis and general traffic.", - "retention": "1 year", + "description": "OCD_bc_tstgrp_description", + "retention": "OCD_retention_1_year", "dataController": "Blueconic.com", "gdprUrl": "https://www.blueconic.com/privacy-policy", "wildcard": "0" @@ -12213,8 +13066,8 @@ "category": "Marketing", "name": "__adal_ca", "domain": "", - "description": "Stores which advertising campaign drove a user to visit, stores traffic source and campaign data.", - "retention": "6 months", + "description": "OCD___adal_ca_description", + "retention": "OCD_retention_6_months", "dataController": "Adalyser.com", "gdprUrl": "https://www.adalyser.com/en/cookies", "wildcard": "0" @@ -12226,8 +13079,8 @@ "category": "Marketing", "name": "__adal_cw", "domain": "", - "description": "Ties back conversion events to earlier visits, stores a visit timestamp.", - "retention": "7 days", + "description": "OCD___adal_cw_description", + "retention": "OCD_retention_7_days", "dataController": "Adalyser.com", "gdprUrl": "https://www.adalyser.com/en/cookies", "wildcard": "0" @@ -12239,8 +13092,8 @@ "category": "Marketing", "name": "__adal_id", "domain": "", - "description": "Uniquely identify a device, stores a generated Device ID.", - "retention": "2 years", + "description": "OCD___adal_id_description", + "retention": "OCD_retention_2_years", "dataController": "Adalyser.com", "gdprUrl": "https://www.adalyser.com/en/cookies", "wildcard": "0" @@ -12252,8 +13105,8 @@ "category": "Marketing", "name": "__adal_ses", "domain": "", - "description": "Determines whether there is an active session and which conversions have taken place in this session to prevent duplicates, stores a list of events in this session.", - "retention": "session", + "description": "OCD___adal_ses_description", + "retention": "OCD_retention_session", "dataController": "Adalyser.com", "gdprUrl": "https://www.adalyser.com/en/cookies", "wildcard": "0" @@ -12265,8 +13118,8 @@ "category": "Analytics", "name": "Pastease.passive.activated*", "domain": "", - "description": "The visitor is selected via this Mopinion cookie and the visitor sees the form.", - "retention": "1 month", + "description": "OCD_Pastease_passive_activated__description", + "retention": "OCD_retention_1_month", "dataController": "Mopinion.com", "gdprUrl": "https://mopinion.com/legal/policies/privacy-statements/", "wildcard": "1" @@ -12278,8 +13131,8 @@ "category": "Analytics", "name": "Pastease.passive.chance*", "domain": "", - "description": "This Mopinion cookie determines the chance that the visitor will see the form.", - "retention": "1 month", + "description": "OCD_Pastease_passive_chance__description", + "retention": "OCD_retention_1_month", "dataController": "Mopinion.com", "gdprUrl": "https://mopinion.com/legal/policies/privacy-statements/", "wildcard": "1" @@ -12291,8 +13144,8 @@ "category": "Analytics", "name": "AFFICHE_W", "domain": "weborama.fr", - "description": "Used by the advertising platform Weborama to determine the visitor’s interests based on pages visits, content clicked and other actions on the website.", - "retention": "3 months", + "description": "OCD_AFFICHE_W_description", + "retention": "OCD_retention_3_months", "dataController": "Weborama", "gdprUrl": "https://weborama.com/en/weborama-privacy-commitment/", "wildcard": "0" @@ -12304,8 +13157,8 @@ "category": "Marketing", "name": "matchadform", "domain": "w55c.net", - "description": "Presents the user with relevant content and advertisement. The service is provided by third-party advertisement hubs, which facilitate real-time bidding for advertisers.", - "retention": "29 days", + "description": "OCD_matchadform_description", + "retention": "OCD_retention_29_days", "dataController": "Roku", "gdprUrl": "https://www.roku.com/legal", "wildcard": "0" @@ -12317,8 +13170,8 @@ "category": "Marketing", "name": "wfivefivec", "domain": "w55c.net", - "description": "Collects data on the user's visits to the website, such as what pages have been loaded. The registered data is used for targeted ads.", - "retention": "13 months", + "description": "OCD_wfivefivec_description", + "retention": "OCD_retention_13_months", "dataController": "Roku", "gdprUrl": "https://www.roku.com/legal", "wildcard": "0" @@ -12330,8 +13183,8 @@ "category": "Marketing", "name": "UserID1", "domain": "adfarm3.adition.com", - "description": "Cookie sets a unique anonymous ID for a website visitor. This ID is used to recognize the user on different sessions and to track their activities on the website. The data collected is used for analysis purposes.", - "retention": "180 days", + "description": "OCD_UserID1_description", + "retention": "OCD_retention_180_days", "dataController": "Adition", "gdprUrl": "https://www.adition.com/kontakt/datenschutz/", "wildcard": "0" @@ -12343,8 +13196,8 @@ "category": "Marketing", "name": "arcki2", "domain": "audrte.com", - "description": "Collects data on user behaviour and interaction in order to optimize the website and make advertisement on the website more relevant.", - "retention": "14 days", + "description": "OCD_arcki2_description", + "retention": "OCD_retention_14_days", "dataController": "Audrte", "gdprUrl": "", "wildcard": "0" @@ -12356,8 +13209,8 @@ "category": "Marketing", "name": "arcki2_adform", "domain": "audrte.com", - "description": "Presents the user with relevant content and advertisement. The service is provided by third-party advertisement hubs, which facilitate real-time bidding for advertisers.", - "retention": "14 days", + "description": "OCD_arcki2_adform_description", + "retention": "OCD_retention_14_days", "dataController": "Audrte", "gdprUrl": "", "wildcard": "0" @@ -12369,8 +13222,8 @@ "category": "Marketing", "name": "arcki2_ddp", "domain": "audrte.com", - "description": "Presents the user with relevant content and advertisement. The service is provided by third-party advertisement hubs, which facilitate real-time bidding for advertisers.", - "retention": "14 days", + "description": "OCD_arcki2_ddp_description", + "retention": "OCD_retention_14_days", "dataController": "Audrte", "gdprUrl": "", "wildcard": "0" @@ -12382,8 +13235,8 @@ "category": "Analytics", "name": "_sn_a", "domain": "", - "description": "This is the cookie used for visitor analytics tracking. It sets a visitor ID so that the visitor can be identified across sessions. This enables all visitor related analytics data to be shown on the analytics pages in your Dashboard. Note that if a visitor is opted out of this cookie, you will still be able to see how many views and conversions your campaigns had, just not any visitor-related data like referrer, location, and so on.", - "retention": "1 year", + "description": "OCD__sn_a_description", + "retention": "OCD_retention_1_year", "dataController": "Sleeknote", "gdprUrl": "https://sleeknote.com/privacy-policy", "wildcard": "0" @@ -12395,8 +13248,8 @@ "category": "Marketing", "name": "_sn_m", "domain": "", - "description": " This cookie contains information used for marketing related targeting options. Targeting options like the referrer, UTM, or geo-location. Note that if this cookie is opted out, the marketing targeting options will not work, and the campaign will default to not show.", - "retention": "1 year", + "description": "OCD__sn_m_description", + "retention": "OCD_retention_1_year", "dataController": "Sleeknote", "gdprUrl": "https://sleeknote.com/privacy-policy", "wildcard": "0" @@ -12408,8 +13261,8 @@ "category": "Functional", "name": "_sn_n", "domain": "", - "description": "This is the necessary cookie set by Sleeknote, as it contains technical information so that the campaigns can show properly and tracking works properly.", - "retention": "1 year", + "description": "OCD__sn_n_description", + "retention": "OCD_retention_1_year", "dataController": "Sleeknote", "gdprUrl": "https://sleeknote.com/privacy-policy", "wildcard": "0" @@ -12421,8 +13274,8 @@ "category": "Functional", "name": "apbct_antibot", "domain": "", - "description": "This cookie is used to distinguish between humans and bots. This is beneficial for the website, in order to make valid reports on the use of their website.", - "retention": "session", + "description": "OCD_apbct_antibot_description", + "retention": "OCD_retention_session", "dataController": "CleanTalk", "gdprUrl": "https://cleantalk.org/publicoffer#privacy", "wildcard": "0" @@ -12434,8 +13287,8 @@ "category": "Functional", "name": "ct_check_js", "domain": "", - "description": "Used in order to detect spam and improve the website's security.", - "retention": "session", + "description": "OCD_ct_check_js_description", + "retention": "OCD_retention_session", "dataController": "CleanTalk", "gdprUrl": "https://cleantalk.org/publicoffer#privacy", "wildcard": "0" @@ -12447,8 +13300,8 @@ "category": "Functional", "name": "ct_fkp_timestamp", "domain": "", - "description": "Used in order to detect spam and improve the website's security. Does not store visitor specific data.", - "retention": "session", + "description": "OCD_ct_fkp_timestamp_description", + "retention": "OCD_retention_session", "dataController": "CleanTalk", "gdprUrl": "https://cleantalk.org/publicoffer#privacy", "wildcard": "0" @@ -12460,8 +13313,8 @@ "category": "Functional", "name": "ct_has_scrolled", "domain": "", - "description": "This cookie is used to distinguish between humans and bots.", - "retention": "session", + "description": "OCD_ct_has_scrolled_description", + "retention": "OCD_retention_session", "dataController": "CleanTalk", "gdprUrl": "https://cleantalk.org/publicoffer#privacy", "wildcard": "0" @@ -12473,8 +13326,8 @@ "category": "Functional", "name": "ct_pointer_data", "domain": "", - "description": "Used in order to detect spam and improve the website's security. Does not store visitor specific data.", - "retention": "session", + "description": "OCD_ct_pointer_data_description", + "retention": "OCD_retention_session", "dataController": "CleanTalk", "gdprUrl": "https://cleantalk.org/publicoffer#privacy", "wildcard": "0" @@ -12486,8 +13339,8 @@ "category": "Functional", "name": "ct_ps_timestamp", "domain": "", - "description": "Used in order to detect spam and improve the website's security. Does not store visitor specific data.", - "retention": "session", + "description": "OCD_ct_ps_timestamp_description", + "retention": "OCD_retention_session", "dataController": "CleanTalk", "gdprUrl": "https://cleantalk.org/publicoffer#privacy", "wildcard": "0" @@ -12499,8 +13352,8 @@ "category": "Functional", "name": "ct_timezone", "domain": "", - "description": "Used in order to detect spam and improve the website's security.", - "retention": "session", + "description": "OCD_ct_timezone_description", + "retention": "OCD_retention_session", "dataController": "CleanTalk", "gdprUrl": "https://cleantalk.org/publicoffer#privacy", "wildcard": "0" @@ -12512,8 +13365,8 @@ "category": "Marketing", "name": "__kla_id", "domain": "", - "description": "When Klaviyo’s JavaScript is enabled, the __kla_id cookie can track and identify site visitors through an auto-generated ID. This cookie can temporarily hold personally identifiable information. Once a visitor is identified, the cookie can pass their data into Klaviyo.", - "retention": "2 years", + "description": "OCD___kla_id_description", + "retention": "OCD_retention_2_years", "dataController": "Klaviyo", "gdprUrl": "https://www.klaviyo.com/legal", "wildcard": "0" @@ -12525,8 +13378,8 @@ "category": "Marketing", "name": "__trf.src", "domain": "", - "description": "Registers how the user has reached the website to enable pay-out of referral commission fees to partners.", - "retention": "1 year", + "description": "OCD___trf_src_description", + "retention": "OCD_retention_1_year", "dataController": "Amazon", "gdprUrl": "https://www.amazon.com/gp/help/customer/display.html/ref=footer_privacy?ie=UTF8&nodeId=468496", "wildcard": "0" @@ -12538,8 +13391,8 @@ "category": "Analytics", "name": "_ALGOLIA", "domain": "", - "description": "Identifies users for your Search Analytics and Personalization.", - "retention": "365 days", + "description": "OCD__ALGOLIA_description", + "retention": "OCD_retention_365_days", "dataController": "Algolia", "gdprUrl": "https://www.algolia.com/policies/privacy/", "wildcard": "0" @@ -12551,8 +13404,8 @@ "category": "Functional", "name": "_csrf", "domain": "stonly.com", - "description": "This cookie is used to prevent Cross-site request forgery (often abbreviated as CSRF) attacks of the website.", - "retention": "session", + "description": "OCD__csrf_description", + "retention": "OCD_retention_session", "dataController": "Stonly", "gdprUrl": "https://stonly.com/privacy", "wildcard": "0" @@ -12564,8 +13417,8 @@ "category": "Marketing", "name": "_tt_enable_cookie", "domain": "", - "description": "Tracking cookie used by TikTok to identify a visitor", - "retention": "389 days", + "description": "OCD__tt_enable_cookie_description", + "retention": "OCD_retention_389_days", "dataController": "TikTok", "gdprUrl": "https://www.tiktok.com/legal/page/eea/privacy-policy/en?lang=en", "wildcard": "0" @@ -12577,8 +13430,8 @@ "category": "Marketing", "name": "_ttp", "domain": "", - "description": "Tracking cookie used by TikTok to identify a visitor", - "retention": "389 days", + "description": "OCD__ttp_description", + "retention": "OCD_retention_389_days", "dataController": "TikTok", "gdprUrl": "https://www.tiktok.com/legal/page/eea/privacy-policy/en?lang=en", "wildcard": "0" @@ -12590,8 +13443,8 @@ "category": "Marketing", "name": "MONITOR_WEB_ID", "domain": "mon-va.byteoversea.com", - "description": "Used by the social networking service, TikTok, for tracking the use of embedded services.", - "retention": "3 months", + "description": "OCD_MONITOR_WEB_ID_description", + "retention": "OCD_retention_3_months", "dataController": "TikTok", "gdprUrl": "https://www.tiktok.com/legal/page/eea/privacy-policy/en?lang=en", "wildcard": "0" @@ -12603,8 +13456,8 @@ "category": "Marketing", "name": "msToken", "domain": "tiktok.com", - "description": "Collects information on user behaviour on multiple websites. This information is used in order to optimize the relevance of advertisement on the website.", - "retention": "9 days", + "description": "OCD_msToken_description", + "retention": "OCD_retention_9_days", "dataController": "TikTok", "gdprUrl": "https://www.tiktok.com/legal/page/eea/privacy-policy/en?lang=en", "wildcard": "0" @@ -12616,8 +13469,8 @@ "category": "Marketing", "name": "ttwid", "domain": "tiktok.com", - "description": "Used by the social networking service, TikTok, for tracking the use of embedded services.", - "retention": "1 year", + "description": "OCD_ttwid_description", + "retention": "OCD_retention_1_year", "dataController": "TikTok", "gdprUrl": "https://www.tiktok.com/legal/page/eea/privacy-policy/en?lang=en", "wildcard": "0" @@ -12629,8 +13482,8 @@ "category": "Analytics", "name": "ahoy_visit", "domain": "", - "description": "Registers statistical data on visitors behaviour on the website. Used for internal analytics by the website operator.", - "retention": "239 days", + "description": "OCD_ahoy_visit_description", + "retention": "OCD_retention_239_days", "dataController": "Ahoy", "gdprUrl": "", "wildcard": "0" @@ -12642,8 +13495,8 @@ "category": "Analytics", "name": "ahoy_visitor", "domain": "", - "description": "Registers statistical data on visitors behaviour on the website. Used for internal analytics by the website operator.", - "retention": "239 days", + "description": "OCD_ahoy_visitor_description", + "retention": "OCD_retention_239_days", "dataController": "Ahoy", "gdprUrl": "", "wildcard": "0" @@ -12655,8 +13508,8 @@ "category": "Functional", "name": "auth0", "domain": "", - "description": "Used to implement the Auth0 session layer.", - "retention": "session", + "description": "OCD_auth0_description", + "retention": "OCD_retention_session", "dataController": "Auth0", "gdprUrl": "", "wildcard": "0" @@ -12668,8 +13521,8 @@ "category": "Functional", "name": "auth0_compat", "domain": "", - "description": "Fallback cookie for single sign-on on browsers that don't support the sameSite=None attribute.", - "retention": "session", + "description": "OCD_auth0_compat_description", + "retention": "OCD_retention_session", "dataController": "Auth0", "gdprUrl": "", "wildcard": "0" @@ -12681,8 +13534,8 @@ "category": "Functional", "name": "auth0-mf", "domain": "", - "description": "Used to establish the trust level for a given device.", - "retention": "session", + "description": "OCD_auth0_mf_description", + "retention": "OCD_retention_session", "dataController": "Auth0", "gdprUrl": "", "wildcard": "0" @@ -12694,8 +13547,8 @@ "category": "Functional", "name": "auth0-mf_compat", "domain": "", - "description": "Fallback cookie for multi-factor authentication on browsers that don't support the sameSite=None attribute.", - "retention": "session", + "description": "OCD_auth0_mf_compat_description", + "retention": "OCD_retention_session", "dataController": "Auth0", "gdprUrl": "", "wildcard": "0" @@ -12707,8 +13560,8 @@ "category": "Functional", "name": "a0_users:sess", "domain": "", - "description": "Used for CSRF protection in Classic Universal Login flows.", - "retention": "session", + "description": "OCD_a0_users_sess_description", + "retention": "OCD_retention_session", "dataController": "Auth0", "gdprUrl": "", "wildcard": "0" @@ -12720,8 +13573,8 @@ "category": "Functional", "name": "a0_users:sess.sig", "domain": "", - "description": "Used for CSRF protection in Classic Universal Login flows.", - "retention": "session", + "description": "OCD_a0_users_sess_sig_description", + "retention": "OCD_retention_session", "dataController": "Auth0", "gdprUrl": "", "wildcard": "0" @@ -12733,8 +13586,8 @@ "category": "Functional", "name": "did", "domain": "", - "description": "Device identification for attack protection.", - "retention": "session", + "description": "OCD_did_description", + "retention": "OCD_retention_session", "dataController": "Auth0", "gdprUrl": "", "wildcard": "0" @@ -12746,8 +13599,8 @@ "category": "Functional", "name": "did_compat", "domain": "", - "description": "Fallback cookie for anomaly detection on browsers that don't support the sameSite=None attribute.", - "retention": "session", + "description": "OCD_did_compat_description", + "retention": "OCD_retention_session", "dataController": "Auth0", "gdprUrl": "", "wildcard": "0" @@ -12759,8 +13612,8 @@ "category": "Marketing", "name": "HAPLB8*", "domain": "go.sonobi.com", - "description": "Sonobi sets this cookie for advertising purposes.", - "retention": "session", + "description": "OCD_HAPLB8__description", + "retention": "OCD_retention_session", "dataController": "Sonobi", "gdprUrl": "https://sonobi.com/privacy-policy/", "wildcard": "1" @@ -12772,8 +13625,8 @@ "category": "Analytics", "name": "atidvisitor", "domain": "", - "description": "List of numsites encountered by the visitor and storage of identified visitor information", - "retention": "6 months by default, modifiable", + "description": "OCD_atidvisitor_description", + "retention": "OCD_retention_6_months_by_default__modifiable", "dataController": "AT Internet", "gdprUrl": "", "wildcard": "0" @@ -12785,8 +13638,8 @@ "category": "Analytics", "name": "atuserid", "domain": "", - "description": "Visitor ID for client-side cookie sites", - "retention": "13 months by default, modifiable", + "description": "OCD_atuserid_description", + "retention": "OCD_retention_13_months_by_default__modifiable", "dataController": "AT Internet", "gdprUrl": "", "wildcard": "0" @@ -12798,8 +13651,8 @@ "category": "Functional", "name": "ja_purity_tpl", "domain": "", - "description": "Indicates the website uses a JoomlArt template", - "retention": "355 days", + "description": "OCD_ja_purity_tpl_description", + "retention": "OCD_retention_355_days", "dataController": "JoomlArt", "gdprUrl": "", "wildcard": "0" @@ -12811,8 +13664,8 @@ "category": "Functional", "name": "ja_purity_ii_tpl", "domain": "", - "description": "Indicates the website uses a JoomlArt template", - "retention": "355 days", + "description": "OCD_ja_purity_ii_tpl_description", + "retention": "OCD_retention_355_days", "dataController": "JoomlArt", "gdprUrl": "", "wildcard": "0" @@ -12824,8 +13677,8 @@ "category": "Functional", "name": "BIGipServer*", "domain": "", - "description": "Used by the f5 BIG-IP load balancer to ensure one user's request is always handled by the same server to maintain a consistent user experience", - "retention": "Unknown", + "description": "OCD_BIGipServer__description", + "retention": "OCD_retention_Unknown", "dataController": "f5", "gdprUrl": "", "wildcard": "1" @@ -12837,8 +13690,8 @@ "category": "Functional", "name": "active_template::*", "domain": "", - "description": "Used to store which template you are viewing on this website.", - "retention": "2 days", + "description": "OCD_active_template____description", + "retention": "OCD_retention_2_days", "dataController": "Ezoic", "gdprUrl": "https://www.ezoic.com/privacy-policy/", "wildcard": "1" @@ -12850,8 +13703,8 @@ "category": "Functional", "name": "ezds", "domain": "", - "description": "Used to store the pixel size of your screen to help personalize your experience and ensure content fits.", - "retention": "1 year", + "description": "OCD_ezds_description", + "retention": "OCD_retention_1_year", "dataController": "Ezoic", "gdprUrl": "https://www.ezoic.com/privacy-policy/", "wildcard": "0" @@ -12863,8 +13716,8 @@ "category": "Functional", "name": "ezoab_*", "domain": "", - "description": "Used to split test different features and functionality.", - "retention": "2 hours", + "description": "OCD_ezoab___description", + "retention": "OCD_retention_2_hours", "dataController": "Ezoic", "gdprUrl": "https://www.ezoic.com/privacy-policy/", "wildcard": "1" @@ -12876,8 +13729,8 @@ "category": "Analytics", "name": "ezoadgid_*", "domain": "", - "description": "Stores an ID that connects you to an age and gender category.", - "retention": "30 minutes", + "description": "OCD_ezoadgid___description", + "retention": "OCD_retention_30_minutes", "dataController": "Ezoic", "gdprUrl": "https://www.ezoic.com/privacy-policy/", "wildcard": "1" @@ -12889,8 +13742,8 @@ "category": "Functional", "name": "ezohw", "domain": "", - "description": "Used to store the pixel size of your browser to help personalize your experience and ensure content fits.", - "retention": "1 year", + "description": "OCD_ezohw_description", + "retention": "OCD_retention_1_year", "dataController": "Ezoic", "gdprUrl": "https://www.ezoic.com/privacy-policy/", "wildcard": "0" @@ -12902,8 +13755,8 @@ "category": "Analytics", "name": "ezopvc_*", "domain": "", - "description": "Used to store the number of pages that you have viewed on this site in this session.", - "retention": "30 minutes", + "description": "OCD_ezopvc___description", + "retention": "OCD_retention_30_minutes", "dataController": "Ezoic", "gdprUrl": "https://www.ezoic.com/privacy-policy/", "wildcard": "1" @@ -12915,8 +13768,8 @@ "category": "Analytics", "name": "ezoref_*", "domain": "", - "description": "Used to store the referring domain (the website you were at before you can to this website).", - "retention": "2 hours", + "description": "OCD_ezoref___description", + "retention": "OCD_retention_2_hours", "dataController": "Ezoic", "gdprUrl": "https://www.ezoic.com/privacy-policy/", "wildcard": "1" @@ -12928,8 +13781,8 @@ "category": "Functional", "name": "ezostid_*", "domain": "", - "description": "Used to test different features and functionality and to record which features and functionality are available to you so you receive a consistent experience.", - "retention": "Unknown", + "description": "OCD_ezostid___description", + "retention": "OCD_retention_Unknown", "dataController": "Ezoic", "gdprUrl": "https://www.ezoic.com/privacy-policy/", "wildcard": "1" @@ -12941,8 +13794,8 @@ "category": "Marketing", "name": "ezosuigeneris*", "domain": "", - "description": "Used to uniquely identify you across different websites on the internet so your experience can be customized.", - "retention": "1 year", + "description": "OCD_ezosuigeneris__description", + "retention": "OCD_retention_1_year", "dataController": "Ezoic", "gdprUrl": "https://www.ezoic.com/privacy-policy/", "wildcard": "1" @@ -12954,8 +13807,8 @@ "category": "Marketing", "name": "ezosuibasgeneris-1", "domain": "", - "description": "Used to uniquely identify you across different websites on the internet so your experience can be customized.", - "retention": "1 year", + "description": "OCD_ezosuibasgeneris_1_description", + "retention": "OCD_retention_1_year", "dataController": "Ezoic", "gdprUrl": "https://www.ezoic.com/privacy-policy/", "wildcard": "0" @@ -12967,8 +13820,8 @@ "category": "Analytics", "name": "ezouid_*", "domain": "", - "description": "Used to uniquely identify you as a visitor on this website. Used for analytics and personalization of your experience.", - "retention": "Unknown", + "description": "OCD_ezouid___description", + "retention": "OCD_retention_Unknown", "dataController": "Ezoic", "gdprUrl": "https://www.ezoic.com/privacy-policy/", "wildcard": "1" @@ -12980,8 +13833,8 @@ "category": "Analytics", "name": "ezovid_*", "domain": "", - "description": "Used to uniquely identify a visit by you to this website. Used for analytics and personalization of your experience.", - "retention": "30 minutes", + "description": "OCD_ezovid___description", + "retention": "OCD_retention_30_minutes", "dataController": "Ezoic", "gdprUrl": "https://www.ezoic.com/privacy-policy/", "wildcard": "1" @@ -12993,8 +13846,8 @@ "category": "Analytics", "name": "ezovuuid_*", "domain": "", - "description": "Used to uniquely identify a visit by you to this website. Used for analytics and personalization of your experience.", - "retention": "30 minutes", + "description": "OCD_ezovuuid___description", + "retention": "OCD_retention_30_minutes", "dataController": "Ezoic", "gdprUrl": "https://www.ezoic.com/privacy-policy/", "wildcard": "1" @@ -13006,8 +13859,8 @@ "category": "Analytics", "name": "ezovuuidtime_*", "domain": "", - "description": "Used to record the time of your visit to this website so different visits can be differentiated from each other.", - "retention": "2 days", + "description": "OCD_ezovuuidtime___description", + "retention": "OCD_retention_2_days", "dataController": "Ezoic", "gdprUrl": "https://www.ezoic.com/privacy-policy/", "wildcard": "1" @@ -13019,8 +13872,8 @@ "category": "Analytics", "name": "ezux_et_*", "domain": "", - "description": "Used to record the amount of time that you engaged with content on this website. Used for analytics purposes to improve user experience.", - "retention": "Unknown", + "description": "OCD_ezux_et___description", + "retention": "OCD_retention_Unknown", "dataController": "Ezoic", "gdprUrl": "https://www.ezoic.com/privacy-policy/", "wildcard": "1" @@ -13032,8 +13885,8 @@ "category": "Analytics", "name": "ezux_ifep_*", "domain": "", - "description": "Used to record whether you have engaged with the content on this site. Used for analytics purposes to improve user experience.", - "retention": "Unknown", + "description": "OCD_ezux_ifep___description", + "retention": "OCD_retention_Unknown", "dataController": "Ezoic", "gdprUrl": "https://www.ezoic.com/privacy-policy/", "wildcard": "1" @@ -13045,8 +13898,8 @@ "category": "Analytics", "name": "ezux_lpl_*", "domain": "", - "description": "Used to record the time that you loaded the last page on this website.", - "retention": "Unknown", + "description": "OCD_ezux_lpl___description", + "retention": "OCD_retention_Unknown", "dataController": "Ezoic", "gdprUrl": "https://www.ezoic.com/privacy-policy/", "wildcard": "1" @@ -13058,8 +13911,8 @@ "category": "Analytics", "name": "ezux_tos_*", "domain": "", - "description": "Used to record the amount of time you have spent on this website.", - "retention": "Unknown", + "description": "OCD_ezux_tos___description", + "retention": "OCD_retention_Unknown", "dataController": "Ezoic", "gdprUrl": "https://www.ezoic.com/privacy-policy/", "wildcard": "1" @@ -13071,8 +13924,8 @@ "category": "Functional", "name": "ezoawesome_*", "domain": "", - "description": "Used for fraud and invalid activity detection.", - "retention": "Unknown", + "description": "OCD_ezoawesome___description", + "retention": "OCD_retention_Unknown", "dataController": "Ezoic", "gdprUrl": "https://www.ezoic.com/privacy-policy/", "wildcard": "1" @@ -13084,8 +13937,8 @@ "category": "Functional", "name": "easysize_button_loaded_for_user", "domain": "", - "description": "Sizing display for products", - "retention": "session", + "description": "OCD_easysize_button_loaded_for_user_description", + "retention": "OCD_retention_session", "dataController": "Easysize.me", "gdprUrl": "https://www.easysize.me/privacy-policy", "wildcard": "0" @@ -13097,8 +13950,8 @@ "category": "Functional", "name": "swym-email", "domain": "", - "description": "This stores the email address when the shopper logs into the store or when the shopper authenticates their wishlist or subscribes for a back-in-stock alert.", - "retention": "1 year", + "description": "OCD_swym_email_description", + "retention": "OCD_retention_1_year", "dataController": "Swym", "gdprUrl": "https://swym.it/privacy/", "wildcard": "0" @@ -13110,8 +13963,8 @@ "category": "Functional", "name": "swym-session-id", "domain": "", - "description": "This is a general-purpose platform session cookie used to maintain an anonymous user session.", - "retention": "30 mins", + "description": "OCD_swym_session_id_description", + "retention": "OCD_retention_30_mins", "dataController": "Swym", "gdprUrl": "https://swym.it/privacy/", "wildcard": "0" @@ -13123,8 +13976,8 @@ "category": "Functional", "name": "swym-pid", "domain": "", - "description": "Unique identifier to track merchants and their wishlist settings.", - "retention": "1 year", + "description": "OCD_swym_pid_description", + "retention": "OCD_retention_1_year", "dataController": "Swym", "gdprUrl": "https://swym.it/privacy/", "wildcard": "0" @@ -13136,8 +13989,8 @@ "category": "Functional", "name": "swym-swymRegid", "domain": "", - "description": "This cookie is used to store an encrypted version of the user's device ID and information on the user’s session.", - "retention": "1 year", + "description": "OCD_swym_swymRegid_description", + "retention": "OCD_retention_1_year", "dataController": "Swym", "gdprUrl": "https://swym.it/privacy/", "wildcard": "0" @@ -13149,8 +14002,8 @@ "category": "Functional", "name": "swym-cu_ct", "domain": "", - "description": "Related to custom cart experience from Swym on the wishlist.", - "retention": "30 mins", + "description": "OCD_swym_cu_ct_description", + "retention": "OCD_retention_30_mins", "dataController": "Swym", "gdprUrl": "https://swym.it/privacy/", "wildcard": "0" @@ -13162,8 +14015,8 @@ "category": "Functional", "name": "swym-o_s", "domain": "", - "description": "Related to swym app versioning systems.", - "retention": "1 year", + "description": "OCD_swym_o_s_description", + "retention": "OCD_retention_1_year", "dataController": "Swym", "gdprUrl": "https://swym.it/privacy/", "wildcard": "0" @@ -13175,8 +14028,8 @@ "category": "Functional", "name": "swym-instrumentMap", "domain": "", - "description": "Related to Wishlist instrumentation for identification of API.", - "retention": "30 mins", + "description": "OCD_swym_instrumentMap_description", + "retention": "OCD_retention_30_mins", "dataController": "Swym", "gdprUrl": "https://swym.it/privacy/", "wildcard": "0" @@ -13188,8 +14041,8 @@ "category": "Functional", "name": "swym-ol_ct", "domain": "", - "description": "Related to swym cart functionality.", - "retention": "30 mins", + "description": "OCD_swym_ol_ct_description", + "retention": "OCD_retention_30_mins", "dataController": "Swym", "gdprUrl": "https://swym.it/privacy/", "wildcard": "0" @@ -13201,8 +14054,8 @@ "category": "Functional", "name": "swym-v-ckd", "domain": "", - "description": "Related to swym app versioning systems.", - "retention": "1 year", + "description": "OCD_swym_v_ckd_description", + "retention": "OCD_retention_1_year", "dataController": "Swym", "gdprUrl": "https://swym.it/privacy/", "wildcard": "0" @@ -13214,8 +14067,8 @@ "category": "Functional", "name": "swym-tpermts", "domain": "", - "description": "Related to asking user permission for marketing", - "retention": "1 year", + "description": "OCD_swym_tpermts_description", + "retention": "OCD_retention_1_year", "dataController": "Swym", "gdprUrl": "https://swym.it/privacy/", "wildcard": "0" @@ -13227,8 +14080,8 @@ "category": "Functional", "name": "swym-u_pref", "domain": "", - "description": "Related to user’s marketing preference.", - "retention": "1 year", + "description": "OCD_swym_u_pref_description", + "retention": "OCD_retention_1_year", "dataController": "Swym", "gdprUrl": "https://swym.it/privacy/", "wildcard": "0" @@ -13240,8 +14093,8 @@ "category": "Functional", "name": "swym-weml", "domain": "", - "description": "Related to user’s email address for Swym apps", - "retention": "1 year", + "description": "OCD_swym_weml_description", + "retention": "OCD_retention_1_year", "dataController": "Swym", "gdprUrl": "https://swym.it/privacy/", "wildcard": "0" @@ -13253,8 +14106,8 @@ "category": "Functional", "name": "enforce_policy", "domain": "paypal.com", - "description": "This cookie is provided by Paypal. The cookie is used in context with transactions on the website - The cookie is necessary for secure transactions.", - "retention": "1 year", + "description": "OCD_enforce_policy_description", + "retention": "OCD_retention_1_year", "dataController": "PayPal", "gdprUrl": "https://www.paypal.com/privacy-center", "wildcard": "0" @@ -13266,8 +14119,8 @@ "category": "Functional", "name": "x-pp-s", "domain": "paypal.com", - "description": "This cookie is provided by PayPal and supports payment services in the website.", - "retention": "session", + "description": "OCD_x_pp_s_description", + "retention": "OCD_retention_session", "dataController": "PayPal", "gdprUrl": "https://www.paypal.com/privacy-center", "wildcard": "0" @@ -13279,11 +14132,22 @@ "category": "Functional", "name": "ts", "domain": "paypal.com", - "description": "This cookie is generally provided by PayPal and supports payment services on the website", - "retention": "3 years", + "description": "OCD_ts_description", + "retention": "OCD_retention_3_years", "dataController": "PayPal", "gdprUrl": "https://www.paypal.com/privacy-center", "wildcard": "0" + }, + { + "platform": "CreativeCDN", + "category": "Functional", + "name": "ts", + "domain": "creativecdn.com", + "description": "This cookie is associated with creativecdn.com. It is provided by PayPal and supports payment services in the website.", + "retention": "365 days", + "dataController": "RTB House", + "gdprUrl": "https://rtbhouse.com/privacy-center", + "wildcard": "0" } ], "ts_c": [ @@ -13292,8 +14156,8 @@ "category": "Functional", "name": "ts_c", "domain": "paypal.com", - "description": "This cookie is provided by Paypal. The cookie is used in context with transactions on the website.", - "retention": "3 years", + "description": "OCD_ts_c_description", + "retention": "OCD_retention_3_years", "dataController": "PayPal", "gdprUrl": "https://www.paypal.com/privacy-center", "wildcard": "0" @@ -13305,8 +14169,8 @@ "category": "Functional", "name": "tsrce", "domain": "paypal.com", - "description": "This cookie is generally provided by PayPal and supports payment services on the website.", - "retention": "session", + "description": "OCD_tsrce_description", + "retention": "OCD_retention_session", "dataController": "PayPal", "gdprUrl": "https://www.paypal.com/privacy-center", "wildcard": "0" @@ -13318,8 +14182,8 @@ "category": "Functional", "name": "nsid", "domain": "paypal.com", - "description": "Cookie for fraud detection. When making a payment via PayPal these cookies are issued – PayPal session/security", - "retention": "session", + "description": "OCD_nsid_description", + "retention": "OCD_retention_session", "dataController": "PayPal", "gdprUrl": "https://www.paypal.com/privacy-center", "wildcard": "0" @@ -13331,8 +14195,8 @@ "category": "Functional", "name": "X-PP-SILOVER", "domain": "paypal.com", - "description": "This cookie is generally provided by PayPal and supports payment services on the website.", - "retention": "30 minutes", + "description": "OCD_X_PP_SILOVER_description", + "retention": "OCD_retention_30_minutes", "dataController": "PayPal", "gdprUrl": "https://www.paypal.com/privacy-center", "wildcard": "0" @@ -13344,8 +14208,8 @@ "category": "Functional", "name": "X-PP-L7", "domain": "paypal.com", - "description": "Paypal - These cookies are essential in order to enable you to move around the website and use its features, such as accessing secure areas of the website. Without these cookies services you have asked for, like shopping baskets or e-billing, cannot be provided.", - "retention": "session", + "description": "OCD_X_PP_L7_description", + "retention": "OCD_retention_session", "dataController": "PayPal", "gdprUrl": "https://www.paypal.com/privacy-center", "wildcard": "0" @@ -13357,8 +14221,8 @@ "category": "Functional", "name": "l7_az", "domain": "paypal.com", - "description": "This cookie is necessary for the PayPal login-function on the website.", - "retention": "1 day", + "description": "OCD_l7_az_description", + "retention": "OCD_retention_1_day", "dataController": "PayPal", "gdprUrl": "https://www.paypal.com/privacy-center", "wildcard": "0" @@ -13370,8 +14234,8 @@ "category": "Functional", "name": "SERVERID", "domain": "", - "description": "Load balancer cookie", - "retention": "session", + "description": "OCD_SERVERID_description", + "retention": "OCD_retention_session", "dataController": "HAProxy", "gdprUrl": "", "wildcard": "0" @@ -13383,8 +14247,8 @@ "category": "Marketing", "name": "arcki2*", "domain": "audrte.com", - "description": "Collects data on user behaviour and interaction in order to optimize the website and make advertisement on the website more relevant.", - "retention": "15 days", + "description": "OCD_arcki2__description", + "retention": "OCD_retention_15_days", "dataController": "Audrte.com", "gdprUrl": "", "wildcard": "1" @@ -13396,8 +14260,8 @@ "category": "Marketing", "name": "v_usr", "domain": "e-volution.ai", - "description": "Collects data about the user's visit to the site, such as the number of returning visits and which pages are read. The purpose is to deliver targeted ads.", - "retention": "13 days", + "description": "OCD_v_usr_description", + "retention": "OCD_retention_13_days", "dataController": "E-volution.ai", "gdprUrl": "https://e-volution.ai/privacy/", "wildcard": "0" @@ -13409,8 +14273,8 @@ "category": "Marketing", "name": "idsync-bsw-uid-s", "domain": "live.streamtheworld.com", - "description": "Presents the user with relevant content and advertisement. The service is provided by third-party advertisement hubs, which facilitate real-time bidding for advertisers.", - "retention": "6 days", + "description": "OCD_idsync_bsw_uid_s_description", + "retention": "OCD_retention_6_days", "dataController": "StreamTheWorld", "gdprUrl": "", "wildcard": "0" @@ -13422,8 +14286,8 @@ "category": "Marketing", "name": "done_redirects*", "domain": "onaudience.com", - "description": "Used to monitor website performance for statistical purposes.", - "retention": "1 day", + "description": "OCD_done_redirects__description", + "retention": "OCD_retention_1_day", "dataController": "OnAudience", "gdprUrl": "https://www.onaudience.com/privacy-policy", "wildcard": "1" @@ -13435,8 +14299,8 @@ "category": "Marketing", "name": "vidoomy-uids", "domain": "vidoomy.com", - "description": "Used in context with video-advertisement. The cookie limits the number of times a user is shown the same advertisement. The cookie is also used to ensure relevance of the video-advertisement to the specific user.", - "retention": "1 year", + "description": "OCD_vidoomy_uids_description", + "retention": "OCD_retention_1_year", "dataController": "Vidoomy", "gdprUrl": "https://www.vidoomy.com/privacypolicy-en.html", "wildcard": "0" @@ -13448,8 +14312,8 @@ "category": "Functional", "name": "bbuserid", "domain": "", - "description": "Used to store the ID of the logged in user.", - "retention": "Unknown", + "description": "OCD_bbuserid_description", + "retention": "OCD_retention_Unknown", "dataController": "vBulletin", "gdprUrl": "https://www.internetbrands.com/privacy/privacy-main", "wildcard": "0" @@ -13461,8 +14325,8 @@ "category": "Functional", "name": "bbpassword", "domain": "", - "description": "Used to store a hash of the logged in user's password.", - "retention": "Unknown", + "description": "OCD_bbpassword_description", + "retention": "OCD_retention_Unknown", "dataController": "vBulletin", "gdprUrl": "https://www.internetbrands.com/privacy/privacy-main", "wildcard": "0" @@ -13474,8 +14338,8 @@ "category": "Functional", "name": "bbsessionhash", "domain": "", - "description": "Used to track the current session from the database.", - "retention": "Session", + "description": "OCD_bbsessionhash_description", + "retention": "OCD_retention_Session", "dataController": "vBulletin", "gdprUrl": "https://www.internetbrands.com/privacy/privacy-main", "wildcard": "0" @@ -13487,8 +14351,8 @@ "category": "Functional", "name": "bbcpsessionhash", "domain": "", - "description": "Used to track the current administrator session from the database.", - "retention": "Session", + "description": "OCD_bbcpsessionhash_description", + "retention": "OCD_retention_Session", "dataController": "vBulletin", "gdprUrl": "https://www.internetbrands.com/privacy/privacy-main", "wildcard": "0" @@ -13500,8 +14364,8 @@ "category": "Functional", "name": "bbnp_notices_displayed", "domain": "", - "description": "Used to keep track of notices to display to the client.", - "retention": "1 year", + "description": "OCD_bbnp_notices_displayed_description", + "retention": "OCD_retention_1_year", "dataController": "vBulletin", "gdprUrl": "https://www.internetbrands.com/privacy/privacy-main", "wildcard": "0" @@ -13513,8 +14377,8 @@ "category": "Functional", "name": "bbsitebuilder_active", "domain": "", - "description": "Used to designate whether the Site Builder is active.", - "retention": "Unknown", + "description": "OCD_bbsitebuilder_active_description", + "retention": "OCD_retention_Unknown", "dataController": "vBulletin", "gdprUrl": "https://www.internetbrands.com/privacy/privacy-main", "wildcard": "0" @@ -13526,8 +14390,8 @@ "category": "Functional", "name": "bblastactivity", "domain": "", - "description": "Stores the time of the last activity.", - "retention": "1 year", + "description": "OCD_bblastactivity_description", + "retention": "OCD_retention_1_year", "dataController": "vBulletin", "gdprUrl": "https://www.internetbrands.com/privacy/privacy-main", "wildcard": "0" @@ -13539,8 +14403,8 @@ "category": "Functional", "name": "bblastvisit", "domain": "", - "description": "Stores the time of the last page view.", - "retention": "1 year", + "description": "OCD_bblastvisit_description", + "retention": "OCD_retention_1_year", "dataController": "vBulletin", "gdprUrl": "https://www.internetbrands.com/privacy/privacy-main", "wildcard": "0" @@ -13552,8 +14416,8 @@ "category": "Marketing", "name": "viewer_token", "domain": "csync.loopme.me", - "description": "This cookie is associated with csync.loopme.me. It is used to track visitors on multiple websites in order to present relevant advertising based on the visitor's preferences.", - "retention": "31 days", + "description": "OCD_viewer_token_description", + "retention": "OCD_retention_31_days", "dataController": "csync.loopme.me", "gdprUrl": "", "wildcard": "0" @@ -13565,8 +14429,8 @@ "category": "Functional", "name": "wires", "domain": "", - "description": "ProcessWire session identifier.", - "retention": "session", + "description": "OCD_wires_description", + "retention": "OCD_retention_session", "dataController": "Processwire", "gdprUrl": "", "wildcard": "0" @@ -13578,8 +14442,8 @@ "category": "Functional", "name": "wires_challenge", "domain": "", - "description": "ProcessWire session cookie used to verify the validity of a session.", - "retention": "30 days", + "description": "OCD_wires_challenge_description", + "retention": "OCD_retention_30_days", "dataController": "Processwire", "gdprUrl": "", "wildcard": "0" @@ -13591,8 +14455,8 @@ "category": "Functional", "name": "ab.storage.userId.*", "domain": "", - "description": "Used to determine whether the currently logged-in user has changed and to associate events with the current user.", - "retention": "Unknown", + "description": "OCD_ab_storage_userId___description", + "retention": "OCD_retention_Unknown", "dataController": "Braze", "gdprUrl": "https://www.braze.com/company/legal/privacy", "wildcard": "1" @@ -13604,8 +14468,8 @@ "category": "Analytics", "name": "ab.storage.sessionId.*", "domain": "", - "description": "Randomly-generated string used to determine whether the user is starting a new or existing session to sync messages and calculate session analytics.", - "retention": "Session", + "description": "OCD_ab_storage_sessionId___description", + "retention": "OCD_retention_Session", "dataController": "Braze", "gdprUrl": "https://www.braze.com/company/legal/privacy", "wildcard": "1" @@ -13617,8 +14481,8 @@ "category": "Analytics", "name": "ab.storage.deviceId.*", "domain": "", - "description": "Randomly-generated string used to identify anonymous users, and to differentiate users’ devices and enables device-based messaging.", - "retention": "Unknown", + "description": "OCD_ab_storage_deviceId___description", + "retention": "OCD_retention_Unknown", "dataController": "Braze", "gdprUrl": "https://www.braze.com/company/legal/privacy", "wildcard": "1" @@ -13630,8 +14494,8 @@ "category": "Functional", "name": "ab.optOut", "domain": "", - "description": "Used to store a user’s opt-out preference.", - "retention": "Unknown", + "description": "OCD_ab_optOut_description", + "retention": "OCD_retention_Unknown", "dataController": "Braze", "gdprUrl": "https://www.braze.com/company/legal/privacy", "wildcard": "0" @@ -13643,8 +14507,8 @@ "category": "Functional", "name": "ab._gd", "domain": "", - "description": "Temporarily created (and then deleted) to determine the root-level cookie domain, which allows the SDK to work properly across sub-domains.", - "retention": "Unknown", + "description": "OCD_ab__gd_description", + "retention": "OCD_retention_Unknown", "dataController": "Braze", "gdprUrl": "https://www.braze.com/company/legal/privacy", "wildcard": "0" @@ -13656,8 +14520,8 @@ "category": "Functional", "name": "devicePixelRatio", "domain": "", - "description": "Used to make the site responsive to the visitor’s screen size.", - "retention": "1 year", + "description": "OCD_devicePixelRatio_description", + "retention": "OCD_retention_1_year", "dataController": "WordPress", "gdprUrl": "https://wordpress.org/about/privacy/", "wildcard": "0" @@ -13669,8 +14533,8 @@ "category": "Analytics", "name": "tk_qs", "domain": "", - "description": "JetPack sets this cookie to store a randomly-generated anonymous ID which is used only within the admin area and for general analytics tracking.", - "retention": "30 minutes", + "description": "OCD_tk_qs_description", + "retention": "OCD_retention_30_minutes", "dataController": "WordPress", "gdprUrl": "https://wordpress.org/about/privacy/", "wildcard": "0" @@ -13682,8 +14546,8 @@ "category": "Marketing", "name": "tk_lr", "domain": "", - "description": "Jetpack - Stores the unique identifier for the publisher to enable Jetpack to collect data.", - "retention": "1 year", + "description": "OCD_tk_lr_description", + "retention": "OCD_retention_1_year", "dataController": "WordPress", "gdprUrl": "https://wordpress.org/about/privacy/", "wildcard": "0" @@ -13695,8 +14559,8 @@ "category": "Marketing", "name": "tk_or", "domain": "", - "description": "Jetpack - Stores the unique identifier for the publisher to enable Jetpack to collect data.", - "retention": "5 Years", + "description": "OCD_tk_or_description", + "retention": "OCD_retention_5_Years", "dataController": "WordPress", "gdprUrl": "https://wordpress.org/about/privacy/", "wildcard": "0" @@ -13708,8 +14572,8 @@ "category": "Analytics", "name": "tk_r3d", "domain": "", - "description": "JetPack installs this cookie to collect internal metrics for user activity and in turn improve user experience.", - "retention": "3 days", + "description": "OCD_tk_r3d_description", + "retention": "OCD_retention_3_days", "dataController": "WordPress", "gdprUrl": "https://wordpress.org/about/privacy/", "wildcard": "0" @@ -13721,8 +14585,8 @@ "category": "Analytics", "name": "tk_tc", "domain": "", - "description": "JetPack sets this cookie to record details on how user's use the website.", - "retention": "session", + "description": "OCD_tk_tc_description", + "retention": "OCD_retention_session", "dataController": "WordPress", "gdprUrl": "https://wordpress.org/about/privacy/", "wildcard": "0" @@ -13734,8 +14598,8 @@ "category": "Functional", "name": "wp-settings-*", "domain": "", - "description": "Used to persist a user’s wp-admin configuration.", - "retention": "1 Year", + "description": "OCD_wp_settings___description", + "retention": "OCD_retention_1_Year", "dataController": "WordPress", "gdprUrl": "https://wordpress.org/about/privacy/", "wildcard": "1" @@ -13747,8 +14611,8 @@ "category": "Functional", "name": "wporg_logged_in", "domain": "", - "description": "Used to check whether the current visitor is a logged-in WordPress.org user.", - "retention": "14 days if you select “Remember Me” when logging in. Otherwise, Session.", + "description": "OCD_wporg_logged_in_description", + "retention": "OCD_retention_14_days_if_you_select__Remember_Me__when_logging_in__Otherwise__Session_", "dataController": "WordPress", "gdprUrl": "https://wordpress.org/about/privacy/", "wildcard": "0" @@ -13760,8 +14624,8 @@ "category": "Functional", "name": "wporg_sec", "domain": "", - "description": "Used to check whether the current visitor is a logged-in WordPress.org user.", - "retention": "14 days if you select “Remember Me” when logging in. Otherwise, Session.", + "description": "OCD_wporg_sec_description", + "retention": "OCD_retention_14_days_if_you_select__Remember_Me__when_logging_in__Otherwise__Session_", "dataController": "WordPress", "gdprUrl": "https://wordpress.org/about/privacy/", "wildcard": "0" @@ -13773,8 +14637,8 @@ "category": "Functional", "name": "wporg_locale", "domain": "", - "description": "Used to persist a user’s locale configuration.", - "retention": "1 year", + "description": "OCD_wporg_locale_description", + "retention": "OCD_retention_1_year", "dataController": "WordPress", "gdprUrl": "https://wordpress.org/about/privacy/", "wildcard": "0" @@ -13786,8 +14650,8 @@ "category": "Functional", "name": "welcome-*", "domain": "", - "description": "Used to record if you’ve chosen to hide the “Welcome” message at the top of the corresponding blog.", - "retention": "permanent", + "description": "OCD_welcome___description", + "retention": "OCD_retention_permanent", "dataController": "WordPress", "gdprUrl": "https://wordpress.org/about/privacy/", "wildcard": "1" @@ -13799,8 +14663,8 @@ "category": "Functional", "name": "showComments", "domain": "", - "description": "Used to determine if you prefer comments to be shown or hidden when reading the site.", - "retention": "10 years", + "description": "OCD_showComments_description", + "retention": "OCD_retention_10_years", "dataController": "WordPress", "gdprUrl": "https://wordpress.org/about/privacy/", "wildcard": "0" @@ -13812,8 +14676,8 @@ "category": "Functional", "name": "trac_form_token", "domain": "", - "description": "Used as a security token for cross-site request forgery protection.", - "retention": "session", + "description": "OCD_trac_form_token_description", + "retention": "OCD_retention_session", "dataController": "WordPress", "gdprUrl": "https://wordpress.org/about/privacy/", "wildcard": "0" @@ -13825,8 +14689,8 @@ "category": "Functional", "name": "trac_session", "domain": "", - "description": "Used to keep anonymous session information.", - "retention": "90 days", + "description": "OCD_trac_session_description", + "retention": "OCD_retention_90_days", "dataController": "WordPress", "gdprUrl": "https://wordpress.org/about/privacy/", "wildcard": "0" @@ -13838,8 +14702,8 @@ "category": "Functional", "name": "codexToken", "domain": "", - "description": "Used to check whether the current visitor is a logged-in WordPress.org user. Only set if you select “Keep me logged in” when logging in.", - "retention": "6 months", + "description": "OCD_codexToken_description", + "retention": "OCD_retention_6_months", "dataController": "WordPress", "gdprUrl": "https://wordpress.org/about/privacy/", "wildcard": "0" @@ -13851,8 +14715,8 @@ "category": "Functional", "name": "codexUserId", "domain": "", - "description": "Used to check whether the current visitor is a logged-in WordPress.org user.", - "retention": "6 months", + "description": "OCD_codexUserId_description", + "retention": "OCD_retention_6_months", "dataController": "WordPress", "gdprUrl": "https://wordpress.org/about/privacy/", "wildcard": "0" @@ -13864,8 +14728,8 @@ "category": "Functional", "name": "codexUserName", "domain": "", - "description": "Used to check whether the current visitor is a logged-in WordPress.org user.", - "retention": "6 months", + "description": "OCD_codexUserName_description", + "retention": "OCD_retention_6_months", "dataController": "WordPress", "gdprUrl": "https://wordpress.org/about/privacy/", "wildcard": "0" @@ -13877,8 +14741,8 @@ "category": "Analytics", "name": "camptix_client_stats", "domain": "", - "description": "Used to track unique visitors to tickets page on a WordCamp site", - "retention": "1 year", + "description": "OCD_camptix_client_stats_description", + "retention": "OCD_retention_1_year", "dataController": "WordPress", "gdprUrl": "https://wordpress.org/about/privacy/", "wildcard": "0" @@ -13890,8 +14754,8 @@ "category": "Functional", "name": "wp-saving-post", "domain": "", - "description": "Used to track if there is saved post exists for a post currently being edited. If exists then let user restore the data", - "retention": "1 day", + "description": "OCD_wp_saving_post_description", + "retention": "OCD_retention_1_day", "dataController": "WordPress", "gdprUrl": "https://wordpress.org/about/privacy/", "wildcard": "0" @@ -13903,8 +14767,8 @@ "category": "Functional", "name": "comment_author_*", "domain": "", - "description": "Used to tracked comment author name, if “Save my name, email, and website in this browser for the next time I comment.” is checked", - "retention": "347 days", + "description": "OCD_comment_author___description", + "retention": "OCD_retention_347_days", "dataController": "WordPress", "gdprUrl": "https://wordpress.org/about/privacy/", "wildcard": "1" @@ -13916,8 +14780,8 @@ "category": "Functional", "name": "comment_author_url_*", "domain": "", - "description": "Used to track comment author url, if “Save my name, email, and website in this browser for the next time I comment.” checkbox is checked", - "retention": "347 days", + "description": "OCD_comment_author_url___description", + "retention": "OCD_retention_347_days", "dataController": "WordPress", "gdprUrl": "https://wordpress.org/about/privacy/", "wildcard": "1" @@ -13929,8 +14793,8 @@ "category": "Functional", "name": "wp-postpass_*", "domain": "", - "description": "Used to maintain session if a post is password protected", - "retention": "10 days", + "description": "OCD_wp_postpass___description", + "retention": "OCD_retention_10_days", "dataController": "WordPress", "gdprUrl": "https://wordpress.org/about/privacy/", "wildcard": "1" @@ -13942,8 +14806,8 @@ "category": "Functional", "name": "wp-settings-time-*", "domain": "", - "description": "Time at which wp-settings-{user} was set", - "retention": "1 year", + "description": "OCD_wp_settings_time___description", + "retention": "OCD_retention_1_year", "dataController": "WordPress", "gdprUrl": "https://wordpress.org/about/privacy/", "wildcard": "1" @@ -13955,8 +14819,8 @@ "category": "Functional", "name": "tix_view_token", "domain": "", - "description": "Used for session managing private CampTix content", - "retention": "2 days", + "description": "OCD_tix_view_token_description", + "retention": "OCD_retention_2_days", "dataController": "WordPress", "gdprUrl": "https://wordpress.org/about/privacy/", "wildcard": "0" @@ -13968,8 +14832,8 @@ "category": "Functional", "name": "jetpackState", "domain": "", - "description": "Used for maintaining Jetpack State", - "retention": "session", + "description": "OCD_jetpackState_description", + "retention": "OCD_retention_session", "dataController": "WordPress", "gdprUrl": "https://wordpress.org/about/privacy/", "wildcard": "0" @@ -13981,8 +14845,8 @@ "category": "Functional", "name": "jpp_math_pass", "domain": "", - "description": "Verifies that a user answered the math problem correctly while logging in.", - "retention": "session", + "description": "OCD_jpp_math_pass_description", + "retention": "OCD_retention_session", "dataController": "WordPress", "gdprUrl": "https://wordpress.org/about/privacy/", "wildcard": "0" @@ -13994,8 +14858,8 @@ "category": "Functional", "name": "stnojs", "domain": "", - "description": "Remember if user do not want JavaScript executed", - "retention": "2 days", + "description": "OCD_stnojs_description", + "retention": "OCD_retention_2_days", "dataController": "WordPress", "gdprUrl": "https://wordpress.org/about/privacy/", "wildcard": "0" @@ -14007,8 +14871,8 @@ "category": "Functional", "name": "wordpress_logged_in_*", "domain": "", - "description": "Remember User session", - "retention": "session", + "description": "OCD_wordpress_logged_in___description", + "retention": "OCD_retention_session", "dataController": "WordPress", "gdprUrl": "https://wordpress.org/about/privacy/", "wildcard": "1" @@ -14020,8 +14884,8 @@ "category": "Functional", "name": "cookiefirst-consent", "domain": "", - "description": "This cookie saves your cookie preferences for this website. You can change these or withdraw your consent easily.", - "retention": "1 year", + "description": "OCD_cookiefirst_consent_description", + "retention": "OCD_retention_1_year", "dataController": "Cookie First", "gdprUrl": "https://cookiefirst.com/legal/privacy-policy/", "wildcard": "0" @@ -14033,8 +14897,8 @@ "category": "Functional", "name": "_iub_cs-*", "domain": "", - "description": "This cookie is used to store cookie acceptance and register consent.", - "retention": "1 year", + "description": "OCD__iub_cs___description", + "retention": "OCD_retention_1_year", "dataController": "Iubenda", "gdprUrl": "https://www.iubenda.com/privacy-policy/252372", "wildcard": "1" @@ -14046,8 +14910,8 @@ "category": "Functional", "name": "didomi_token", "domain": "", - "description": "This cookie contains consent information for personalized purposes and for personalized partners, as well as information specific to Didomi (e.g. user ID).", - "retention": "1 year", + "description": "OCD_didomi_token_description", + "retention": "OCD_retention_1_year", "dataController": "Didomi", "gdprUrl": "https://privacy.console.didomi.io/", "wildcard": "0" @@ -14059,8 +14923,8 @@ "category": "Functional", "name": "euconsent-v2", "domain": "", - "description": "This cookie contains the chain of consent for the IAB's Transparency and consent framework as well as the consent information for all IAB standards (partners and purposes).", - "retention": "1 year", + "description": "OCD_euconsent_v2_description", + "retention": "OCD_retention_1_year", "dataController": "Didomi", "gdprUrl": "https://privacy.console.didomi.io/", "wildcard": "0" @@ -14072,8 +14936,8 @@ "category": "Functional", "name": "_global_lucky_opt_out", "domain": "", - "description": "If set, will not run Lucky Orange. Set via our opt out links.", - "retention": "10 years", + "description": "OCD__global_lucky_opt_out_description", + "retention": "OCD_retention_10_years", "dataController": "Lucky Orange", "gdprUrl": "https://www.luckyorange.com/legal/privacy", "wildcard": "0" @@ -14085,8 +14949,8 @@ "category": "Functional", "name": "_lo_np_*", "domain": "", - "description": "Set if a user should no longer receive a particular poll.", - "retention": "30 days", + "description": "OCD__lo_np___description", + "retention": "OCD_retention_30_days", "dataController": "Lucky Orange", "gdprUrl": "https://www.luckyorange.com/legal/privacy", "wildcard": "1" @@ -14098,8 +14962,8 @@ "category": "Functional", "name": "_lo_bn", "domain": "", - "description": "Indicated this visitor has been banned from tracking.", - "retention": "30 days", + "description": "OCD__lo_bn_description", + "retention": "OCD_retention_30_days", "dataController": "Lucky Orange", "gdprUrl": "https://www.luckyorange.com/legal/privacy", "wildcard": "0" @@ -14111,8 +14975,8 @@ "category": "Functional", "name": "_lo_cid", "domain": "", - "description": "\tID of the visitor's current chat, if any.", - "retention": "Session", + "description": "OCD__lo_cid_description", + "retention": "OCD_retention_Session", "dataController": "Lucky Orange", "gdprUrl": "https://www.luckyorange.com/legal/privacy", "wildcard": "0" @@ -14124,8 +14988,8 @@ "category": "Analytics", "name": "_lo_uid", "domain": "", - "description": "Unique identifier for the visitor.", - "retention": "2 years", + "description": "OCD__lo_uid_description", + "retention": "OCD_retention_2_years", "dataController": "Lucky Orange", "gdprUrl": "https://www.luckyorange.com/legal/privacy", "wildcard": "0" @@ -14137,8 +15001,8 @@ "category": "Analytics", "name": "_lo_rid", "domain": "", - "description": "ID of the visitor's current recording.", - "retention": "30 minutes", + "description": "OCD__lo_rid_description", + "retention": "OCD_retention_30_minutes", "dataController": "Lucky Orange", "gdprUrl": "https://www.luckyorange.com/legal/privacy", "wildcard": "0" @@ -14150,8 +15014,8 @@ "category": "Analytics", "name": "_lo_v", "domain": "", - "description": "Total number of visitor's visits.", - "retention": "1 year", + "description": "OCD__lo_v_description", + "retention": "OCD_retention_1_year", "dataController": "Lucky Orange", "gdprUrl": "https://www.luckyorange.com/legal/privacy", "wildcard": "0" @@ -14163,8 +15027,8 @@ "category": "Analytics", "name": "__lotl", "domain": "", - "description": "URL of the visitor's original landing page, if any.", - "retention": "180 days", + "description": "OCD___lotl_description", + "retention": "OCD_retention_180_days", "dataController": "Lucky Orange", "gdprUrl": "https://www.luckyorange.com/legal/privacy", "wildcard": "0" @@ -14176,8 +15040,8 @@ "category": "Analytics", "name": "__lotr", "domain": "", - "description": "URL of the visitor's original referrer, if any.", - "retention": "180 days", + "description": "OCD___lotr_description", + "retention": "OCD_retention_180_days", "dataController": "Lucky Orange", "gdprUrl": "https://www.luckyorange.com/legal/privacy", "wildcard": "0" @@ -14189,8 +15053,8 @@ "category": "Functional", "name": "axeptio_authorized_vendors", "domain": "", - "description": "Lists all cookies validated by the user", - "retention": "1 year", + "description": "OCD_axeptio_authorized_vendors_description", + "retention": "OCD_retention_1_year", "dataController": "Axeptio", "gdprUrl": "https://www.axept.io/", "wildcard": "0" @@ -14202,8 +15066,8 @@ "category": "Functional", "name": "axeptio_cookies", "domain": "", - "description": "Cookie is set by a script that displays a banner allowing the user to accept Cookies on a case-by-case basis and is kept for 12 months, in order to determine for which Cookies the user has given his consent.", - "retention": "1 year", + "description": "OCD_axeptio_cookies_description", + "retention": "OCD_retention_1_year", "dataController": "Axeptio", "gdprUrl": "https://www.axept.io/", "wildcard": "0" @@ -14215,8 +15079,8 @@ "category": "Functional", "name": "axeptio_all_vendors", "domain": "", - "description": "Lists all available vendors subject to the user's consent", - "retention": "1 year", + "description": "OCD_axeptio_all_vendors_description", + "retention": "OCD_retention_1_year", "dataController": "Axeptio", "gdprUrl": "https://www.axept.io/", "wildcard": "0" @@ -14228,8 +15092,8 @@ "category": "Functional", "name": "borlabs-cookie", "domain": "", - "description": "Stores the user’s cookie consent state for embedded content on the current domain", - "retention": "1 year", + "description": "OCD_borlabs_cookie_description", + "retention": "OCD_retention_1_year", "dataController": "Borlabs", "gdprUrl": "https://borlabs.io/privacy/", "wildcard": "0" @@ -14241,8 +15105,8 @@ "category": "Functional", "name": "osano_consentmanager", "domain": "", - "description": "Stores the user's current consent status.", - "retention": "1 year", + "description": "OCD_osano_consentmanager_description", + "retention": "OCD_retention_1_year", "dataController": "Osano", "gdprUrl": "https://osano.trusthub.com/privacy", "wildcard": "0" @@ -14254,8 +15118,8 @@ "category": "Functional", "name": "osano_consentmanager_expdate", "domain": "", - "description": "Stores the expiration of the user's captured consent.", - "retention": "1 year", + "description": "OCD_osano_consentmanager_expdate_description", + "retention": "OCD_retention_1_year", "dataController": "Osano", "gdprUrl": "https://osano.trusthub.com/privacy", "wildcard": "0" @@ -14267,8 +15131,8 @@ "category": "Functional", "name": "osano_consentmanager_uuid", "domain": "", - "description": "Stores the user's unique consent identifier.", - "retention": "1 year", + "description": "OCD_osano_consentmanager_uuid_description", + "retention": "OCD_retention_1_year", "dataController": "Osano", "gdprUrl": "https://osano.trusthub.com/privacy", "wildcard": "0" @@ -14280,8 +15144,8 @@ "category": "Functional", "name": "cookieconsent_status", "domain": "", - "description": "This cookie is used to remember if you have consented to the use of cookies on this website.", - "retention": "1 year", + "description": "OCD_cookieconsent_status_description", + "retention": "OCD_retention_1_year", "dataController": "Osano", "gdprUrl": "https://osano.trusthub.com/privacy", "wildcard": "0" @@ -14293,8 +15157,8 @@ "category": "Functional", "name": "cookieconsent_page", "domain": "", - "description": "Page where the user complies to the cookie consent", - "retention": "session", + "description": "OCD_cookieconsent_page_description", + "retention": "OCD_retention_session", "dataController": "Osano", "gdprUrl": "https://osano.trusthub.com/privacy", "wildcard": "0" @@ -14306,8 +15170,8 @@ "category": "Functional", "name": "__cmpconsent*", "domain": "", - "description": "Consent String of the IAB CMP Framework (TCF) v2 specific to a single account in our platform.", - "retention": "1 year", + "description": "OCD___cmpconsent__description", + "retention": "OCD_retention_1_year", "dataController": "Consentmanager.net", "gdprUrl": "https://www.consentmanager.net/datenschutz/", "wildcard": "1" @@ -14319,8 +15183,8 @@ "category": "Functional", "name": "__cmpiab*", "domain": "", - "description": "(only if simplified format is enabled) List of IAB vendor IDs separated by underscore", - "retention": "1 year", + "description": "OCD___cmpiab__description", + "retention": "OCD_retention_1_year", "dataController": "Consentmanager.net", "gdprUrl": "https://www.consentmanager.net/datenschutz/", "wildcard": "1" @@ -14332,8 +15196,8 @@ "category": "Functional", "name": "__cmpcvc*", "domain": "", - "description": "List of custom vendor IDs separated by underscore specific to a single account in our platform", - "retention": "1 year", + "description": "OCD___cmpcvc__description", + "retention": "OCD_retention_1_year", "dataController": "Consentmanager.net", "gdprUrl": "https://www.consentmanager.net/datenschutz/", "wildcard": "1" @@ -14345,8 +15209,8 @@ "category": "Functional", "name": "__cmpcpc*", "domain": "", - "description": "List of custom purpose IDs separated by underscore", - "retention": "1 year", + "description": "OCD___cmpcpc__description", + "retention": "OCD_retention_1_year", "dataController": "Consentmanager.net", "gdprUrl": "https://www.consentmanager.net/datenschutz/", "wildcard": "1" @@ -14358,8 +15222,8 @@ "category": "Functional", "name": "__cmpccc*", "domain": "", - "description": "Consent information in custom consent format", - "retention": "1 year", + "description": "OCD___cmpccc__description", + "retention": "OCD_retention_1_year", "dataController": "Consentmanager.net", "gdprUrl": "https://www.consentmanager.net/datenschutz/", "wildcard": "1" @@ -14371,8 +15235,8 @@ "category": "Functional", "name": "__cmpwel*", "domain": "", - "description": "Information on PUR (pay or accept) mode", - "retention": "1 year", + "description": "OCD___cmpwel__description", + "retention": "OCD_retention_1_year", "dataController": "Consentmanager.net", "gdprUrl": "https://www.consentmanager.net/datenschutz/", "wildcard": "1" @@ -14384,8 +15248,8 @@ "category": "Functional", "name": "__cmpiuid", "domain": "", - "description": "If enabled, a unique random ID per visitor", - "retention": "1 year", + "description": "OCD___cmpiuid_description", + "retention": "OCD_retention_1_year", "dataController": "Consentmanager.net", "gdprUrl": "https://www.consentmanager.net/datenschutz/", "wildcard": "0" @@ -14397,8 +15261,8 @@ "category": "Functional", "name": "__cmpccx", "domain": "", - "description": "Integer. Test if visitor left the website after seeing the consent layer.", - "retention": "1 year", + "description": "OCD___cmpccx_description", + "retention": "OCD_retention_1_year", "dataController": "Consentmanager.net", "gdprUrl": "https://www.consentmanager.net/datenschutz/", "wildcard": "0" @@ -14410,8 +15274,8 @@ "category": "Functional", "name": "__cmpcc", "domain": "", - "description": "Integer. Test if visitors browser supports cookies.", - "retention": "1 year", + "description": "OCD___cmpcc_description", + "retention": "OCD_retention_1_year", "dataController": "Consentmanager.net", "gdprUrl": "https://www.consentmanager.net/datenschutz/", "wildcard": "0" @@ -14423,8 +15287,8 @@ "category": "Functional", "name": "__cmpfcc", "domain": "", - "description": "Integer. Test if visitors browser supports cookies.", - "retention": "1 year", + "description": "OCD___cmpfcc_description", + "retention": "OCD_retention_1_year", "dataController": "Consentmanager.net", "gdprUrl": "https://www.consentmanager.net/datenschutz/", "wildcard": "0" @@ -14436,8 +15300,8 @@ "category": "Functional", "name": "__cmpld", "domain": "", - "description": "\tTimestamp. Contains the time when the visitor last saw the consent layer.", - "retention": "1 year", + "description": "OCD___cmpld_description", + "retention": "OCD_retention_1_year", "dataController": "Consentmanager.net", "gdprUrl": "https://www.consentmanager.net/datenschutz/", "wildcard": "0" @@ -14449,8 +15313,8 @@ "category": "Functional", "name": "__cmpccpausps*", "domain": "", - "description": "Consent information in IAB USP CCPA Format.", - "retention": "1 year", + "description": "OCD___cmpccpausps__description", + "retention": "OCD_retention_1_year", "dataController": "Consentmanager.net", "gdprUrl": "https://www.consentmanager.net/datenschutz/", "wildcard": "1" @@ -14462,8 +15326,8 @@ "category": "Functional", "name": "hu-consent", "domain": "", - "description": "Stores the permission to use cookies for the current domain by the user", - "retention": "1 month", + "description": "OCD_hu_consent_description", + "retention": "OCD_retention_1_month", "dataController": "Hu-manity.co", "gdprUrl": "https://hu-manity.co/privacy-policy/", "wildcard": "0" @@ -14475,8 +15339,8 @@ "category": "Functional", "name": "complianz_policy_id", "domain": "", - "description": "Stores the user’s cookie consent state for the current domain", - "retention": "1 year", + "description": "OCD_complianz_policy_id_description", + "retention": "OCD_retention_1_year", "dataController": "Complianz", "gdprUrl": "https://complianz.io/legal/privacy-statement/", "wildcard": "0" @@ -14488,8 +15352,8 @@ "category": "Functional", "name": "complianz_consent_status", "domain": "", - "description": "Stores the status of the cookie agreement of the current user", - "retention": "1 year", + "description": "OCD_complianz_consent_status_description", + "retention": "OCD_retention_1_year", "dataController": "Complianz", "gdprUrl": "https://complianz.io/legal/privacy-statement/", "wildcard": "0" @@ -14501,8 +15365,8 @@ "category": "Functional", "name": "cmplz_marketing", "domain": "", - "description": "Stores the setting of the marketing/statistic level of the cookie agreement.", - "retention": "1 year", + "description": "OCD_cmplz_marketing_description", + "retention": "OCD_retention_1_year", "dataController": "Complianz", "gdprUrl": "https://complianz.io/legal/privacy-statement/", "wildcard": "0" @@ -14514,8 +15378,8 @@ "category": "Functional", "name": "cmplz_statistics", "domain": "", - "description": "Stores the setting of the statistic level of the cookie agreement.", - "retention": "1 year", + "description": "OCD_cmplz_statistics_description", + "retention": "OCD_retention_1_year", "dataController": "Complianz", "gdprUrl": "https://complianz.io/legal/privacy-statement/", "wildcard": "0" @@ -14527,8 +15391,8 @@ "category": "Functional", "name": "cmplz_preferences", "domain": "", - "description": "Stores the setting of the preferences level of the cookie agreement.", - "retention": "1 year", + "description": "OCD_cmplz_preferences_description", + "retention": "OCD_retention_1_year", "dataController": "Complianz", "gdprUrl": "https://complianz.io/legal/privacy-statement/", "wildcard": "0" @@ -14540,8 +15404,8 @@ "category": "Functional", "name": "cmplz_functional", "domain": "", - "description": "Stores the setting of the functional level of the cookie agreement.", - "retention": "1 year", + "description": "OCD_cmplz_functional_description", + "retention": "OCD_retention_1_year", "dataController": "Complianz", "gdprUrl": "https://complianz.io/legal/privacy-statement/", "wildcard": "0" @@ -14553,8 +15417,8 @@ "category": "Functional", "name": "cmplz_stats", "domain": "", - "description": "Stores the setting of the stats level of the cookie agreement.", - "retention": "1 year", + "description": "OCD_cmplz_stats_description", + "retention": "OCD_retention_1_year", "dataController": "Complianz", "gdprUrl": "https://complianz.io/legal/privacy-statement/", "wildcard": "0" @@ -14566,8 +15430,8 @@ "category": "Functional", "name": "cmplz_choice", "domain": "", - "description": "Store if a message has been dismissed", - "retention": "1 year", + "description": "OCD_cmplz_choice_description", + "retention": "OCD_retention_1_year", "dataController": "Complianz", "gdprUrl": "https://complianz.io/legal/privacy-statement/", "wildcard": "0" @@ -14579,8 +15443,8 @@ "category": "Functional", "name": "cmplz_id", "domain": "", - "description": "Store cookie consent preferences", - "retention": "1 year", + "description": "OCD_cmplz_id_description", + "retention": "OCD_retention_1_year", "dataController": "Complianz", "gdprUrl": "https://complianz.io/legal/privacy-statement/", "wildcard": "0" @@ -14592,8 +15456,8 @@ "category": "Functional", "name": "cmplz_user_data", "domain": "", - "description": "Read to determine which cookie banner to show", - "retention": "1 year", + "description": "OCD_cmplz_user_data_description", + "retention": "OCD_retention_1_year", "dataController": "Complianz", "gdprUrl": "https://complianz.io/legal/privacy-statement/", "wildcard": "0" @@ -14605,8 +15469,8 @@ "category": "Functional", "name": "cmplz_saved_services", "domain": "", - "description": "Store cookie consent preferences", - "retention": "1 year", + "description": "OCD_cmplz_saved_services_description", + "retention": "OCD_retention_1_year", "dataController": "Complianz", "gdprUrl": "https://complianz.io/legal/privacy-statement/", "wildcard": "0" @@ -14618,8 +15482,8 @@ "category": "Functional", "name": "cmplz_consented_services", "domain": "", - "description": "Store cookie consent preferences", - "retention": "1 year", + "description": "OCD_cmplz_consented_services_description", + "retention": "OCD_retention_1_year", "dataController": "Complianz", "gdprUrl": "https://complianz.io/legal/privacy-statement/", "wildcard": "0" @@ -14631,8 +15495,8 @@ "category": "Functional", "name": "cmplz_policy_id", "domain": "", - "description": "Store accepted cookie policy ID", - "retention": "1 year", + "description": "OCD_cmplz_policy_id_description", + "retention": "OCD_retention_1_year", "dataController": "Complianz", "gdprUrl": "https://complianz.io/legal/privacy-statement/", "wildcard": "0" @@ -14644,8 +15508,8 @@ "category": "Functional", "name": "cmplz_saved_categories", "domain": "", - "description": "Store cookie consent preferences", - "retention": "1 year", + "description": "OCD_cmplz_saved_categories_description", + "retention": "OCD_retention_1_year", "dataController": "Complianz", "gdprUrl": "https://complianz.io/legal/privacy-statement/", "wildcard": "0" @@ -14657,8 +15521,8 @@ "category": "Functional", "name": "cmplz_banner-status", "domain": "", - "description": "This cookie stores if the cookie banner has been dismissed", - "retention": "1 year", + "description": "OCD_cmplz_banner_status_description", + "retention": "OCD_retention_1_year", "dataController": "Complianz", "gdprUrl": "https://complianz.io/legal/privacy-statement/", "wildcard": "0" @@ -14670,8 +15534,8 @@ "category": "Functional", "name": "cookie_notice_accepted", "domain": "", - "description": "Identifies whether the user has accepted the use of cookies on this web site", - "retention": "3 months", + "description": "OCD_cookie_notice_accepted_description", + "retention": "OCD_retention_3_months", "dataController": "Digital Factory", "gdprUrl": "https://dfactory.co/privacy-policy/", "wildcard": "0" @@ -14683,8 +15547,8 @@ "category": "Functional", "name": "moove_gdpr_popup", "domain": "", - "description": "When this Cookie is enabled, these Cookies are used to save your Cookie Setting Preferences.", - "retention": "1 year", + "description": "OCD_moove_gdpr_popup_description", + "retention": "OCD_retention_1_year", "dataController": "Moove", "gdprUrl": "https://www.mooveagency.com/privacy-policy/", "wildcard": "0" @@ -14696,8 +15560,8 @@ "category": "Functional", "name": "__tlbcpv", "domain": "", - "description": "Used to record the cookie consent preferences of visitors", - "retention": "1 year", + "description": "OCD___tlbcpv_description", + "retention": "OCD_retention_1_year", "dataController": "Termly", "gdprUrl": "https://termly.io/our-privacy-policy/", "wildcard": "0" @@ -14709,8 +15573,8 @@ "category": "Functional", "name": "__tltpl_*", "domain": "", - "description": "Used to record the policies that visitors consent to", - "retention": "1 year", + "description": "OCD___tltpl___description", + "retention": "OCD_retention_1_year", "dataController": "Termly", "gdprUrl": "https://termly.io/our-privacy-policy/", "wildcard": "1" @@ -14722,8 +15586,8 @@ "category": "Functional", "name": "__tluid", "domain": "", - "description": "Assigns a random ID number to each visitor so that their policy consent and cookie consent preferences can be saved.", - "retention": "1 year", + "description": "OCD___tluid_description", + "retention": "OCD_retention_1_year", "dataController": "Termly", "gdprUrl": "https://termly.io/our-privacy-policy/", "wildcard": "0" @@ -14735,8 +15599,8 @@ "category": "Analytics", "name": "__stid", "domain": "sharethis.com", - "description": "The __stid cookie is set as part of the ShareThis service and monitors user-activity, e.g. Web pages viewed, navigation from page to page, time spent on each page etc.", - "retention": "1 year", + "description": "OCD___stid_description", + "retention": "OCD_retention_1_year", "dataController": "ShareThis", "gdprUrl": "https://sharethis.com/privacy/", "wildcard": "0" @@ -14748,8 +15612,8 @@ "category": "Marketing", "name": "__stidv", "domain": "sharethis.com", - "description": "ShareThis cookie ID version.", - "retention": "10 years", + "description": "OCD___stidv_description", + "retention": "OCD_retention_10_years", "dataController": "ShareThis", "gdprUrl": "https://sharethis.com/privacy/", "wildcard": "0" @@ -14761,8 +15625,8 @@ "category": "Functional", "name": "pubconsent*", "domain": "sharethis.com", - "description": "ShareThis cookie set to indicate user has made a declaration about GDPR data collection for IAB TCF v1 format.", - "retention": "13 months", + "description": "OCD_pubconsent__description", + "retention": "OCD_retention_13_months", "dataController": "ShareThis", "gdprUrl": "https://sharethis.com/privacy/", "wildcard": "1" @@ -14774,8 +15638,8 @@ "category": "Functional", "name": "st_optout", "domain": "sharethis.com", - "description": "ShareThis cookie set to indicate that user has opted out from data collection.", - "retention": "10 years", + "description": "OCD_st_optout_description", + "retention": "OCD_retention_10_years", "dataController": "ShareThis", "gdprUrl": "https://sharethis.com/privacy/", "wildcard": "0" @@ -14787,8 +15651,8 @@ "category": "Analytics", "name": "pxcelBcnLcy", "domain": "sharethis.com", - "description": "ShareThis Tag Management System cookie to track latency on reporting beacon.", - "retention": "session", + "description": "OCD_pxcelBcnLcy_description", + "retention": "OCD_retention_session", "dataController": "ShareThis", "gdprUrl": "https://sharethis.com/privacy/", "wildcard": "0" @@ -14800,8 +15664,8 @@ "category": "Functional", "name": "pxcelAcc3PC", "domain": "", - "description": "ShareThis Tag Management System cookie to check whether third party cookies are accepted by the browser. This is only set if there is no incoming cookie in the request.", - "retention": "1 day", + "description": "OCD_pxcelAcc3PC_description", + "retention": "OCD_retention_1_day", "dataController": "ShareThis", "gdprUrl": "https://sharethis.com/privacy/", "wildcard": "0" @@ -14813,8 +15677,8 @@ "category": "Analytics", "name": "pxcelPage*", "domain": "sharethis.com", - "description": "ShareThis Tag Management System cookie to track status of pixel rotation loading. ShareThis uses a different cookie for different groups of sites within the ShareThis network.", - "retention": "1 year", + "description": "OCD_pxcelPage__description", + "retention": "OCD_retention_1_year", "dataController": "ShareThis", "gdprUrl": "https://sharethis.com/privacy/", "wildcard": "1" @@ -14826,8 +15690,8 @@ "category": "Functional", "name": "usprivacy*", "domain": "", - "description": "ShareThis reads if the usprivacy cookie is present in the publisher domain.", - "retention": "session", + "description": "OCD_usprivacy__description", + "retention": "OCD_retention_session", "dataController": "ShareThis", "gdprUrl": "https://sharethis.com/privacy/", "wildcard": "1" @@ -14839,8 +15703,8 @@ "category": "Functional", "name": "euconsent*", "domain": "", - "description": "ShareThis reads if the euconsent cookie is present in the publisher domain.", - "retention": "session", + "description": "OCD_euconsent__description", + "retention": "OCD_retention_session", "dataController": "ShareThis", "gdprUrl": "https://sharethis.com/privacy/", "wildcard": "1" @@ -14852,8 +15716,8 @@ "category": "Functional", "name": "fpestid", "domain": "", - "description": "Fpestid is a ShareThis cookie ID set in the domain of the website operator.", - "retention": "13 months", + "description": "OCD_fpestid_description", + "retention": "OCD_retention_13_months", "dataController": "ShareThis", "gdprUrl": "https://sharethis.com/privacy/", "wildcard": "0" @@ -14865,8 +15729,8 @@ "category": "Marketing", "name": "khaos", "domain": "rubiconproject.com", - "description": "Rubicon Project cookie used for tracking advertising campaigns and collect anonymized user behavior statistics", - "retention": "1 year", + "description": "OCD_khaos_description", + "retention": "OCD_retention_1_year", "dataController": "Magnite", "gdprUrl": "https://www.magnite.com/legal/platform-cookie-statement/", "wildcard": "0" @@ -14878,8 +15742,8 @@ "category": "Marketing", "name": "audit", "domain": "rubiconproject.com", - "description": "Set by Rubicon Project to record cookie consent data.", - "retention": "1 year", + "description": "OCD_audit_description", + "retention": "OCD_retention_1_year", "dataController": "Magnite", "gdprUrl": "https://www.magnite.com/legal/platform-cookie-statement/", "wildcard": "0" @@ -14891,8 +15755,8 @@ "category": "Marketing", "name": "put_*", "domain": "rubiconproject.com", - "description": "Records anonymous user data, such as IP, geographical location, websites visited and ads clicked on, in order to optimise visualisation of ads according to user movement around websites using the same advertising network.", - "retention": "1 month", + "description": "OCD_put___description", + "retention": "OCD_retention_1_month", "dataController": "Magnite", "gdprUrl": "https://www.magnite.com/legal/platform-cookie-statement/", "wildcard": "1" @@ -14904,8 +15768,8 @@ "category": "Marketing", "name": "rpb", "domain": "rubiconproject.com", - "description": "Records anonymous user data, such as IP, geographical location, websites visited and ads clicked on, in order to optimise visualisation of ads according to user movement around websites using the same advertising network.", - "retention": "1 month", + "description": "OCD_rpb_description", + "retention": "OCD_retention_1_month", "dataController": "Magnite", "gdprUrl": "https://www.magnite.com/legal/platform-cookie-statement/", "wildcard": "0" @@ -14917,8 +15781,8 @@ "category": "Marketing", "name": "rpx", "domain": "rubiconproject.com", - "description": "Records anonymous user data, such as IP, geographical location, websites visited and ads clicked on, in order to optimise visualisation of ads according to user movement around websites using the same advertising network.", - "retention": "1 month", + "description": "OCD_rpx_description", + "retention": "OCD_retention_1_month", "dataController": "Magnite", "gdprUrl": "https://www.magnite.com/legal/platform-cookie-statement/", "wildcard": "0" @@ -14930,11 +15794,22 @@ "category": "Marketing", "name": "c", "domain": "rubiconproject.com", - "description": "Records anonymous user data, such as IP, geographical location, websites visited and ads clicked on, in order to optimise visualisation of ads according to user movement around websites using the same advertising network.", - "retention": "1 month", + "description": "OCD_c_description", + "retention": "OCD_retention_1_month", "dataController": "Magnite", "gdprUrl": "https://www.magnite.com/legal/platform-cookie-statement/", "wildcard": "0" + }, + { + "platform": "CreativeCDN", + "category": "Marketing", + "name": "c", + "domain": "creativecdn.com", + "description": "Regulates the synchronization of user identification and the exchange of your data between various advertising services.", + "retention": "364 days", + "dataController": "RTB House", + "gdprUrl": "https://rtbhouse.com/privacy-center", + "wildcard": "0" } ], "apiDomain_*": [ @@ -14943,8 +15818,8 @@ "category": "Marketing", "name": "apiDomain_*", "domain": "gigya.com", - "description": "The shared domain API calls for all sites in a group should be sent to.", - "retention": "12 months", + "description": "OCD_apiDomain___description", + "retention": "OCD_retention_12_months", "dataController": "SAP", "gdprUrl": "https://www.sap.com/privacy", "wildcard": "1" @@ -14956,8 +15831,8 @@ "category": "Functional", "name": "gac_*", "domain": "", - "description": "Used to trigger server initiated login.", - "retention": "session", + "description": "OCD_gac___description", + "retention": "OCD_retention_session", "dataController": "SAP", "gdprUrl": "https://www.sap.com/privacy", "wildcard": "1" @@ -14969,8 +15844,8 @@ "category": "Functional", "name": "_gig_APIProxy_enabled", "domain": "", - "description": "Used to indicate whether to use APIProxy or not.", - "retention": "session", + "description": "OCD__gig_APIProxy_enabled_description", + "retention": "OCD_retention_session", "dataController": "SAP", "gdprUrl": "https://www.sap.com/privacy", "wildcard": "0" @@ -14982,8 +15857,8 @@ "category": "Marketing", "name": "gig_bootstrap_*", "domain": "gigya.com", - "description": "If declined, user may be intermittently logged out.", - "retention": "12 months", + "description": "OCD_gig_bootstrap___description", + "retention": "OCD_retention_12_months", "dataController": "SAP", "gdprUrl": "https://www.sap.com/privacy", "wildcard": "1" @@ -14995,8 +15870,8 @@ "category": "Functional", "name": "gig_canary", "domain": "", - "description": "Indicates whether the client is using the canary version of the WebSDK.", - "retention": "12 months", + "description": "OCD_gig_canary_description", + "retention": "OCD_retention_12_months", "dataController": "SAP", "gdprUrl": "https://www.sap.com/privacy", "wildcard": "0" @@ -15008,8 +15883,8 @@ "category": "Functional", "name": "gig_canary_3*", "domain": "", - "description": "Indicates whether the client is using the canary version of the WebSDK.", - "retention": "12 months", + "description": "OCD_gig_canary_3__description", + "retention": "OCD_retention_12_months", "dataController": "SAP", "gdprUrl": "https://www.sap.com/privacy", "wildcard": "1" @@ -15021,8 +15896,8 @@ "category": "Marketing", "name": "_gig_email", "domain": "", - "description": "Last used email address in share (when sending email).", - "retention": "12 months", + "description": "OCD__gig_email_description", + "retention": "OCD_retention_12_months", "dataController": "SAP", "gdprUrl": "https://www.sap.com/privacy", "wildcard": "0" @@ -15034,8 +15909,8 @@ "category": "Functional", "name": "gig_canary_ver*", "domain": "", - "description": "The version name of the WebSDK's canary version.", - "retention": "12 months", + "description": "OCD_gig_canary_ver__description", + "retention": "OCD_retention_12_months", "dataController": "SAP", "gdprUrl": "https://www.sap.com/privacy", "wildcard": "1" @@ -15047,8 +15922,8 @@ "category": "Marketing", "name": "gig_hasGmid", "domain": "gigya.com", - "description": "Internal cookie for the Web SDK", - "retention": "12 months", + "description": "OCD_gig_hasGmid_description", + "retention": "OCD_retention_12_months", "dataController": "SAP", "gdprUrl": "https://www.sap.com/privacy", "wildcard": "0" @@ -15060,8 +15935,8 @@ "category": "Marketing", "name": "_gig_llu", "domain": "gigya.com", - "description": "Last login provider username for Login Welcome back screen.", - "retention": "12 months", + "description": "OCD__gig_llu_description", + "retention": "OCD_retention_12_months", "dataController": "SAP", "gdprUrl": "https://www.sap.com/privacy", "wildcard": "0" @@ -15073,8 +15948,8 @@ "category": "Marketing", "name": "_gig_llp", "domain": "gigya.com", - "description": "Last login provider username for Login Welcome back screen.", - "retention": "12 months", + "description": "OCD__gig_llp_description", + "retention": "OCD_retention_12_months", "dataController": "SAP", "gdprUrl": "https://www.sap.com/privacy", "wildcard": "0" @@ -15086,8 +15961,8 @@ "category": "Functional", "name": "glt_*", "domain": "", - "description": "Login Token for authentication.", - "retention": "12 months", + "description": "OCD_glt___description", + "retention": "OCD_retention_12_months", "dataController": "SAP", "gdprUrl": "https://www.sap.com/privacy", "wildcard": "1" @@ -15099,8 +15974,8 @@ "category": "Functional", "name": "_gig_lt", "domain": "", - "description": "Login Token for authentication.", - "retention": "12 months", + "description": "OCD__gig_lt_description", + "retention": "OCD_retention_12_months", "dataController": "SAP", "gdprUrl": "https://www.sap.com/privacy", "wildcard": "0" @@ -15112,8 +15987,8 @@ "category": "Functional", "name": "gig_last_ver_*", "domain": "", - "description": "Last time of verification of the session when the site is using the verifyLoginInterval property of global CONF in order to trigger reverification.", - "retention": "12 months", + "description": "OCD_gig_last_ver___description", + "retention": "OCD_retention_12_months", "dataController": "SAP", "gdprUrl": "https://www.sap.com/privacy", "wildcard": "1" @@ -15125,8 +16000,8 @@ "category": "Functional", "name": "gig_loginToken_*", "domain": "gigya.com", - "description": "SAP Customer Data Cloud's Single Sign On (SSO) group login token.", - "retention": "12 months", + "description": "OCD_gig_loginToken___description", + "retention": "OCD_retention_12_months", "dataController": "SAP", "gdprUrl": "https://www.sap.com/privacy", "wildcard": "1" @@ -15138,8 +16013,8 @@ "category": "Functional", "name": "_gig_shareUI_cb_*", "domain": "", - "description": "Login Token for authentication.", - "retention": "12 months", + "description": "OCD__gig_shareUI_cb___description", + "retention": "OCD_retention_12_months", "dataController": "SAP", "gdprUrl": "https://www.sap.com/privacy", "wildcard": "1" @@ -15151,8 +16026,8 @@ "category": "Marketing", "name": "_gig_shareUI_lastUID", "domain": "", - "description": "Last logged in UID.", - "retention": "12 months", + "description": "OCD__gig_shareUI_lastUID_description", + "retention": "OCD_retention_12_months", "dataController": "SAP", "gdprUrl": "https://www.sap.com/privacy", "wildcard": "0" @@ -15164,8 +16039,8 @@ "category": "Marketing", "name": "_gigRefUid_*", "domain": "", - "description": "Last referrer User ID.", - "retention": "12 months", + "description": "OCD__gigRefUid___description", + "retention": "OCD_retention_12_months", "dataController": "SAP", "gdprUrl": "https://www.sap.com/privacy", "wildcard": "1" @@ -15177,8 +16052,8 @@ "category": "Functional", "name": "gig_toggles", "domain": "", - "description": "This value is sent to SAP Customer Data Cloud in order to identify toggles that the back-end behavior depends on to process the specified toggle.", - "retention": "session", + "description": "OCD_gig_toggles_description", + "retention": "OCD_retention_session", "dataController": "SAP", "gdprUrl": "https://www.sap.com/privacy", "wildcard": "0" @@ -15190,8 +16065,8 @@ "category": "Functional", "name": "gig3pc", "domain": "", - "description": "Remembers if third-party cookies are blocked to avoid checking every time.", - "retention": "2 days", + "description": "OCD_gig3pc_description", + "retention": "OCD_retention_2_days", "dataController": "SAP", "gdprUrl": "https://www.sap.com/privacy", "wildcard": "0" @@ -15203,8 +16078,8 @@ "category": "Functional", "name": "gig3pctest", "domain": "", - "description": "A temp cookie used to check if third-party cookies are blocked.", - "retention": "session", + "description": "OCD_gig3pctest_description", + "retention": "OCD_retention_session", "dataController": "SAP", "gdprUrl": "https://www.sap.com/privacy", "wildcard": "0" @@ -15216,8 +16091,8 @@ "category": "Functional", "name": "glnk", "domain": "", - "description": "Ticket for second phase of login.", - "retention": "session", + "description": "OCD_glnk_description", + "retention": "OCD_retention_session", "dataController": "SAP", "gdprUrl": "https://www.sap.com/privacy", "wildcard": "0" @@ -15229,8 +16104,8 @@ "category": "Marketing", "name": "gmid", "domain": "", - "description": "User cookie.", - "retention": "12 months", + "description": "OCD_gmid_description", + "retention": "OCD_retention_12_months", "dataController": "SAP", "gdprUrl": "https://www.sap.com/privacy", "wildcard": "0" @@ -15242,8 +16117,8 @@ "category": "Functional", "name": "gst", "domain": "", - "description": "Server ticket for second phase of login.", - "retention": "30 Minutes", + "description": "OCD_gst_description", + "retention": "OCD_retention_30_Minutes", "dataController": "SAP", "gdprUrl": "https://www.sap.com/privacy", "wildcard": "0" @@ -15255,8 +16130,8 @@ "category": "Functional", "name": "GSLM_*", "domain": "", - "description": "Session magic cookie.", - "retention": "session", + "description": "OCD_GSLM___description", + "retention": "OCD_retention_session", "dataController": "SAP", "gdprUrl": "https://www.sap.com/privacy", "wildcard": "1" @@ -15268,8 +16143,8 @@ "category": "Marketing", "name": "hasGmid", "domain": "gigya.com", - "description": "Internal cookie for the Web SDK", - "retention": "13 months", + "description": "OCD_hasGmid_description", + "retention": "OCD_retention_13_months", "dataController": "SAP", "gdprUrl": "https://www.sap.com/privacy", "wildcard": "0" @@ -15281,8 +16156,8 @@ "category": "Functional", "name": "SAML_*", "domain": "gigya.com", - "description": "This cookie is saved by SAML SP to manage the SAML session information and, specifically, the parameters needed for logout.", - "retention": "12 months", + "description": "OCD_SAML___description", + "retention": "OCD_retention_12_months", "dataController": "SAP", "gdprUrl": "https://www.sap.com/privacy", "wildcard": "1" @@ -15294,8 +16169,8 @@ "category": "Functional", "name": "gltexp_*", "domain": "", - "description": "Login Token Expiration.", - "retention": "session", + "description": "OCD_gltexp___description", + "retention": "OCD_retention_session", "dataController": "SAP", "gdprUrl": "https://www.sap.com/privacy", "wildcard": "1" @@ -15307,8 +16182,8 @@ "category": "Marketing", "name": "_gig_*", "domain": "", - "description": "Callback for listener.", - "retention": "12 months", + "description": "OCD__gig___description", + "retention": "OCD_retention_12_months", "dataController": "SAP", "gdprUrl": "https://www.sap.com/privacy", "wildcard": "1" @@ -15320,8 +16195,8 @@ "category": "Functional", "name": "ua_*", "domain": "", - "description": "COPPA (under age).", - "retention": "1 day", + "description": "OCD_ua___description", + "retention": "OCD_retention_1_day", "dataController": "SAP", "gdprUrl": "https://www.sap.com/privacy", "wildcard": "1" @@ -15333,8 +16208,8 @@ "category": "Marketing", "name": "ucid", "domain": "", - "description": "Unique computer identifier used for generating reports, and used by the Web SDK to get saved response.", - "retention": "13 months", + "description": "OCD_ucid_description", + "retention": "OCD_retention_13_months", "dataController": "SAP", "gdprUrl": "https://www.sap.com/privacy", "wildcard": "0" @@ -15346,8 +16221,8 @@ "category": "Marketing", "name": "ozone_uid", "domain": "the-ozone-project.com", - "description": "This cookie contains unique randomly-generated values that enable the Ozone Project to distinguish browsers and mobile devices.", - "retention": "90 days", + "description": "OCD_ozone_uid_description", + "retention": "OCD_retention_90_days", "dataController": "The Ozone Project", "gdprUrl": "https://www.ozoneproject.com/privacy-matters", "wildcard": "0" @@ -15359,8 +16234,8 @@ "category": "Analytics", "name": "mc_cid", "domain": "", - "description": "Mailchimp campaign ID", - "retention": "14 days", + "description": "OCD_mc_cid_description", + "retention": "OCD_retention_14_days", "dataController": "Mailchimp", "gdprUrl": "https://mailchimp.com/legal/", "wildcard": "0" @@ -15372,8 +16247,8 @@ "category": "Analytics", "name": "mc_eid", "domain": "", - "description": "Mailchimp email ID", - "retention": "14 days", + "description": "OCD_mc_eid_description", + "retention": "OCD_retention_14_days", "dataController": "Mailchimp", "gdprUrl": "https://mailchimp.com/legal/", "wildcard": "0" @@ -15385,8 +16260,8 @@ "category": "Analytics", "name": "mc_landing_site", "domain": "", - "description": "Page visitor entered your site on", - "retention": "14 days", + "description": "OCD_mc_landing_site_description", + "retention": "OCD_retention_14_days", "dataController": "Mailchimp", "gdprUrl": "https://mailchimp.com/legal/", "wildcard": "0" @@ -15398,8 +16273,8 @@ "category": "Marketing", "name": "_BEAMER_FIRST_VISIT_*", "domain": "hotjar.com", - "description": "Set by Beamer (hotjar.com) to store the date of the user’s first interaction with insights.", - "retention": "3000 days", + "description": "OCD__BEAMER_FIRST_VISIT___description", + "retention": "OCD_retention_3000_days", "dataController": "Hotjar", "gdprUrl": "https://www.getbeamer.com/privacy-policy", "wildcard": "1" @@ -15411,8 +16286,8 @@ "category": "Marketing", "name": "_BEAMER_USER_ID_*", "domain": "hotjar.com", - "description": "Set by Beamer (hotjar.com) to store an internal ID for a user.", - "retention": "300 days", + "description": "OCD__BEAMER_USER_ID___description", + "retention": "OCD_retention_300_days", "dataController": "Hotjar", "gdprUrl": "https://www.getbeamer.com/privacy-policy", "wildcard": "1" @@ -15424,8 +16299,8 @@ "category": "Marketing", "name": "_BEAMER_DATE_*", "domain": "hotjar.com", - "description": "Set by Beamer (hotjar.com). Stores the latest date in which the feed or page was opened.", - "retention": "300 days", + "description": "OCD__BEAMER_DATE___description", + "retention": "OCD_retention_300_days", "dataController": "Hotjar", "gdprUrl": "https://www.getbeamer.com/privacy-policy", "wildcard": "1" @@ -15437,8 +16312,8 @@ "category": "Marketing", "name": "_BEAMER_LAST_POST_SHOWN_*", "domain": "hotjar.com", - "description": "Set by Beamer (hotjar.com). Stores the timestamp for the last time the number of unread posts was updated for the user.", - "retention": "300 days", + "description": "OCD__BEAMER_LAST_POST_SHOWN___description", + "retention": "OCD_retention_300_days", "dataController": "Hotjar", "gdprUrl": "https://www.getbeamer.com/privacy-policy", "wildcard": "1" @@ -15450,8 +16325,8 @@ "category": "Marketing", "name": "_BEAMER_FILTER_BY_URL_*", "domain": "hotjar.com", - "description": "This cookie is set by Beamer to store whether to apply URL filtering on the feed", - "retention": "20 minutes", + "description": "OCD__BEAMER_FILTER_BY_URL___description", + "retention": "OCD_retention_20_minutes", "dataController": "Hotjar", "gdprUrl": "https://www.getbeamer.com/privacy-policy", "wildcard": "1" @@ -15463,8 +16338,8 @@ "category": "Analytics", "name": "adhese2", "domain": "ads-[account].adhese.com", - "description": "Unique Reach reporting", - "retention": "30 days", + "description": "OCD_adhese2_description", + "retention": "OCD_retention_30_days", "dataController": "Adhese", "gdprUrl": "https://adhese.com/images/Privacy%20Policy_Doggybites.pdf", "wildcard": "0" @@ -15476,8 +16351,8 @@ "category": "Marketing", "name": "cap*", "domain": "ads-[account].adhese.com", - "description": "Frequency Capping", - "retention": "30 days", + "description": "OCD_cap__description", + "retention": "OCD_retention_30_days", "dataController": "Adhese", "gdprUrl": "https://adhese.com/images/Privacy%20Policy_Doggybites.pdf", "wildcard": "1" @@ -15489,8 +16364,8 @@ "category": "Marketing", "name": "pubmatic_uid", "domain": ".adhese.com", - "description": "When Adhese is used as server connection to SSPs or DSPs with whom the Account holder has a contract, a user syncing process can be installed where the SSP user_uid is stored in an Adhese cookie. This is dependent of consent for all parties involved (Accountholder, SSP, Adhese)", - "retention": "7 days", + "description": "OCD_pubmatic_uid_description", + "retention": "OCD_retention_7_days", "dataController": "Adhese", "gdprUrl": "https://adhese.com/images/Privacy%20Policy_Doggybites.pdf", "wildcard": "0" @@ -15502,8 +16377,8 @@ "category": "Marketing", "name": "improvedigital_uid", "domain": ".adhese.com", - "description": "When Adhese is used as server connection to SSPs or DSPs with whom the Account holder has a contract, a user syncing process can be installed where the SSP user_uid is stored in an Adhese cookie. This is dependent of consent for all parties involved (Accountholder, SSP, Adhese)", - "retention": "7 days", + "description": "OCD_improvedigital_uid_description", + "retention": "OCD_retention_7_days", "dataController": "Adhese", "gdprUrl": "https://adhese.com/images/Privacy%20Policy_Doggybites.pdf", "wildcard": "0" @@ -15515,8 +16390,8 @@ "category": "Marketing", "name": "rubicon_uid", "domain": ".adhese.com", - "description": "When Adhese is used as server connection to SSPs or DSPs with whom the Account holder has a contract, a user syncing process can be installed where the SSP user_uid is stored in an Adhese cookie. This is dependent of consent for all parties involved (Accountholder, SSP, Adhese)", - "retention": "7 days", + "description": "OCD_rubicon_uid_description", + "retention": "OCD_retention_7_days", "dataController": "Adhese", "gdprUrl": "https://adhese.com/images/Privacy%20Policy_Doggybites.pdf", "wildcard": "0" @@ -15528,8 +16403,8 @@ "category": "Marketing", "name": "adform_uid", "domain": ".adhese.com", - "description": "When Adhese is used as server connection to SSPs or DSPs with whom the Account holder has a contract, a user syncing process can be installed where the SSP user_uid is stored in an Adhese cookie. This is dependent of consent for all parties involved (Accountholder, SSP, Adhese)", - "retention": "7 days", + "description": "OCD_adform_uid_description", + "retention": "OCD_retention_7_days", "dataController": "Adhese", "gdprUrl": "https://adhese.com/images/Privacy%20Policy_Doggybites.pdf", "wildcard": "0" @@ -15541,8 +16416,8 @@ "category": "Marketing", "name": "appnexus_uid", "domain": ".adhese.com", - "description": "When Adhese is used as server connection to SSPs or DSPs with whom the Account holder has a contract, a user syncing process can be installed where the SSP user_uid is stored in an Adhese cookie. This is dependent of consent for all parties involved (Accountholder, SSP, Adhese)", - "retention": "7 days", + "description": "OCD_appnexus_uid_description", + "retention": "OCD_retention_7_days", "dataController": "Adhese", "gdprUrl": "https://adhese.com/images/Privacy%20Policy_Doggybites.pdf", "wildcard": "0" @@ -15554,8 +16429,8 @@ "category": "Marketing", "name": "triplelift_uid", "domain": ".adhese.com", - "description": "When Adhese is used as server connection to SSPs or DSPs with whom the Account holder has a contract, a user syncing process can be installed where the SSP user_uid is stored in an Adhese cookie. This is dependent of consent for all parties involved (Accountholder, SSP, Adhese)", - "retention": "7 days", + "description": "OCD_triplelift_uid_description", + "retention": "OCD_retention_7_days", "dataController": "Adhese", "gdprUrl": "https://adhese.com/images/Privacy%20Policy_Doggybites.pdf", "wildcard": "0" @@ -15567,8 +16442,8 @@ "category": "Marketing", "name": "adheseCustomer", "domain": ".adhese.com", - "description": "When Adhese is used as server connection to SSPs or DSPs with whom the Account holder has a contract, a user syncing process can be installed where the SSP user_uid is stored in an Adhese cookie. This is dependent of consent for all parties involved (Accountholder, SSP, Adhese)", - "retention": "7 days", + "description": "OCD_adheseCustomer_description", + "retention": "OCD_retention_7_days", "dataController": "Adhese", "gdprUrl": "https://adhese.com/images/Privacy%20Policy_Doggybites.pdf", "wildcard": "0" @@ -15580,8 +16455,8 @@ "category": "Functional", "name": "pmaAuth-*", "domain": "", - "description": "Per server authentication", - "retention": "session", + "description": "OCD_pmaAuth___description", + "retention": "OCD_retention_session", "dataController": "phpMyAdmin", "gdprUrl": "", "wildcard": "1" @@ -15593,8 +16468,8 @@ "category": "Functional", "name": "phpMyAdmin", "domain": "", - "description": "Session identifier", - "retention": "session", + "description": "OCD_phpMyAdmin_description", + "retention": "OCD_retention_session", "dataController": "phpMyAdmin", "gdprUrl": "", "wildcard": "0" @@ -15606,8 +16481,8 @@ "category": "Functional", "name": "pmaUser-*", "domain": "", - "description": "Per server username", - "retention": "30 days", + "description": "OCD_pmaUser___description", + "retention": "OCD_retention_30_days", "dataController": "phpMyAdmin", "gdprUrl": "", "wildcard": "1" @@ -15619,8 +16494,8 @@ "category": "Functional", "name": "pma_lang", "domain": "", - "description": "Language preference", - "retention": "30 days", + "description": "OCD_pma_lang_description", + "retention": "OCD_retention_30_days", "dataController": "phpMyAdmin", "gdprUrl": "", "wildcard": "0" @@ -15632,8 +16507,8 @@ "category": "Functional", "name": "PLESKSESSID", "domain": "", - "description": "Keeps a Plesk session", - "retention": "session", + "description": "OCD_PLESKSESSID_description", + "retention": "OCD_retention_session", "dataController": "Plesk", "gdprUrl": "https://www.plesk.com/privacy-policy", "wildcard": "0" @@ -15645,8 +16520,8 @@ "category": "Functional", "name": "plesk-items-per-page", "domain": "", - "description": "Save the state of UI elements in Plesk", - "retention": "session", + "description": "OCD_plesk_items_per_page_description", + "retention": "OCD_retention_session", "dataController": "Plesk", "gdprUrl": "https://www.plesk.com/privacy-policy", "wildcard": "0" @@ -15658,8 +16533,8 @@ "category": "Functional", "name": "plek-list-type", "domain": "", - "description": "Save the state of UI elements in Plesk", - "retention": "session", + "description": "OCD_plek_list_type_description", + "retention": "OCD_retention_session", "dataController": "Plesk", "gdprUrl": "https://www.plesk.com/privacy-policy", "wildcard": "0" @@ -15671,8 +16546,8 @@ "category": "Functional", "name": "plesk-sort-dir", "domain": "", - "description": "Save the state of UI elements in Plesk", - "retention": "session", + "description": "OCD_plesk_sort_dir_description", + "retention": "OCD_retention_session", "dataController": "Plesk", "gdprUrl": "https://www.plesk.com/privacy-policy", "wildcard": "0" @@ -15684,8 +16559,8 @@ "category": "Functional", "name": "plesk-sort-field", "domain": "", - "description": "Save the state of UI elements in Plesk", - "retention": "session", + "description": "OCD_plesk_sort_field_description", + "retention": "OCD_retention_session", "dataController": "Plesk", "gdprUrl": "https://www.plesk.com/privacy-policy", "wildcard": "0" @@ -15697,8 +16572,8 @@ "category": "Functional", "name": "sites-active-list-state-collapsed", "domain": "", - "description": "Save the state of UI elements in Plesk", - "retention": "session", + "description": "OCD_sites_active_list_state_collapsed_description", + "retention": "OCD_retention_session", "dataController": "Plesk", "gdprUrl": "https://www.plesk.com/privacy-policy", "wildcard": "0" @@ -15710,8 +16585,8 @@ "category": "Functional", "name": "lists-state", "domain": "", - "description": "Save the state of UI elements in Plesk", - "retention": "session", + "description": "OCD_lists_state_description", + "retention": "OCD_retention_session", "dataController": "Plesk", "gdprUrl": "https://www.plesk.com/privacy-policy", "wildcard": "0" @@ -15723,8 +16598,8 @@ "category": "Functional", "name": "fe_typo_user", "domain": "", - "description": "Used to identify a session ID when logged-in to the TYPO3 Frontend", - "retention": "session", + "description": "OCD_fe_typo_user_description", + "retention": "OCD_retention_session", "dataController": "TYPO3", "gdprUrl": "https://typo3.org/privacy-policy", "wildcard": "0" @@ -15736,8 +16611,8 @@ "category": "Functional", "name": "be_typo_user", "domain": "", - "description": "Used to identify a backend session when a Backend User logged in to TYPO3 Backend or Frontend", - "retention": "session", + "description": "OCD_be_typo_user_description", + "retention": "OCD_retention_session", "dataController": "TYPO3", "gdprUrl": "https://typo3.org/privacy-policy", "wildcard": "0" @@ -15749,8 +16624,8 @@ "category": "Functional", "name": "Typo3InstallTool", "domain": "", - "description": "Used to validate a session for the System Maintenance Area / Install Tool", - "retention": "session", + "description": "OCD_Typo3InstallTool_description", + "retention": "OCD_retention_session", "dataController": "TYPO3", "gdprUrl": "https://typo3.org/privacy-policy", "wildcard": "0" @@ -15762,8 +16637,8 @@ "category": "Functional", "name": "be_lastLoginProvider", "domain": "", - "description": "Stores information about the last login provider when logging into TYPO3 Backend", - "retention": "session", + "description": "OCD_be_lastLoginProvider_description", + "retention": "OCD_retention_session", "dataController": "TYPO3", "gdprUrl": "https://typo3.org/privacy-policy", "wildcard": "0" @@ -15775,8 +16650,8 @@ "category": "Marketing", "name": "KelkooID", "domain": "kelkoogroup.net", - "description": "This cookie identifies the user for statistics and ad retargeting.", - "retention": "1 year", + "description": "OCD_KelkooID_description", + "retention": "OCD_retention_1_year", "dataController": "Kelkoo", "gdprUrl": "https://www.kelkoogroup.com/privacy-policy/", "wildcard": "0" @@ -15788,8 +16663,8 @@ "category": "Marketing", "name": "_cio", "domain": "", - "description": "Used to identify visitors in order to send transactional and targeted email messages.", - "retention": "1 day", + "description": "OCD__cio_description", + "retention": "OCD_retention_1_day", "dataController": "Customer.io", "gdprUrl": "https://customer.io/legal/privacy-policy/", "wildcard": "0" @@ -15801,8 +16676,8 @@ "category": "Marketing", "name": "_cioid", "domain": "", - "description": "Used to identify visitors in order to send transactional and targeted email messages.", - "retention": "1 year", + "description": "OCD__cioid_description", + "retention": "OCD_retention_1_year", "dataController": "Customer.io", "gdprUrl": "https://customer.io/legal/privacy-policy/", "wildcard": "0" @@ -15814,8 +16689,8 @@ "category": "Marketing", "name": "_cioanonid", "domain": "", - "description": "Used to identify visitors in order to send transactional and targeted email messages.", - "retention": "1 year", + "description": "OCD__cioanonid_description", + "retention": "OCD_retention_1_year", "dataController": "Customer.io", "gdprUrl": "https://customer.io/legal/privacy-policy/", "wildcard": "0" @@ -15827,8 +16702,8 @@ "category": "Marketing", "name": "cioFT", "domain": "", - "description": "Used to identify visitors in order to send transactional and targeted email messages.", - "retention": "1 year", + "description": "OCD_cioFT_description", + "retention": "OCD_retention_1_year", "dataController": "Customer.io", "gdprUrl": "https://customer.io/legal/privacy-policy/", "wildcard": "0" @@ -15840,8 +16715,8 @@ "category": "Marketing", "name": "cioLT", "domain": "", - "description": "Used to identify visitors in order to send transactional and targeted email messages.", - "retention": "1 year", + "description": "OCD_cioLT_description", + "retention": "OCD_retention_1_year", "dataController": "Customer.io", "gdprUrl": "https://customer.io/legal/privacy-policy/", "wildcard": "0" @@ -15853,8 +16728,8 @@ "category": "Analytics", "name": "_chartbeat*", "domain": "", - "description": "Cookie is used to register if a person has visited the domain before (to calculate new vs returning users).", - "retention": "30 days", + "description": "OCD__chartbeat__description", + "retention": "OCD_retention_30_days", "dataController": "Chartbeat", "gdprUrl": "https://chartbeat.com/privacy/", "wildcard": "1" @@ -15866,8 +16741,8 @@ "category": "Analytics", "name": "_SUPERFLY_nosample", "domain": "", - "description": "Cookie is used only if you go over your plan's traffic limit. At that point the cookie is set and will disable the beacon from that visitor for one hour.", - "retention": "1 hour", + "description": "OCD__SUPERFLY_nosample_description", + "retention": "OCD_retention_1_hour", "dataController": "Chartbeat", "gdprUrl": "https://chartbeat.com/privacy/", "wildcard": "0" @@ -15879,8 +16754,8 @@ "category": "Functional", "name": "Datadome", "domain": "", - "description": "This a security cookie based upon detecting BOTS and malicious traffic.", - "retention": "1 year", + "description": "OCD_Datadome_description", + "retention": "OCD_retention_1_year", "dataController": "Datadome", "gdprUrl": "https://datadome.co/privacy-policy/", "wildcard": "0" @@ -15892,8 +16767,8 @@ "category": "Marketing", "name": "TXCSDMN_*", "domain": "tappx.com", - "description": "This cookie is associated with Tappx, an AdTech platform.", - "retention": "1 month", + "description": "OCD_TXCSDMN___description", + "retention": "OCD_retention_1_month", "dataController": "Tappx", "gdprUrl": "https://www.tappx.com/legal/privacy-policy", "wildcard": "1" @@ -15905,8 +16780,8 @@ "category": "Marketing", "name": "TXCD", "domain": "tappx.com", - "description": "This cookie is associated with Tappx, an AdTech platform.", - "retention": "1 month", + "description": "OCD_TXCD_description", + "retention": "OCD_retention_1_month", "dataController": "Tappx", "gdprUrl": "https://www.tappx.com/legal/privacy-policy", "wildcard": "0" @@ -15918,8 +16793,8 @@ "category": "Marketing", "name": "rai-pltn-pl-*", "domain": "richaudience.com", - "description": "Ad-serving frequency control, optimization and Brand Safety.", - "retention": "1 day", + "description": "OCD_rai_pltn_pl___description", + "retention": "OCD_retention_1_day", "dataController": "richAudience", "gdprUrl": "https://richaudience.com/en/privacy/", "wildcard": "1" @@ -15931,8 +16806,8 @@ "category": "Marketing", "name": "avcid-*", "domain": "richaudience.com", - "description": "ID Syncing with DSP / SSP for communications using Open RTB protocol", - "retention": "90 days", + "description": "OCD_avcid___description", + "retention": "OCD_retention_90_days", "dataController": "richAudience", "gdprUrl": "https://richaudience.com/en/privacy/", "wildcard": "1" @@ -15944,8 +16819,8 @@ "category": "Marketing", "name": "pdid", "domain": "richaudience.com", - "description": "Randomly generated user ID.", - "retention": "1 month", + "description": "OCD_pdid_description", + "retention": "OCD_retention_1_month", "dataController": "richAudience", "gdprUrl": "https://richaudience.com/en/privacy/", "wildcard": "0" @@ -15957,8 +16832,8 @@ "category": "Marketing", "name": "data-*", "domain": "contextual.media.net", - "description": "Cookie used to record your browsing activity, with the purpose of displaying targeted ads.", - "retention": "1 year", + "description": "OCD_data___description", + "retention": "OCD_retention_1_year", "dataController": "Media.net", "gdprUrl": "https://www.media.net/privacy-policy/", "wildcard": "1" @@ -15970,8 +16845,8 @@ "category": "Marketing", "name": "visitor-id", "domain": "contextual.media.net", - "description": "This cookie is used to collect information on the visitor, which we then use for analytics purposes.", - "retention": "1 year", + "description": "OCD_visitor_id_description", + "retention": "OCD_retention_1_year", "dataController": "Media.net", "gdprUrl": "https://www.media.net/privacy-policy/", "wildcard": "0" @@ -15983,8 +16858,8 @@ "category": "Marketing", "name": "gdpr_status", "domain": "contextual.media.net", - "description": "Determines whether you have accepted the cookie consent box, to prevent it being shown the next time you visit", - "retention": "6 months", + "description": "OCD_gdpr_status_description", + "retention": "OCD_retention_6_months", "dataController": "Media.net", "gdprUrl": "https://www.media.net/privacy-policy/", "wildcard": "0" @@ -15996,8 +16871,8 @@ "category": "Functional", "name": "_pbjs_userid_consent_data", "domain": "", - "description": "This cookie is used to know if the user's consent choices have changed since the last page load. It is a hashed (cyrb53Hash) value of the consent string with a 30 day expiration.", - "retention": "30 days", + "description": "OCD__pbjs_userid_consent_data_description", + "retention": "OCD_retention_30_days", "dataController": "Prebid", "gdprUrl": "https://prebidprd.wpengine.com/privacy-policy/", "wildcard": "0" @@ -16009,8 +16884,8 @@ "category": "Marketing", "name": "DotomiUser", "domain": "dotomi.com", - "description": "This cookie is set by the provider Dotomi. This cookie is used for sales/lead correlation and for targeting and marketing purposes. it is used to store unique surfer ID.", - "retention": "13 months", + "description": "OCD_DotomiUser_description", + "retention": "OCD_retention_13_months", "dataController": "Epsilon", "gdprUrl": "https://legal.epsilon.com/global-privacy-policies/", "wildcard": "0" @@ -16022,8 +16897,8 @@ "category": "Marketing", "name": "cjae", "domain": "dotomi.com", - "description": "The cookie is set by the provider Dotomi. This cookie is used to record visitor behaviour.", - "retention": "1 month", + "description": "OCD_cjae_description", + "retention": "OCD_retention_1_month", "dataController": "Epsilon", "gdprUrl": "https://legal.epsilon.com/global-privacy-policies/", "wildcard": "0" @@ -16035,8 +16910,8 @@ "category": "Marketing", "name": "DotomiStatus", "domain": "dotomi.com", - "description": "Used to honor device-level opt-out preferences.", - "retention": "5 years", + "description": "OCD_DotomiStatus_description", + "retention": "OCD_retention_5_years", "dataController": "Epsilon", "gdprUrl": "https://legal.epsilon.com/global-privacy-policies/", "wildcard": "0" @@ -16048,8 +16923,8 @@ "category": "Marketing", "name": "DotomiSession_*", "domain": "dotomi.com", - "description": "Pseudonymous session id", - "retention": "session", + "description": "OCD_DotomiSession___description", + "retention": "OCD_retention_session", "dataController": "Epsilon", "gdprUrl": "https://legal.epsilon.com/global-privacy-policies/", "wildcard": "1" @@ -16061,8 +16936,8 @@ "category": "Marketing", "name": "DotomiSync", "domain": "dotomi.com", - "description": "Used to identify which sync pixels we set on users via registration tags", - "retention": "1 year", + "description": "OCD_DotomiSync_description", + "retention": "OCD_retention_1_year", "dataController": "Epsilon", "gdprUrl": "https://legal.epsilon.com/global-privacy-policies/", "wildcard": "0" @@ -16074,8 +16949,8 @@ "category": "Marketing", "name": "dtm_token", "domain": "", - "description": "Manage cookie level profile, freq. cap, retargeting", - "retention": "13 months", + "description": "OCD_dtm_token_description", + "retention": "OCD_retention_13_months", "dataController": "Epsilon", "gdprUrl": "https://legal.epsilon.com/global-privacy-policies/", "wildcard": "0" @@ -16087,8 +16962,8 @@ "category": "Marketing", "name": "dtm_token_exp", "domain": "", - "description": "Logs timestamp for dtm_token cookie", - "retention": "session", + "description": "OCD_dtm_token_exp_description", + "retention": "OCD_retention_session", "dataController": "Epsilon", "gdprUrl": "https://legal.epsilon.com/global-privacy-policies/", "wildcard": "0" @@ -16100,8 +16975,8 @@ "category": "Marketing", "name": "dtm_tcdata", "domain": "", - "description": "Stores consent for vendors that participate in the IAB Transparency and Consent Framework.", - "retention": "1 day", + "description": "OCD_dtm_tcdata_description", + "retention": "OCD_retention_1_day", "dataController": "Epsilon", "gdprUrl": "https://legal.epsilon.com/global-privacy-policies/", "wildcard": "0" @@ -16113,8 +16988,8 @@ "category": "Marketing", "name": "dtm_tcdata_exp", "domain": "", - "description": "Logs timestamp for dtm_tcdata cookie", - "retention": "session", + "description": "OCD_dtm_tcdata_exp_description", + "retention": "OCD_retention_session", "dataController": "Epsilon", "gdprUrl": "https://legal.epsilon.com/global-privacy-policies/", "wildcard": "0" @@ -16126,8 +17001,8 @@ "category": "Marketing", "name": "dtm_token_sc", "domain": "", - "description": "Our first party cookie set via headers on registration tags", - "retention": "13 months", + "description": "OCD_dtm_token_sc_description", + "retention": "OCD_retention_13_months", "dataController": "Epsilon", "gdprUrl": "https://legal.epsilon.com/global-privacy-policies/", "wildcard": "0" @@ -16139,8 +17014,8 @@ "category": "Marketing", "name": "dtm_user_id", "domain": "", - "description": "Used to identify users registration", - "retention": "13 months", + "description": "OCD_dtm_user_id_description", + "retention": "OCD_retention_13_months", "dataController": "Epsilon", "gdprUrl": "https://legal.epsilon.com/global-privacy-policies/", "wildcard": "0" @@ -16152,8 +17027,8 @@ "category": "Marketing", "name": "dtm_user_id_sc", "domain": "", - "description": "Used to identify users registration", - "retention": "13 months", + "description": "OCD_dtm_user_id_sc_description", + "retention": "OCD_retention_13_months", "dataController": "Epsilon", "gdprUrl": "https://legal.epsilon.com/global-privacy-policies/", "wildcard": "0" @@ -16165,8 +17040,8 @@ "category": "Marketing", "name": "dtm_gdpr_delete", "domain": "dotomi.com", - "description": "Set when GDPR data delete is executed. Life span is 30 days. When this cookie exists, GDPR consent is considered revoked.", - "retention": "30 days", + "description": "OCD_dtm_gdpr_delete_description", + "retention": "OCD_retention_30_days", "dataController": "Epsilon", "gdprUrl": "https://legal.epsilon.com/global-privacy-policies/", "wildcard": "0" @@ -16178,8 +17053,8 @@ "category": "Marketing", "name": "dtm_gpc_optout", "domain": "dotomi.com", - "description": "Set when GPC Optout is initiated. Presence of this cookie helps us prevent multiple downstream optout requests for the same user", - "retention": "30 days", + "description": "OCD_dtm_gpc_optout_description", + "retention": "OCD_retention_30_days", "dataController": "Epsilon", "gdprUrl": "https://legal.epsilon.com/global-privacy-policies/", "wildcard": "0" @@ -16191,8 +17066,8 @@ "category": "Marketing", "name": "pluto2", "domain": "fastclick.net", - "description": "This is a temporary cookie that is created in the case when no PLUTO cookie is set AND the user hits the advertiser site where a Re-Targeting pixel has been executed.", - "retention": "13 months", + "description": "OCD_pluto2_description", + "retention": "OCD_retention_13_months", "dataController": "Epsilon", "gdprUrl": "https://legal.epsilon.com/global-privacy-policies/", "wildcard": "0" @@ -16204,8 +17079,8 @@ "category": "Marketing", "name": "pluto", "domain": "fastclick.net", - "description": "The Session ID is used to track preference information.", - "retention": "13 months", + "description": "OCD_pluto_description", + "retention": "OCD_retention_13_months", "dataController": "Epsilon", "gdprUrl": "https://legal.epsilon.com/global-privacy-policies/", "wildcard": "0" @@ -16217,8 +17092,8 @@ "category": "Functional", "name": "fastclick", "domain": "fastclick.net", - "description": "Tells the delivery system that the browser had opted out of the network", - "retention": "10 years", + "description": "OCD_fastclick_description", + "retention": "OCD_retention_10_years", "dataController": "Epsilon", "gdprUrl": "https://legal.epsilon.com/global-privacy-policies/", "wildcard": "0" @@ -16230,8 +17105,8 @@ "category": "Marketing", "name": "svid", "domain": "mediaplex.com", - "description": "Used to relate preference information for marketing purposes", - "retention": "13 months", + "description": "OCD_svid_description", + "retention": "OCD_retention_13_months", "dataController": "Epsilon", "gdprUrl": "https://legal.epsilon.com/global-privacy-policies/", "wildcard": "0" @@ -16243,8 +17118,8 @@ "category": "Marketing", "name": "rts", "domain": "mediaplex.com", - "description": "Used to track last time browser was redirected through Corporate Cookie Service / Dotomi.com domain", - "retention": "13 months", + "description": "OCD_rts_description", + "retention": "OCD_retention_13_months", "dataController": "Epsilon", "gdprUrl": "https://legal.epsilon.com/global-privacy-policies/", "wildcard": "0" @@ -16256,8 +17131,8 @@ "category": "Functional", "name": "lhc_per", "domain": "", - "description": "Stores persistent information about chat id to be able to keep same chat while customer is navigating through pages.", - "retention": "180 days", + "description": "OCD_lhc_per_description", + "retention": "OCD_retention_180_days", "dataController": "Live helper chat", "gdprUrl": "https://livehelperchat.com/gdpr-compliance-504a.html", "wildcard": "0" @@ -16269,8 +17144,8 @@ "category": "Functional", "name": "lhc_ldep", "domain": "", - "description": "Stores required department id. To disable user to change department.", - "retention": "Unknown", + "description": "OCD_lhc_ldep_description", + "retention": "OCD_retention_Unknown", "dataController": "Live helper chat", "gdprUrl": "https://livehelperchat.com/gdpr-compliance-504a.html", "wildcard": "0" @@ -16282,8 +17157,8 @@ "category": "Functional", "name": "lhc_ses", "domain": "", - "description": "Stores temporary information about chat. Was invitation to chat shown or not.", - "retention": "session", + "description": "OCD_lhc_ses_description", + "retention": "OCD_retention_session", "dataController": "Live helper chat", "gdprUrl": "https://livehelperchat.com/gdpr-compliance-504a.html", "wildcard": "0" @@ -16295,8 +17170,8 @@ "category": "Analytics", "name": "_ym_metrika_enabled", "domain": "", - "description": "Checks whether other Yandex.Metrica cookies are installed correctly", - "retention": "60 minutes", + "description": "OCD__ym_metrika_enabled_description", + "retention": "OCD_retention_60_minutes", "dataController": "Yandex", "gdprUrl": "https://yandex.com/support/metrica/general/gdpr.html", "wildcard": "0" @@ -16308,8 +17183,8 @@ "category": "Analytics", "name": "_ym_isad", "domain": "", - "description": "Determines whether a user has ad blockers", - "retention": "2 days", + "description": "OCD__ym_isad_description", + "retention": "OCD_retention_2_days", "dataController": "Yandex", "gdprUrl": "https://yandex.com/support/metrica/general/gdpr.html", "wildcard": "0" @@ -16321,8 +17196,8 @@ "category": "Analytics", "name": "_ym_uid", "domain": "", - "description": "Used for identifying site users", - "retention": "1 year", + "description": "OCD__ym_uid_description", + "retention": "OCD_retention_1_year", "dataController": "Yandex", "gdprUrl": "https://yandex.com/support/metrica/general/gdpr.html", "wildcard": "0" @@ -16334,8 +17209,8 @@ "category": "Analytics", "name": "_ym_d", "domain": "", - "description": "Saves the date of the user's first site session", - "retention": "1 year", + "description": "OCD__ym_d_description", + "retention": "OCD_retention_1_year", "dataController": "Yandex", "gdprUrl": "https://yandex.com/support/metrica/general/gdpr.html", "wildcard": "0" @@ -16347,8 +17222,8 @@ "category": "Analytics", "name": "yabs-sid", "domain": ".yandex.ru", - "description": "Session ID", - "retention": "session", + "description": "OCD_yabs_sid_description", + "retention": "OCD_retention_session", "dataController": "Yandex", "gdprUrl": "https://yandex.com/support/metrica/general/gdpr.html", "wildcard": "0" @@ -16360,8 +17235,8 @@ "category": "Functional", "name": "_ym_debug", "domain": ".yandex.ru", - "description": "Indicates that debug mode is active", - "retention": "session", + "description": "OCD__ym_debug_description", + "retention": "OCD_retention_session", "dataController": "Yandex", "gdprUrl": "https://yandex.com/support/metrica/general/gdpr.html", "wildcard": "0" @@ -16373,8 +17248,8 @@ "category": "Analytics", "name": "_ym_visorc_*", "domain": ".yandex.ru", - "description": "Allows Session Replay to function correctly", - "retention": "30 minutes", + "description": "OCD__ym_visorc___description", + "retention": "OCD_retention_30_minutes", "dataController": "Yandex", "gdprUrl": "https://yandex.com/support/metrica/general/gdpr.html", "wildcard": "1" @@ -16386,8 +17261,8 @@ "category": "Functional", "name": "_ym_hostIndex", "domain": ".yandex.ru", - "description": "Limits the number of requests", - "retention": "1 day", + "description": "OCD__ym_hostIndex_description", + "retention": "OCD_retention_1_day", "dataController": "Yandex", "gdprUrl": "https://yandex.com/support/metrica/general/gdpr.html", "wildcard": "0" @@ -16399,8 +17274,8 @@ "category": "Analytics", "name": "yandexuid", "domain": ".yandex.ru", - "description": "Registers data on visitors' website-behaviour. This is used for internal analysis and website optimization.", - "retention": "1 year (in some countries, the period may be longer)", + "description": "OCD_yandexuid_description", + "retention": "OCD_retention_1_year__in_some_countries__the_period_may_be_longer_", "dataController": "Yandex", "gdprUrl": "https://yandex.com/support/metrica/general/gdpr.html", "wildcard": "0" @@ -16412,8 +17287,8 @@ "category": "Analytics", "name": "yuidss", "domain": ".yandex.ru", - "description": "Registers data on visitors' website-behaviour. This is used for internal analysis and website optimization.", - "retention": "1 year (in some countries, the period may be longer)", + "description": "OCD_yuidss_description", + "retention": "OCD_retention_1_year__in_some_countries__the_period_may_be_longer_", "dataController": "Yandex", "gdprUrl": "https://yandex.com/support/metrica/general/gdpr.html", "wildcard": "0" @@ -16425,8 +17300,8 @@ "category": "Analytics", "name": "ymex", "domain": ".yandex.ru", - "description": "Stores auxiliary information for Yandex.Metrica performance: ID creation time and their alternative values.", - "retention": "1 year ", + "description": "OCD_ymex_description", + "retention": "OCD_retention_1_year_", "dataController": "Yandex", "gdprUrl": "https://yandex.com/support/metrica/general/gdpr.html", "wildcard": "0" @@ -16438,8 +17313,8 @@ "category": "Analytics", "name": "usst", "domain": ".yandex.ru", - "description": "Stores auxiliary information for syncing site user IDs between different Yandex domains", - "retention": "1 year", + "description": "OCD_usst_description", + "retention": "OCD_retention_1_year", "dataController": "Yandex", "gdprUrl": "https://yandex.com/support/metrica/general/gdpr.html", "wildcard": "0" @@ -16451,8 +17326,8 @@ "category": "Analytics", "name": "is_gdpr_b", "domain": ".yandex.ru", - "description": "Detecting users from regions where the General Data Protection Regulation (GDPR) applies", - "retention": "1 year", + "description": "OCD_is_gdpr_b_description", + "retention": "OCD_retention_1_year", "dataController": "Yandex", "gdprUrl": "https://yandex.com/support/metrica/general/gdpr.html", "wildcard": "0" @@ -16464,8 +17339,8 @@ "category": "Analytics", "name": "is_gdpr", "domain": ".yandex.ru", - "description": "Detecting users from regions where the General Data Protection Regulation (GDPR) applies", - "retention": "1 year", + "description": "OCD_is_gdpr_description", + "retention": "OCD_retention_1_year", "dataController": "Yandex", "gdprUrl": "https://yandex.com/support/metrica/general/gdpr.html", "wildcard": "0" @@ -16477,8 +17352,8 @@ "category": "Analytics", "name": "yabs-vdrf", "domain": ".yandex.ru", - "description": "Registers data on visitors from multiple visits and on multiple websites. This information is used to measure the efficiency of advertisement on websites.", - "retention": "5 days", + "description": "OCD_yabs_vdrf_description", + "retention": "OCD_retention_5_days", "dataController": "Yandex", "gdprUrl": "https://yandex.com/support/metrica/general/gdpr.html", "wildcard": "0" @@ -16490,8 +17365,8 @@ "category": "Analytics", "name": "bh", "domain": ".yandex.ru", - "description": "Collects data on user behaviour and interaction in order to optimize the website and make advertisement on the website more relevant.", - "retention": "1 Year", + "description": "OCD_bh_description", + "retention": "OCD_retention_1_Year", "dataController": "Yandex", "gdprUrl": "https://yandex.com/support/metrica/general/gdpr.html", "wildcard": "0" @@ -16503,8 +17378,8 @@ "category": "Analytics", "name": "_yasc", "domain": ".yandex.ru", - "description": "Collects data on the user across websites - This data is used to make advertisement more relevant.", - "retention": "10 Years", + "description": "OCD__yasc_description", + "retention": "OCD_retention_10_Years", "dataController": "Yandex", "gdprUrl": "https://yandex.com/support/metrica/general/gdpr.html", "wildcard": "0" @@ -16516,8 +17391,8 @@ "category": "Analytics", "name": "yashr", "domain": ".yandex.ru", - "description": "Collects data on the user across websites - This data is used to make advertisement more relevant.", - "retention": "1 Year", + "description": "OCD_yashr_description", + "retention": "OCD_retention_1_Year", "dataController": "Yandex", "gdprUrl": "https://yandex.com/support/metrica/general/gdpr.html", "wildcard": "0" @@ -16529,8 +17404,8 @@ "category": "Functional", "name": "_KMPage*", "domain": "", - "description": "In Salesforce Classic, used to read the last user selection for Find in View, Article Language, {DataCategory}, and Validation Status in Article Management.", - "retention": "1 day", + "description": "OCD__KMPage__description", + "retention": "OCD_retention_1_day", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "1" @@ -16542,8 +17417,8 @@ "category": "Functional", "name": "_KnowledgePageDispatcher*", "domain": "", - "description": "In Salesforce Classic, used to remember the user selection to determine whether to show Articles or My Drafts view in Knowledge.", - "retention": "session", + "description": "OCD__KnowledgePageDispatcher__description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "1" @@ -16555,8 +17430,8 @@ "category": "Functional", "name": "_KnowledgePageFilter*", "domain": "", - "description": "In Salesforce Classic, used to remember the last user selection for the data category filter in Knowledge.", - "retention": "session", + "description": "OCD__KnowledgePageFilter__description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "1" @@ -16568,8 +17443,8 @@ "category": "Functional", "name": "_KnowledgePageFilterArticleArticleType*", "domain": "", - "description": "In Salesforce Classic, used to remember the last user selection for the article type filter for Articles view in Knowledge.", - "retention": "session", + "description": "OCD__KnowledgePageFilterArticleArticleType__description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "1" @@ -16581,8 +17456,8 @@ "category": "Functional", "name": "_KnowledgePageFilterArticlePublishStatus*", "domain": "", - "description": "In Salesforce Classic, used to remember the last user selection for the publish status filter for Articles view in Knowledge.", - "retention": "session", + "description": "OCD__KnowledgePageFilterArticlePublishStatus__description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "1" @@ -16594,8 +17469,8 @@ "category": "Functional", "name": "_KnowledgePageFilterArticleValidationStatus*", "domain": "", - "description": "In Salesforce Classic, used to remember the last user selection for the validation status filter for Articles view in Knowledge.", - "retention": "session", + "description": "OCD__KnowledgePageFilterArticleValidationStatus__description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "1" @@ -16607,8 +17482,8 @@ "category": "Functional", "name": "_KnowledgePageFilterLanguage*", "domain": "", - "description": "In Salesforce Classic, used to remember the last user selection for the language filter in Knowledge.", - "retention": "session", + "description": "OCD__KnowledgePageFilterLanguage__description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "1" @@ -16620,8 +17495,8 @@ "category": "Functional", "name": "_KnowledgePageFilterMyDraftArticleType*", "domain": "", - "description": "In Salesforce Classic, used to remember the last user selection for the article type filter for My Drafts view in Knowledge.", - "retention": "session", + "description": "OCD__KnowledgePageFilterMyDraftArticleType__description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "1" @@ -16633,8 +17508,8 @@ "category": "Functional", "name": "_KnowledgePageFilterMyDraftPublishStatus*", "domain": "", - "description": "In Salesforce Classic, used to remember the last user selection for the publish status filter for My Drafts view in Knowledge.", - "retention": "session", + "description": "OCD__KnowledgePageFilterMyDraftPublishStatus__description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "1" @@ -16646,8 +17521,8 @@ "category": "Functional", "name": "_KnowledgePageFilterMyDraftValidationStatus*", "domain": "", - "description": "In Salesforce Classic, used to remember the last user selection for the validation status filter for My Drafts view in Knowledge.", - "retention": "session", + "description": "OCD__KnowledgePageFilterMyDraftValidationStatus__description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "1" @@ -16659,8 +17534,8 @@ "category": "Functional", "name": "_KnowledgePageSortFieldArticle*", "domain": "", - "description": "In Salesforce Classic, used to remember the last user selection for Sort by for the Articles view in Knowledge.", - "retention": "session", + "description": "OCD__KnowledgePageSortFieldArticle__description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "1" @@ -16672,8 +17547,8 @@ "category": "Functional", "name": "_KnowledgePageSortFieldMyDraft*", "domain": "", - "description": "In Salesforce Classic, used to remember the last user selection for Sort by for the My Drafts view in Knowledge.", - "retention": "session", + "description": "OCD__KnowledgePageSortFieldMyDraft__description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "1" @@ -16685,8 +17560,8 @@ "category": "Functional", "name": "_spring_KmMlAnyoneDraftArticlesList*", "domain": "", - "description": "In Salesforce Classic, used to configure layout properties for the Draft Articles view in Article Management.", - "retention": "1 day", + "description": "OCD__spring_KmMlAnyoneDraftArticlesList__description", + "retention": "OCD_retention_1_day", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "1" @@ -16698,8 +17573,8 @@ "category": "Functional", "name": "_spring_KmMlArchivedArticlesList*", "domain": "", - "description": "In Salesforce Classic, used to configure layout properties for Archived Articles in Article Management.", - "retention": "1 day", + "description": "OCD__spring_KmMlArchivedArticlesList__description", + "retention": "OCD_retention_1_day", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "1" @@ -16711,8 +17586,8 @@ "category": "Functional", "name": "_spring_KmMlMyDraftArticlesList*", "domain": "", - "description": "In Salesforce Classic, used to configure layout properties for Draft Articles assigned to Me in Article Management.", - "retention": "1 day", + "description": "OCD__spring_KmMlMyDraftArticlesList__description", + "retention": "OCD_retention_1_day", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "1" @@ -16724,8 +17599,8 @@ "category": "Functional", "name": "_spring_KmMlMyDraftTranslationsList*", "domain": "", - "description": "In Salesforce Classic, used to configure layout properties for Draft Translations in Article Management.", - "retention": "1 day", + "description": "OCD__spring_KmMlMyDraftTranslationsList__description", + "retention": "OCD_retention_1_day", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "1" @@ -16737,8 +17612,8 @@ "category": "Functional", "name": "_spring_KmMlPublishedArticlesList*", "domain": "", - "description": "In Salesforce Classic, used to configure layout properties for Published Articles in Article Management.", - "retention": "1 day", + "description": "OCD__spring_KmMlPublishedArticlesList__description", + "retention": "OCD_retention_1_day", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "1" @@ -16750,8 +17625,8 @@ "category": "Functional", "name": "_spring_KmMlPublishedTranslationsList*", "domain": "", - "description": "In Salesforce Classic, used to configure layout properties for Published Translations in Article Management.", - "retention": "1 day", + "description": "OCD__spring_KmMlPublishedTranslationsList__description", + "retention": "OCD_retention_1_day", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "1" @@ -16763,8 +17638,8 @@ "category": "Functional", "name": "_sid*", "domain": "", - "description": "Identifies a Live Agent session. Stores a unique pseudonymous ID for a specific browser session over chat service.", - "retention": "session", + "description": "OCD__sid__description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "1" @@ -16776,8 +17651,8 @@ "category": "Functional", "name": "activeView", "domain": "", - "description": "In Salesforce Classic, used to remember the last user selection for Articles or Translations tab in Article Management.", - "retention": "session", + "description": "OCD_activeView_description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -16789,8 +17664,8 @@ "category": "Marketing", "name": "apex__EmailAddress", "domain": "", - "description": "Caches contact IDs associated with email addresses.", - "retention": "1 year", + "description": "OCD_apex__EmailAddress_description", + "retention": "OCD_retention_1_year", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -16802,8 +17677,8 @@ "category": "Analytics", "name": "auraBrokenDefGraph", "domain": "", - "description": "Used to track when a Lightning page has malformed HTML.", - "retention": "1 week", + "description": "OCD_auraBrokenDefGraph_description", + "retention": "OCD_retention_1_week", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -16815,8 +17690,8 @@ "category": "Functional", "name": "autocomplete", "domain": "", - "description": "Determines if the login page remembers the user’s username.", - "retention": "60 days", + "description": "OCD_autocomplete_description", + "retention": "OCD_retention_60_days", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -16828,8 +17703,8 @@ "category": "Functional", "name": "BAYEAX_BROWSER", "domain": "", - "description": "Identify a unique browser subscribed to CometD streaming channels.", - "retention": "session", + "description": "OCD_BAYEAX_BROWSER_description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -16841,8 +17716,8 @@ "category": "Analytics", "name": "calViewState", "domain": "", - "description": "Sets the inline calendar date state in Salesforce Classic (current week selected).", - "retention": "session", + "description": "OCD_calViewState_description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -16854,8 +17729,8 @@ "category": "Functional", "name": "caPanelState", "domain": "", - "description": "Saves the open, closed, and height percent states of the calendar panel.", - "retention": "session", + "description": "OCD_caPanelState_description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -16867,8 +17742,8 @@ "category": "Functional", "name": "renderCtx", "domain": "salesforce.com", - "description": "Used to deliver requested pages and content based on a user's navigation.", - "retention": "session", + "description": "OCD_renderCtx_description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/legal/", "wildcard": "0" @@ -16880,8 +17755,8 @@ "category": "Analytics", "name": "pctrk", "domain": "salesforce.com", - "description": "Used to count page views by unauthenticated users against license usage.", - "retention": "session", + "description": "OCD_pctrk_description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/legal/", "wildcard": "0" @@ -16893,8 +17768,8 @@ "category": "Functional", "name": "force-stream", "domain": "salesforce.com", - "description": "Used to properly route server requests within Salesforce infrastructure for sticky sessions.", - "retention": "3 hours", + "description": "OCD_force_stream_description", + "retention": "OCD_retention_3_hours", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/legal/", "wildcard": "0" @@ -16906,8 +17781,8 @@ "category": "Functional", "name": "sfdc-stream", "domain": "salesforce.com", - "description": "Used to properly route server requests within Salesforce infrastructure for sticky sessions.", - "retention": "3 hours", + "description": "OCD_sfdc_stream_description", + "retention": "OCD_retention_3_hours", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/legal/", "wildcard": "0" @@ -16919,8 +17794,8 @@ "category": "Functional", "name": "BrowserId_sec", "domain": "salesforce.com", - "description": "Used to log secure browser sessions/visits for internal-only product analytics.", - "retention": "1 year", + "description": "OCD_BrowserId_sec_description", + "retention": "OCD_retention_1_year", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/legal/", "wildcard": "0" @@ -16932,8 +17807,8 @@ "category": "Functional", "name": "force-proxy-stream", "domain": "salesforce.com", - "description": "Used to ensure client requests hit the same proxy hosts and are more likely to retrieve content from cache.", - "retention": "3 hours", + "description": "OCD_force_proxy_stream_description", + "retention": "OCD_retention_3_hours", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/legal/", "wildcard": "0" @@ -16945,8 +17820,8 @@ "category": "Functional", "name": "BrowserId", "domain": "salesforce.com", - "description": "Used to log browser sessions/visits for internal-only product analytics.", - "retention": "1 year", + "description": "OCD_BrowserId_description", + "retention": "OCD_retention_1_year", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -16958,8 +17833,8 @@ "category": "Functional", "name": "QCQQ", "domain": "salesforce.com", - "description": "Used to detect the official login page for Forced Login POST detection.", - "retention": "session", + "description": "OCD_QCQQ_description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -16971,8 +17846,8 @@ "category": "Functional", "name": "sid_Client", "domain": "salesforce.com", - "description": "Used to validate orgid and userid on the client side.", - "retention": "session", + "description": "OCD_sid_Client_description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -16984,8 +17859,8 @@ "category": "Functional", "name": "idccsrf", "domain": "salesforce.com", - "description": "Used for SSO authentication as CSRF protection.", - "retention": "3 months", + "description": "OCD_idccsrf_description", + "retention": "OCD_retention_3_months", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -16997,8 +17872,8 @@ "category": "Functional", "name": "rsid", "domain": "salesforce.com", - "description": "Used for an admin user to 'log in as' one of their org user.", - "retention": "session", + "description": "OCD_rsid_description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17010,8 +17885,8 @@ "category": "Functional", "name": "rsid2", "domain": "salesforce.com", - "description": "Used for an admin user to 'log in as' one of their org portal user.", - "retention": "session", + "description": "OCD_rsid2_description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17023,8 +17898,8 @@ "category": "Functional", "name": "RRetURL", "domain": "salesforce.com", - "description": "Used for 'log in as' to return to original page.", - "retention": "session", + "description": "OCD_RRetURL_description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17036,8 +17911,8 @@ "category": "Functional", "name": "RRetURL2", "domain": "salesforce.com", - "description": "Used for portal 'log in as' to return to original page.", - "retention": "session", + "description": "OCD_RRetURL2_description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17049,8 +17924,8 @@ "category": "Functional", "name": "alohaEpt", "domain": "salesforce.com", - "description": "Used to log page load EPT (Experience Page Time) for Visualforce (Classic UI) pages.", - "retention": "90 sec", + "description": "OCD_alohaEpt_description", + "retention": "OCD_retention_90_sec", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17062,8 +17937,8 @@ "category": "Functional", "name": "clientSrc", "domain": "salesforce.com", - "description": "Used to validate the IP from where a user logs in.", - "retention": "session", + "description": "OCD_clientSrc_description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17075,8 +17950,8 @@ "category": "Marketing", "name": "oinfo", "domain": "salesforce.com", - "description": "Used to track the State, Edition and orgID of a customer's org.", - "retention": "3 months", + "description": "OCD_oinfo_description", + "retention": "OCD_retention_3_months", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17088,8 +17963,8 @@ "category": "Marketing", "name": "expid_*", "domain": "salesforce.com", - "description": "Used to render pages based on specified brand.", - "retention": "session", + "description": "OCD_expid___description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "1" @@ -17101,8 +17976,8 @@ "category": "Functional", "name": "oid", "domain": "salesforce.com", - "description": "Used to redirect a user to the correct Salesforce org and assist the user for the next login.", - "retention": "2 years", + "description": "OCD_oid_description", + "retention": "OCD_retention_2_years", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17114,8 +17989,8 @@ "category": "Functional", "name": "CookieConsentPolicy*", "domain": "salesforce.com", - "description": "Used to apply end-user cookie consent preferences set by our client-side utility.", - "retention": "1 year", + "description": "OCD_CookieConsentPolicy__description", + "retention": "OCD_retention_1_year", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "1" @@ -17127,8 +18002,8 @@ "category": "Marketing", "name": "_kuid_", "domain": "krxd.net (3rd party)", - "description": "Registers a unique ID that identifies a returning user's device. The ID is used for targeted ads.", - "retention": "6 months", + "description": "OCD__kuid__description", + "retention": "OCD_retention_6_months", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17140,8 +18015,8 @@ "category": "Marketing", "name": "visitor_id*", "domain": "", - "description": "The visitor cookie includes a unique visitor ID and the unique identifier for your account.", - "retention": "1 year", + "description": "OCD_visitor_id__description", + "retention": "OCD_retention_1_year", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "1" @@ -17153,8 +18028,8 @@ "category": "Functional", "name": "cookieSettingVerified", "domain": "", - "description": "\tUsed to create a popup message telling users that cookies are required.", - "retention": "session", + "description": "OCD_cookieSettingVerified_description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17166,8 +18041,8 @@ "category": "Functional", "name": "cordovaVersion", "domain": "", - "description": "Used for internal diagnostics with mobile applications.", - "retention": "session", + "description": "OCD_cordovaVersion_description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17179,8 +18054,8 @@ "category": "Functional", "name": "cqcid", "domain": "", - "description": "Used to track a guest shopper’s browsing activity.", - "retention": "1 year", + "description": "OCD_cqcid_description", + "retention": "OCD_retention_1_year", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17192,8 +18067,8 @@ "category": "Functional", "name": "csssid", "domain": "", - "description": "Used to establish a request context in the correct tenant org.", - "retention": "session", + "description": "OCD_csssid_description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17205,8 +18080,8 @@ "category": "Functional", "name": "csssid_Client", "domain": "", - "description": "Enables user switching.", - "retention": "session", + "description": "OCD_csssid_Client_description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17218,8 +18093,8 @@ "category": "Functional", "name": "devOverrideCsrfToken", "domain": "", - "description": "CSRF Token.", - "retention": "session", + "description": "OCD_devOverrideCsrfToken_description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17231,8 +18106,8 @@ "category": "Functional", "name": "disco", "domain": "", - "description": "Tracks the last user login and active session for bypassing login. For example, OAuth immediate flow.", - "retention": "session", + "description": "OCD_disco_description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17244,8 +18119,8 @@ "category": "Functional", "name": "FedAuth", "domain": "", - "description": "For the SharePoint connector, used to authenticate to the top-level site in SharePoint.", - "retention": "session", + "description": "OCD_FedAuth_description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17257,8 +18132,8 @@ "category": "Functional", "name": "gTalkCollapsed", "domain": "", - "description": "Controls whether the sidebar in Salesforce Classic is open for a user.", - "retention": "1 year", + "description": "OCD_gTalkCollapsed_description", + "retention": "OCD_retention_1_year", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17270,8 +18145,8 @@ "category": "Functional", "name": "guest_uuid_essential_*", "domain": "", - "description": "Provides a unique ID for guest users in Salesforce Sites. Expires 1 year after the user’s last visit to the site.", - "retention": "1 year", + "description": "OCD_guest_uuid_essential___description", + "retention": "OCD_retention_1_year", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "1" @@ -17283,8 +18158,8 @@ "category": "Functional", "name": "hideDevelopmentTools", "domain": "", - "description": "Used to determine whether to show the developer tools.", - "retention": "session", + "description": "OCD_hideDevelopmentTools_description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17296,8 +18171,8 @@ "category": "Functional", "name": "hideFilesWarningModal", "domain": "", - "description": "Stores the user acknowledgment that a public link to a Salesforce file is on email send. The warning window isn’t continually shown after the user acknowledges this action.", - "retention": "50 years", + "description": "OCD_hideFilesWarningModal_description", + "retention": "OCD_retention_50_years", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17309,8 +18184,8 @@ "category": "Functional", "name": "hideIdentityDialog", "domain": "", - "description": "Hides the dialog box that informs that the current user is logged out when switching to another user.", - "retention": "1 year", + "description": "OCD_hideIdentityDialog_description", + "retention": "OCD_retention_1_year", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17322,8 +18197,8 @@ "category": "Functional", "name": "Host-ERIC_PROD-", "domain": "", - "description": "Enterprise Request Infrastructure Cookie (ERIC) carries the cross-site request forgery (CSRF) security token between the server and the client. The cookie name indicates the server mode (PROD or PRODDEBUG) and a random number. A different token is generated for each Lightning app.", - "retention": "1 minute", + "description": "OCD_Host_ERIC_PROD__description", + "retention": "OCD_retention_1_minute", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17335,8 +18210,8 @@ "category": "Functional", "name": "ideaToggle", "domain": "", - "description": "Show the Ideas list view or the Feed list view.", - "retention": "session", + "description": "OCD_ideaToggle_description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17348,8 +18223,8 @@ "category": "Functional", "name": "inst", "domain": "", - "description": "Used to redirect requests to an instance when bookmarks and hardcoded URLs send requests to a different instance. This type of redirect can happen after an org migration, a split, or after any URL update.", - "retention": "session", + "description": "OCD_inst_description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17361,8 +18236,8 @@ "category": "Functional", "name": "iotcontextsplashdisable", "domain": "", - "description": "For the IoT product, stores user preference of whether to show Context Splash popup.", - "retention": "10 years", + "description": "OCD_iotcontextsplashdisable_description", + "retention": "OCD_retention_10_years", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17374,8 +18249,8 @@ "category": "Functional", "name": "lastlist", "domain": "", - "description": "Used to store the cookie name for the last list URL.", - "retention": "session", + "description": "OCD_lastlist_description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17387,8 +18262,8 @@ "category": "Functional", "name": "liveagent_invite_rejected_", "domain": "", - "description": "Instructs Live Agent not to reissue an invitation on the same domain. Deletion of this cookie degrades the customer’s experience because they can get repeated invitations.", - "retention": "session", + "description": "OCD_liveagent_invite_rejected__description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17400,8 +18275,8 @@ "category": "Functional", "name": "liveagent_sid", "domain": "", - "description": "Identifies a Live Agent session. Stores a unique pseudonymous ID for a specific browser session over chat service.", - "retention": "session", + "description": "OCD_liveagent_sid_description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17413,8 +18288,8 @@ "category": "Functional", "name": "lloopch_loid", "domain": "", - "description": "Determines whether to send the user to a specific portal login or an app login.", - "retention": "1 year", + "description": "OCD_lloopch_loid_description", + "retention": "OCD_retention_1_year", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17426,8 +18301,8 @@ "category": "Functional", "name": "login", "domain": "", - "description": "If the user’s session has expired, used to fetch the username and populate it on the main login page when using the process builder app.", - "retention": "60 Days", + "description": "OCD_login_description", + "retention": "OCD_retention_60_Days", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17439,8 +18314,8 @@ "category": "Functional", "name": "pc-unit", "domain": "", - "description": "Sets a preference for displaying platform cache units to either MB or KB.", - "retention": "1 year", + "description": "OCD_pc_unit_description", + "retention": "OCD_retention_1_year", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17452,8 +18327,8 @@ "category": "Functional", "name": "PreferredLanguage", "domain": "", - "description": "Stores the user language preference for language detection and localized user experience.", - "retention": "1 year", + "description": "OCD_PreferredLanguage_description", + "retention": "OCD_retention_1_year", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17465,8 +18340,8 @@ "category": "Functional", "name": "promptTestMod", "domain": "", - "description": "Stores whether test mode is in effect. This cookie is read-only.", - "retention": "30 days", + "description": "OCD_promptTestMod_description", + "retention": "OCD_retention_30_days", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17478,8 +18353,8 @@ "category": "Functional", "name": "redirectionWarning", "domain": "", - "description": "Enables the customer to store URLs that are exempt from setting a redirect warning interstitial page on an allowlist.", - "retention": "1 year", + "description": "OCD_redirectionWarning_description", + "retention": "OCD_retention_1_year", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17491,8 +18366,8 @@ "category": "Functional", "name": "schgtclose", "domain": "", - "description": "Deprecated feature, not used.", - "retention": "session", + "description": "OCD_schgtclose_description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17504,8 +18379,8 @@ "category": "Functional", "name": "sfdc_lv2", "domain": "", - "description": "Stores identity confirmation details for users. If the cookie isn’t set or it expires, users must repeat the identity confirmation process the next time that they log in. Identity confirmation requires a verification method such as SMS, an authenticator app, or a security key.", - "retention": "1 year", + "description": "OCD_sfdc_lv2_description", + "retention": "OCD_retention_1_year", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17517,8 +18392,8 @@ "category": "Functional", "name": "showNewBuilderWarningMessage", "domain": "", - "description": "Used to show or hide a warning message for the new dashboard builder.", - "retention": "100 years", + "description": "OCD_showNewBuilderWarningMessage_description", + "retention": "OCD_retention_100_years", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17530,8 +18405,8 @@ "category": "Functional", "name": "sidebarPinned", "domain": "", - "description": "Controls the state of the Salesforce Classic sidebar.", - "retention": "10 years", + "description": "OCD_sidebarPinned_description", + "retention": "OCD_retention_10_years", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17543,8 +18418,8 @@ "category": "Functional", "name": "ssostartpage", "domain": "", - "description": "Identifies the Identity Provider (IdP) location for single sign-on (SSO). Certain service provider initiated SSO requests can fail without this cookie.", - "retention": "1 year", + "description": "OCD_ssostartpage_description", + "retention": "OCD_retention_1_year", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17556,8 +18431,8 @@ "category": "Functional", "name": "SUCSP", "domain": "", - "description": "Used when the user identity that an administrator is assuming, via Log In as Another User, is a Customer Success Portal (CSP) user.", - "retention": "session", + "description": "OCD_SUCSP_description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17569,8 +18444,8 @@ "category": "Functional", "name": "SUPRM", "domain": "", - "description": "Used when the user identity that an administrator is assuming, via Log In as Another User, is a Partner Relationship Management (PRM) portal user.", - "retention": "session", + "description": "OCD_SUPRM_description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17582,8 +18457,8 @@ "category": "Functional", "name": "t", "domain": "", - "description": "Used to avoid duplicate access checks.", - "retention": "session", + "description": "OCD_t_description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17595,8 +18470,8 @@ "category": "Functional", "name": "useStandbyUrl", "domain": "", - "description": "Controls how quickly to set the standby URL when loading the softphone.", - "retention": "session", + "description": "OCD_useStandbyUrl_description", + "retention": "OCD_retention_session", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17608,8 +18483,8 @@ "category": "Functional", "name": "waveUserPrefFinderLeftNav", "domain": "", - "description": "Preference for left navigation UI in CRM Analytics.", - "retention": "100 years", + "description": "OCD_waveUserPrefFinderLeftNav_description", + "retention": "OCD_retention_100_years", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17621,8 +18496,8 @@ "category": "Functional", "name": "waveUserPrefFinderListView", "domain": "", - "description": "Preference for displaying list views in CRM Analytics.", - "retention": "100 years", + "description": "OCD_waveUserPrefFinderListView_description", + "retention": "OCD_retention_100_years", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17634,8 +18509,8 @@ "category": "Functional", "name": "webact", "domain": "", - "description": "Used to collect metrics per page view for personalization.", - "retention": "1 year", + "description": "OCD_webact_description", + "retention": "OCD_retention_1_year", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17647,8 +18522,8 @@ "category": "Functional", "name": "WelcomePanel", "domain": "", - "description": "Stores Salesforce preferences.", - "retention": "1 day", + "description": "OCD_WelcomePanel_description", + "retention": "OCD_retention_1_day", "dataController": "Salesforce", "gdprUrl": "https://www.salesforce.com/company/privacy/", "wildcard": "0" @@ -17660,8 +18535,8 @@ "category": "Functional", "name": "cookieyesID", "domain": "", - "description": "CookieYes sets this cookie as a unique identifier for visitors according to their consent.", - "retention": "1 year", + "description": "OCD_cookieyesID_description", + "retention": "OCD_retention_1_year", "dataController": "CookieYes", "gdprUrl": "https://www.cookieyes.com/cookie-policy/", "wildcard": "0" @@ -17673,8 +18548,8 @@ "category": "Functional", "name": "cky-consent", "domain": "", - "description": "The cookie is set by CookieYes to remember the users' consent settings so that the website recognizes the users the next time they visit.", - "retention": "1 year", + "description": "OCD_cky_consent_description", + "retention": "OCD_retention_1_year", "dataController": "CookieYes", "gdprUrl": "https://www.cookieyes.com/cookie-policy/", "wildcard": "0" @@ -17686,8 +18561,8 @@ "category": "Functional", "name": "cookieyes-necessary", "domain": "", - "description": "CookieYes sets this cookie to remember the consent of users for the use of cookies in the 'Necessary' category.", - "retention": "1 year", + "description": "OCD_cookieyes_necessary_description", + "retention": "OCD_retention_1_year", "dataController": "CookieYes", "gdprUrl": "https://www.cookieyes.com/cookie-policy/", "wildcard": "0" @@ -17699,8 +18574,8 @@ "category": "Functional", "name": "cookieyes-functional", "domain": "", - "description": "CookieYes sets this cookie to remember the consent of users for the use of cookies in the 'Functional' category.", - "retention": "1 year", + "description": "OCD_cookieyes_functional_description", + "retention": "OCD_retention_1_year", "dataController": "CookieYes", "gdprUrl": "https://www.cookieyes.com/cookie-policy/", "wildcard": "0" @@ -17712,8 +18587,8 @@ "category": "Functional", "name": "cookieyes-analytics", "domain": "", - "description": "CookieYes sets this cookie to remember the consent of users for the use of cookies in the 'Analytics' category.", - "retention": "1 year", + "description": "OCD_cookieyes_analytics_description", + "retention": "OCD_retention_1_year", "dataController": "CookieYes", "gdprUrl": "https://www.cookieyes.com/cookie-policy/", "wildcard": "0" @@ -17725,8 +18600,8 @@ "category": "Functional", "name": "cookieyes-advertisement", "domain": "", - "description": "CookieYes sets this cookie to remember the consent of users for the use of cookies in the 'Advertisement' category.", - "retention": "1 year", + "description": "OCD_cookieyes_advertisement_description", + "retention": "OCD_retention_1_year", "dataController": "CookieYes", "gdprUrl": "https://www.cookieyes.com/cookie-policy/", "wildcard": "0" @@ -17738,8 +18613,8 @@ "category": "Functional", "name": "cookieyes_privacy_policy_generator_session", "domain": "", - "description": "CookieYes sets this cookie to identify a session instance for a user.", - "retention": "2 hours", + "description": "OCD_cookieyes_privacy_policy_generator_session_description", + "retention": "OCD_retention_2_hours", "dataController": "CookieYes", "gdprUrl": "https://www.cookieyes.com/cookie-policy/", "wildcard": "0" @@ -17751,8 +18626,8 @@ "category": "Functional", "name": "cookieyes_session", "domain": "", - "description": "CookieYes sets this cookie to identify a session instance for a user.", - "retention": "2 hours", + "description": "OCD_cookieyes_session_description", + "retention": "OCD_retention_2_hours", "dataController": "CookieYes", "gdprUrl": "https://www.cookieyes.com/cookie-policy/", "wildcard": "0" @@ -17764,8 +18639,8 @@ "category": "Functional", "name": "cky-action", "domain": "", - "description": "This cookie is set by CookieYes and is used to remember the action taken by the user.", - "retention": "1 year", + "description": "OCD_cky_action_description", + "retention": "OCD_retention_1_year", "dataController": "CookieYes", "gdprUrl": "https://www.cookieyes.com/cookie-policy/", "wildcard": "0" @@ -17777,8 +18652,8 @@ "category": "Functional", "name": "cookieyes-performance", "domain": "", - "description": "CookieYes sets this cookie to remember the user's consent for cookies in the 'Performance' category.", - "retention": "1 year", + "description": "OCD_cookieyes_performance_description", + "retention": "OCD_retention_1_year", "dataController": "CookieYes", "gdprUrl": "https://www.cookieyes.com/cookie-policy/", "wildcard": "0" @@ -17790,8 +18665,8 @@ "category": "Functional", "name": "cookieyes-consent", "domain": "", - "description": "CookieYes sets this cookie to remember user's consent preferences so that their preferences are respected on their subsequent visits to this site. It does not collect or store any personal information of the site visitors.", - "retention": "1 year", + "description": "OCD_cookieyes_consent_description", + "retention": "OCD_retention_1_year", "dataController": "CookieYes", "gdprUrl": "https://www.cookieyes.com/privacy-policy/", "wildcard": "0" @@ -17803,8 +18678,8 @@ "category": "Marketing", "name": "tv_U*", "domain": "tremorhub.com", - "description": "Collects information on user behaviour on multiple websites. This information is used in order to optimize the relevance of advertisement on the website.", - "retention": "30 days", + "description": "OCD_tv_U__description", + "retention": "OCD_retention_30_days", "dataController": "Magnite", "gdprUrl": "https://www.magnite.com/legal/advertising-platform-privacy-policy/", "wildcard": "1" @@ -17816,8 +18691,8 @@ "category": "Marketing", "name": "tvid", "domain": "tremorhub.com", - "description": "Presents the user with relevant content and advertisement. The service is provided by third-party advertisement hubs, which facilitate real-time bidding for advertisers.", - "retention": "1 year", + "description": "OCD_tvid_description", + "retention": "OCD_retention_1_year", "dataController": "Magnite", "gdprUrl": "https://www.magnite.com/legal/advertising-platform-privacy-policy/", "wildcard": "0" @@ -17829,8 +18704,8 @@ "category": "Functional", "name": "JSESSIONID*", "domain": "", - "description": "JSESSIONID is a cookie generated by Servlet containers and used for session management in J2EE web applications for HTTP protocol. If a Web server is using a cookie for session management, it creates and sends JSESSIONID cookie to the client and then the client sends it back to the server in subsequent HTTP requests. JSESSIONID is a platform session cookie and is used by sites with JavaServer Pages (JSP). The cookie is used to maintain an anonymous user session by the server.", - "retention": "session", + "description": "OCD_JSESSIONID__description", + "retention": "OCD_retention_session", "dataController": "J2EE", "gdprUrl": "", "wildcard": "1" @@ -17842,8 +18717,8 @@ "category": "Functional", "name": "SSLB", "domain": "", - "description": "Indicates to a downstream load balancer that subsequent requests by a user should be routed to or away from SiteSpect, depending on the cookie's value.", - "retention": "2 years", + "description": "OCD_SSLB_description", + "retention": "OCD_retention_2_years", "dataController": "SiteSpect", "gdprUrl": "https://www.sitespect.com/privacy-overview/", "wildcard": "0" @@ -17855,8 +18730,8 @@ "category": "Functional", "name": "SSPV", "domain": "", - "description": "Used by the Preview feature and used when the Logging Level field on the Logging & Performance tab for the Domain is set to Debug.", - "retention": "1 year", + "description": "OCD_SSPV_description", + "retention": "OCD_retention_1_year", "dataController": "SiteSpect", "gdprUrl": "https://www.sitespect.com/privacy-overview/", "wildcard": "0" @@ -17868,8 +18743,8 @@ "category": "Functional", "name": "SSRT", "domain": "", - "description": "Stores the date and time of the user's last request to determine if the visit has timed out. ", - "retention": "1 year", + "description": "OCD_SSRT_description", + "retention": "OCD_retention_1_year", "dataController": "SiteSpect", "gdprUrl": "https://www.sitespect.com/privacy-overview/", "wildcard": "0" @@ -17881,8 +18756,8 @@ "category": "Functional", "name": "SSSC", "domain": "", - "description": "A session-only cookie used to send the user's Campaign assignment information to the backend webserver.", - "retention": "1 year", + "description": "OCD_SSSC_description", + "retention": "OCD_retention_1_year", "dataController": "SiteSpect", "gdprUrl": "https://www.sitespect.com/privacy-overview/", "wildcard": "0" @@ -17894,8 +18769,8 @@ "category": "Functional", "name": "ep201", "domain": "", - "description": "Load balancing site traffic and preventing site abuse", - "retention": "30 minutes", + "description": "OCD_ep201_description", + "retention": "OCD_retention_30_minutes", "dataController": "SurveyMonkey", "gdprUrl": "https://www.surveymonkey.com/mp/legal/privacy/", "wildcard": "0" @@ -17907,8 +18782,8 @@ "category": "Functional", "name": "ep202", "domain": "", - "description": "Signup source attribution, event stitching, and assigning visitors to experiments", - "retention": "1 year", + "description": "OCD_ep202_description", + "retention": "OCD_retention_1_year", "dataController": "SurveyMonkey", "gdprUrl": "https://www.surveymonkey.com/mp/legal/privacy/", "wildcard": "0" @@ -17920,8 +18795,8 @@ "category": "Marketing", "name": "usbls", "domain": "", - "description": "Usabilla uses this cookie for campaigns targeted to visitors new or returning to the site. This cookie is used to track which category applies to users and to then show the campaign to the right users.", - "retention": "session", + "description": "OCD_usbls_description", + "retention": "OCD_retention_session", "dataController": "SurveyMonkey", "gdprUrl": "https://www.surveymonkey.com/mp/legal/privacy/", "wildcard": "0" @@ -17933,8 +18808,8 @@ "category": "Functional", "name": "xf_consent", "domain": "", - "description": "This cookie is used to store a user's cookie consent preferences.", - "retention": "1 year", + "description": "OCD_xf_consent_description", + "retention": "OCD_retention_1_year", "dataController": "Xenforo", "gdprUrl": "https://xenforo.com/privacy-policy/", "wildcard": "0" @@ -17946,8 +18821,8 @@ "category": "Functional", "name": "xf_csrf", "domain": "", - "description": "This cookie is used to store a user's cross-site request forgery token, preventing other applications from making malicious requests on the user's behalf.", - "retention": "1 year", + "description": "OCD_xf_csrf_description", + "retention": "OCD_retention_1_year", "dataController": "Xenforo", "gdprUrl": "https://xenforo.com/privacy-policy/", "wildcard": "0" @@ -17959,8 +18834,8 @@ "category": "Functional", "name": "xf_dbWriteForced", "domain": "", - "description": "This cookie is used to indicate that the request should be completed using the database write server.", - "retention": "1 year", + "description": "OCD_xf_dbWriteForced_description", + "retention": "OCD_retention_1_year", "dataController": "Xenforo", "gdprUrl": "https://xenforo.com/privacy-policy/", "wildcard": "0" @@ -17972,8 +18847,8 @@ "category": "Functional", "name": "xf_inline_mod_*", "domain": "", - "description": "These cookies are used to store a user's currently selected inline moderation items.", - "retention": "1 year", + "description": "OCD_xf_inline_mod___description", + "retention": "OCD_retention_1_year", "dataController": "Xenforo", "gdprUrl": "https://xenforo.com/privacy-policy/", "wildcard": "1" @@ -17985,8 +18860,8 @@ "category": "Functional", "name": "xf_language_id", "domain": "", - "description": "This cookie is used to store a user's selected language.", - "retention": "1 year", + "description": "OCD_xf_language_id_description", + "retention": "OCD_retention_1_year", "dataController": "Xenforo", "gdprUrl": "https://xenforo.com/privacy-policy/", "wildcard": "0" @@ -17998,8 +18873,8 @@ "category": "Functional", "name": "xf_ls", "domain": "", - "description": "This cookie is used to store a user's local storage contents in the event their browser does not support the native local storage mechanism.", - "retention": "1 year", + "description": "OCD_xf_ls_description", + "retention": "OCD_retention_1_year", "dataController": "Xenforo", "gdprUrl": "https://xenforo.com/privacy-policy/", "wildcard": "0" @@ -18011,8 +18886,8 @@ "category": "Functional", "name": "xf_notice_dismiss", "domain": "", - "description": "This cookie is used to store a user's dismissed notices.", - "retention": "1 year", + "description": "OCD_xf_notice_dismiss_description", + "retention": "OCD_retention_1_year", "dataController": "Xenforo", "gdprUrl": "https://xenforo.com/privacy-policy/", "wildcard": "0" @@ -18024,8 +18899,8 @@ "category": "Functional", "name": "xf_push_notice_dismiss", "domain": "", - "description": "This cookie is used to determine whether or not a user has dismissed the push notification notice.", - "retention": "1 year", + "description": "OCD_xf_push_notice_dismiss_description", + "retention": "OCD_retention_1_year", "dataController": "Xenforo", "gdprUrl": "https://xenforo.com/privacy-policy/", "wildcard": "0" @@ -18037,8 +18912,8 @@ "category": "Functional", "name": "xf_push_subscription_updated", "domain": "", - "description": "This cookie is used to determine if a user's push subscription preferences have been updated.", - "retention": "1 year", + "description": "OCD_xf_push_subscription_updated_description", + "retention": "OCD_retention_1_year", "dataController": "Xenforo", "gdprUrl": "https://xenforo.com/privacy-policy/", "wildcard": "0" @@ -18050,8 +18925,8 @@ "category": "Functional", "name": "xf_session", "domain": "", - "description": "This cookie is used to store a user's session identifier.", - "retention": "session", + "description": "OCD_xf_session_description", + "retention": "OCD_retention_session", "dataController": "Xenforo", "gdprUrl": "https://xenforo.com/privacy-policy/", "wildcard": "0" @@ -18063,8 +18938,8 @@ "category": "Functional", "name": "xf_style_id", "domain": "", - "description": "This cookie is used to store a user's selected style.", - "retention": "1 year", + "description": "OCD_xf_style_id_description", + "retention": "OCD_retention_1_year", "dataController": "Xenforo", "gdprUrl": "https://xenforo.com/privacy-policy/", "wildcard": "0" @@ -18076,8 +18951,8 @@ "category": "Functional", "name": "xf_tfa_trust", "domain": "", - "description": "This cookie is used to determine if a user has previously chosen to trust this device without requiring further two-step verification for a period of time.", - "retention": "45 days", + "description": "OCD_xf_tfa_trust_description", + "retention": "OCD_retention_45_days", "dataController": "Xenforo", "gdprUrl": "https://xenforo.com/privacy-policy/", "wildcard": "0" @@ -18089,8 +18964,8 @@ "category": "Functional", "name": "xf_toggle", "domain": "", - "description": "This cookie and local storage item are used to store a user's preferences for toggling various controls open or closed.", - "retention": "1 year", + "description": "OCD_xf_toggle_description", + "retention": "OCD_retention_1_year", "dataController": "Xenforo", "gdprUrl": "https://xenforo.com/privacy-policy/", "wildcard": "0" @@ -18102,8 +18977,8 @@ "category": "Functional", "name": "xf_user", "domain": "", - "description": "This cookie is used to store a user's remember me token, allowing their credentials to persist across multiple sessions.", - "retention": "1 year", + "description": "OCD_xf_user_description", + "retention": "OCD_retention_1_year", "dataController": "Xenforo", "gdprUrl": "https://xenforo.com/privacy-policy/", "wildcard": "0" @@ -18115,8 +18990,8 @@ "category": "Functional", "name": "xf_emoji_usage", "domain": "", - "description": "This cookie is used to store which emojis a user has recently used when composing a message.", - "retention": "session", + "description": "OCD_xf_emoji_usage_description", + "retention": "OCD_retention_session", "dataController": "Xenforo", "gdprUrl": "https://xenforo.com/privacy-policy/", "wildcard": "0" @@ -18128,8 +19003,8 @@ "category": "Functional", "name": "xf_from_search", "domain": "", - "description": "This cookie is used to track when a user has arrived on the site from a search engine.", - "retention": "session", + "description": "OCD_xf_from_search_description", + "retention": "OCD_retention_session", "dataController": "Xenforo", "gdprUrl": "https://xenforo.com/privacy-policy/", "wildcard": "0" @@ -18141,8 +19016,8 @@ "category": "Marketing", "name": "sailthru_content", "domain": "", - "description": "Tracks recent pageviews for all visitors, and can be used to populate a new user profile.", - "retention": "1 year", + "description": "OCD_sailthru_content_description", + "retention": "OCD_retention_1_year", "dataController": "Sailthru", "gdprUrl": "https://www.sailthru.com/legal/privacy-statement/", "wildcard": "0" @@ -18154,8 +19029,8 @@ "category": "Marketing", "name": "sailthru_pageviews", "domain": "", - "description": "This cookie is set by Sailthru to tracks the number of page views for each user.", - "retention": "30 minutes", + "description": "OCD_sailthru_pageviews_description", + "retention": "OCD_retention_30_minutes", "dataController": "Sailthru", "gdprUrl": "https://www.sailthru.com/legal/privacy-statement/", "wildcard": "0" @@ -18167,8 +19042,8 @@ "category": "Marketing", "name": "sailthru_visitor", "domain": "", - "description": "This cookie is set by Sailthru. The cookie contains an id that is used to identify a user's pageviews within a session.", - "retention": "1 year", + "description": "OCD_sailthru_visitor_description", + "retention": "OCD_retention_1_year", "dataController": "Sailthru", "gdprUrl": "https://www.sailthru.com/legal/privacy-statement/", "wildcard": "0" @@ -18180,8 +19055,8 @@ "category": "Marketing", "name": "IMRID", "domain": "imrworldwide.com", - "description": "This domain is owned by Nielsen. The main business activity is: Consumer Profiling for Online Advertising", - "retention": "390 days", + "description": "OCD_IMRID_description", + "retention": "OCD_retention_390_days", "dataController": "Nielsen", "gdprUrl": "https://www.nielsen.com/legal/privacy-principles/", "wildcard": "0" @@ -18193,8 +19068,8 @@ "category": "Marketing", "name": "ud", "domain": "exelator.com", - "description": "Collects data related to the user’s visits to the website, such as the number of visits, average time spent on the website and what pages have been loaded, with the purpose of displaying targeted ads.", - "retention": "119 days", + "description": "OCD_ud_description", + "retention": "OCD_retention_119_days", "dataController": "Nielsen", "gdprUrl": "https://www.nielsen.com/legal/privacy-principles/", "wildcard": "0" @@ -18206,8 +19081,8 @@ "category": "Marketing", "name": "udo", "domain": "exelator.com", - "description": "Collects information on user behavior on multiple websites. This information is used in order to optimize the relevance of advertisement on the website.", - "retention": "119 days", + "description": "OCD_udo_description", + "retention": "OCD_retention_119_days", "dataController": "Nielsen", "gdprUrl": "https://www.nielsen.com/legal/privacy-principles/", "wildcard": "0" @@ -18219,8 +19094,8 @@ "category": "Marketing", "name": "EE", "domain": "exelator.com", - "description": "Collects data related to the user’s visits to the website, such as the number of visits, average time spent on the website and what pages have been loaded, with the purpose of displaying targeted ads.", - "retention": "119 days", + "description": "OCD_EE_description", + "retention": "OCD_retention_119_days", "dataController": "Nielsen", "gdprUrl": "https://www.nielsen.com/legal/privacy-principles/", "wildcard": "0" @@ -18232,8 +19107,8 @@ "category": "Marketing", "name": "i00", "domain": "ioam.de", - "description": "This cookie is used to share anonymous data about the use of online and mobile media players with the Broadcasters' Audience Research Board (BARB) to understand how many people watch online, and how much they watch.", - "retention": "1 year", + "description": "OCD_i00_description", + "retention": "OCD_retention_1_year", "dataController": "infOnline", "gdprUrl": "https://www.infonline.de/en/datenschutzerklaerung/", "wildcard": "0" @@ -18245,8 +19120,8 @@ "category": "Functional", "name": "CookieScriptConsent", "domain": "", - "description": "This cookie is used by Cookie-Script.com service to remember visitor cookie consent preferences. It is necessary for Cookie-Script.com cookie banner to work properly.", - "retention": "6 months", + "description": "OCD_CookieScriptConsent_description", + "retention": "OCD_retention_6_months", "dataController": "Cookie Script", "gdprUrl": "https://cookie-script.com/legal/privacy-policy", "wildcard": "0" @@ -18258,8 +19133,8 @@ "category": "Functional", "name": "rack.session", "domain": "", - "description": "Cookie generated by the Ruby Rack app. This is a general purpose identifier used to maintain user session variables.", - "retention": "session", + "description": "OCD_rack_session_description", + "retention": "OCD_retention_session", "dataController": "Rack", "gdprUrl": "", "wildcard": "0" @@ -18271,8 +19146,8 @@ "category": "Analytics", "name": "pa_user", "domain": "", - "description": "The pa_user cookie tracks an authenticated visitor (user) over time, even if the user does not log in again during subsequent visits. This cookie is managed by the customer who chooses its value and decides if the cookie should be set or not", - "retention": "13 months", + "description": "OCD_pa_user_description", + "retention": "OCD_retention_13_months", "dataController": "Piano", "gdprUrl": "https://piano.io/privacy-policy/", "wildcard": "0" @@ -18284,8 +19159,8 @@ "category": "Functional", "name": "xbc", "domain": "", - "description": "This cookie is used by Multiple Composer features, used for, metering, A/B testing, adblocker conversion tracking, credits, affiliates, first-visit segmentation, and AMP reader ID linking.", - "retention": "2 years", + "description": "OCD_xbc_description", + "retention": "OCD_retention_2_years", "dataController": "Piano", "gdprUrl": "https://piano.io/privacy-policy/", "wildcard": "0" @@ -18297,8 +19172,8 @@ "category": "Functional", "name": "__tbc", "domain": "", - "description": "This cookie is used for tracking conversion and external segmentation", - "retention": "2 years", + "description": "OCD___tbc_description", + "retention": "OCD_retention_2_years", "dataController": "Piano", "gdprUrl": "https://piano.io/privacy-policy/", "wildcard": "0" @@ -18310,8 +19185,8 @@ "category": "Functional", "name": "__pls", "domain": "", - "description": "This cookie is used to differentiate users has been subscribed to an ESP push list", - "retention": "2 years", + "description": "OCD___pls_description", + "retention": "OCD_retention_2_years", "dataController": "Piano", "gdprUrl": "https://piano.io/privacy-policy/", "wildcard": "0" @@ -18323,8 +19198,8 @@ "category": "Functional", "name": "__tac", "domain": "", - "description": "This cookie is used to check access via JWT won't work and the Composer Cookies stop working", - "retention": "90 days", + "description": "OCD___tac_description", + "retention": "OCD_retention_90_days", "dataController": "Piano", "gdprUrl": "https://piano.io/privacy-policy/", "wildcard": "0" @@ -18336,8 +19211,8 @@ "category": "Marketing", "name": "_pcus", "domain": "", - "description": "This cookie is used to User segmentation", - "retention": "2 years", + "description": "OCD__pcus_description", + "retention": "OCD_retention_2_years", "dataController": "Piano", "gdprUrl": "https://piano.io/privacy-policy/", "wildcard": "0" @@ -18349,8 +19224,8 @@ "category": "Analytics", "name": "cX_P", "domain": "", - "description": "This cookie contains the browserId that is used in Piano products for reporting and tracking purposes", - "retention": "13 months", + "description": "OCD_cX_P_description", + "retention": "OCD_retention_13_months", "dataController": "Piano", "gdprUrl": "https://piano.io/privacy-policy/", "wildcard": "0" @@ -18362,8 +19237,8 @@ "category": "Marketing", "name": "cX_G", "domain": "", - "description": "This cookie is a Global ID mapping different IDs together into one ID. Used for building user profile information across all sites of a single customer where cx.js is implemented", - "retention": "13 months", + "description": "OCD_cX_G_description", + "retention": "OCD_retention_13_months", "dataController": "Piano", "gdprUrl": "https://piano.io/privacy-policy/", "wildcard": "0" @@ -18375,8 +19250,8 @@ "category": "Analytics", "name": "pnespsdk_visitor", "domain": "", - "description": "This cookie is used for tracking user visits", - "retention": "12 months", + "description": "OCD_pnespsdk_visitor_description", + "retention": "OCD_retention_12_months", "dataController": "Piano", "gdprUrl": "https://piano.io/privacy-policy/", "wildcard": "0" @@ -18388,8 +19263,8 @@ "category": "Analytics", "name": "pnespsdk_push_subscription_added", "domain": "", - "description": "This cookie is used only in case the Push notifications feature in ESP is activated and allows correct tracking of Push notification subscription events", - "retention": "12 months", + "description": "OCD_pnespsdk_push_subscription_added_description", + "retention": "OCD_retention_12_months", "dataController": "Piano", "gdprUrl": "https://piano.io/privacy-policy/", "wildcard": "0" @@ -18401,8 +19276,8 @@ "category": "Marketing", "name": "pnespsdk_pnespid", "domain": "", - "description": "This cookie is used to connect a user visit coming from an email campaign click with a visitor on the website", - "retention": "12 months", + "description": "OCD_pnespsdk_pnespid_description", + "retention": "OCD_retention_12_months", "dataController": "Piano", "gdprUrl": "https://piano.io/privacy-policy/", "wildcard": "0" @@ -18414,8 +19289,8 @@ "category": "Functional", "name": "pnespsdk_ssn", "domain": "", - "description": "This session cookie is mandatory for the ESP service to be correctly running", - "retention": "session", + "description": "OCD_pnespsdk_ssn_description", + "retention": "OCD_retention_session", "dataController": "Piano", "gdprUrl": "https://piano.io/privacy-policy/", "wildcard": "0" @@ -18427,8 +19302,8 @@ "category": "Functional", "name": "__utp", "domain": "", - "description": "This cookie is used for logged-in user's session, and contains details of a logged-in user. By default, this cookie is set on the top-level domain", - "retention": "2 years", + "description": "OCD___utp_description", + "retention": "OCD_retention_2_years", "dataController": "Piano", "gdprUrl": "https://piano.io/privacy-policy/", "wildcard": "0" @@ -18440,8 +19315,8 @@ "category": "Functional", "name": "__pil", "domain": "", - "description": "This cookie is used to set the preferred language for the Piano templates. Value for example: de_DE. If not available, VX's LANG cookie is used.", - "retention": "30 days", + "description": "OCD___pil_description", + "retention": "OCD_retention_30_days", "dataController": "Piano", "gdprUrl": "https://piano.io/privacy-policy/", "wildcard": "0" @@ -18453,8 +19328,8 @@ "category": "Functional", "name": "__pid", "domain": "", - "description": "This cookie stores the domain received on the frontend is used as a domain for other cookies (incl. __utp, __idr, __tae) Example value: .piano.io", - "retention": "30 days", + "description": "OCD___pid_description", + "retention": "OCD_retention_30_days", "dataController": "Piano", "gdprUrl": "https://piano.io/privacy-policy/", "wildcard": "0" @@ -18466,8 +19341,8 @@ "category": "Functional", "name": "__idr", "domain": "", - "description": "The User Session Cookie is set when a user selects the option 'Stay logged in' when signing in. The expiration depends on the value configured in the Piano ID settings. Various browser restrictions and cookie rules affect the expiration as well.", - "retention": "30 days", + "description": "OCD___idr_description", + "retention": "OCD_retention_30_days", "dataController": "Piano", "gdprUrl": "https://piano.io/privacy-policy/", "wildcard": "0" @@ -18479,8 +19354,8 @@ "category": "Functional", "name": "__eea", "domain": "", - "description": "This cookie is used to determine if the user token (stored in __utp) needs to be refreshed with the new expiration automatically every 24 hours.", - "retention": "30 days", + "description": "OCD___eea_description", + "retention": "OCD_retention_30_days", "dataController": "Piano", "gdprUrl": "https://piano.io/privacy-policy/", "wildcard": "0" @@ -18492,8 +19367,8 @@ "category": "Functional", "name": "__code", "domain": "", - "description": "This cookie is used for ID OAuth authorization.", - "retention": "session", + "description": "OCD___code_description", + "retention": "OCD_retention_session", "dataController": "Piano", "gdprUrl": "https://piano.io/privacy-policy/", "wildcard": "0" @@ -18505,8 +19380,8 @@ "category": "Functional", "name": "__bid", "domain": "tinypass.com", - "description": "this cookie is used to Identifies the browser of the end user", - "retention": "1 year", + "description": "OCD___bid_description", + "retention": "OCD_retention_1_year", "dataController": "Piano", "gdprUrl": "https://piano.io/privacy-policy/", "wildcard": "0" @@ -18518,8 +19393,8 @@ "category": "Functional", "name": "__ut", "domain": "", - "description": "This cookie is used to Store on your website, the User Token Cookie stores encrypted data used by all Piano User Accounts", - "retention": "2 years", + "description": "OCD___ut_description", + "retention": "OCD_retention_2_years", "dataController": "Piano", "gdprUrl": "https://piano.io/privacy-policy/", "wildcard": "0" @@ -18531,8 +19406,8 @@ "category": "Analytics", "name": "__pvi", "domain": "", - "description": "This cookie stores data about the last visit to the site including the AID, lastTrackedVisitId, domain and time of the visit. Used for reporting only.", - "retention": "1 day", + "description": "OCD___pvi_description", + "retention": "OCD_retention_1_day", "dataController": "Piano", "gdprUrl": "https://piano.io/privacy-policy/", "wildcard": "0" @@ -18544,8 +19419,8 @@ "category": "Analytics", "name": "__pat", "domain": "", - "description": "This cookie stores difference between the client’s application time zone and UTC. At midnight, (application's local time), the previous visit is expired and a new one is created. The cookie is used for calculation.", - "retention": "30 days", + "description": "OCD___pat_description", + "retention": "OCD_retention_30_days", "dataController": "Piano", "gdprUrl": "https://piano.io/privacy-policy/", "wildcard": "0" @@ -18557,8 +19432,8 @@ "category": "Functional", "name": "__pnahc", "domain": "", - "description": "This cookie stores the result of previous Adblock detection, removes false-positive AdBlock detection clauses.", - "retention": "90 days", + "description": "OCD___pnahc_description", + "retention": "OCD_retention_90_days", "dataController": "Piano", "gdprUrl": "https://piano.io/privacy-policy/", "wildcard": "0" @@ -18570,8 +19445,8 @@ "category": "Functional", "name": "LANG", "domain": "tinypass.com", - "description": "This cookie stores the selected locale", - "retention": "1500 days", + "description": "OCD_LANG_description", + "retention": "OCD_retention_1500_days", "dataController": "Piano", "gdprUrl": "https://piano.io/privacy-policy/", "wildcard": "0" @@ -18583,8 +19458,8 @@ "category": "Functional", "name": "LANG_CHANGED", "domain": "tinypass.com", - "description": "This cookie stores the temporarily selected locale (e.g. impersonation in Admin dashboard).", - "retention": "1 day", + "description": "OCD_LANG_CHANGED_description", + "retention": "OCD_retention_1_day", "dataController": "Piano", "gdprUrl": "https://piano.io/privacy-policy/", "wildcard": "0" @@ -18596,8 +19471,8 @@ "category": "Functional", "name": "_pctx", "domain": "", - "description": "This cookie is required to sync different Piano product scripts containing common data points. It contains data from different products, for example for Composer Insights or Ad Revenue Insights, but only IF you have implemented any of these products.", - "retention": "13 months", + "description": "OCD__pctx_description", + "retention": "OCD_retention_13_months", "dataController": "Piano", "gdprUrl": "https://piano.io/privacy-policy/", "wildcard": "0" @@ -18609,8 +19484,8 @@ "category": "Functional", "name": "_pprv", "domain": "", - "description": "This cookie contains the property consent (linked to a product) the end-user has consented to. More information about Consent management can be found here.", - "retention": "13 months", + "description": "OCD__pprv_description", + "retention": "OCD_retention_13_months", "dataController": "Piano", "gdprUrl": "https://piano.io/privacy-policy/", "wildcard": "0" @@ -18622,8 +19497,8 @@ "category": "Functional", "name": "_pcid", "domain": "", - "description": "This cookie contains the browserId (BID) that is used in Piano products for reporting and tracking purposes.", - "retention": "13 months", + "description": "OCD__pcid_description", + "retention": "OCD_retention_13_months", "dataController": "Piano", "gdprUrl": "https://piano.io/privacy-policy/", "wildcard": "0" @@ -18635,8 +19510,8 @@ "category": "Analytics", "name": "DotMetrics.SessionCookieTemp", "domain": ".dotmetrics.net", - "description": "This cookie DotMetrics obtain information about a general site visit collect in the DotMetrics Research Network.", - "retention": "Session", + "description": "OCD_DotMetrics_SessionCookieTemp_description", + "retention": "OCD_retention_Session", "dataController": "Dot Metrics", "gdprUrl": "https://dotmetrics.net/privacy-center.html", "wildcard": "0" @@ -18648,8 +19523,8 @@ "category": "Analytics", "name": "DotMetrics.UniqueUserIdentityCookie", "domain": ".dotmetrics.net", - "description": "This cookie contains information about the current user (unique ID, creation time, current tracking mode and version)", - "retention": "Session", + "description": "OCD_DotMetrics_UniqueUserIdentityCookie_description", + "retention": "OCD_retention_Session", "dataController": "Dot Metrics", "gdprUrl": "https://dotmetrics.net/privacy-center.html", "wildcard": "0" @@ -18661,8 +19536,8 @@ "category": "Analytics", "name": "DotMetrics.DeviceKey", "domain": ".dotmetrics.net", - "description": "This cookie collects information about your device. The purpose for which we use it is to provide a high quality view of the survey or some content on your device.", - "retention": "Session", + "description": "OCD_DotMetrics_DeviceKey_description", + "retention": "OCD_retention_Session", "dataController": "Dot Metrics", "gdprUrl": "https://dotmetrics.net/privacy-center.html", "wildcard": "0" @@ -18674,8 +19549,8 @@ "category": "Analytics", "name": "DotMetrics.SessionCookieTempTimed", "domain": ".dotmetrics.net", - "description": "This cookie contains information about the current site from which you access the DotMetrics research network.", - "retention": "Session", + "description": "OCD_DotMetrics_SessionCookieTempTimed_description", + "retention": "OCD_retention_Session", "dataController": "Dot Metrics", "gdprUrl": "https://dotmetrics.net/privacy-center.html", "wildcard": "0" @@ -18687,8 +19562,8 @@ "category": "Marketing", "name": "ki_s", "domain": "qualaroo.com", - "description": "This cookie is used to store the current state of any survey the user has viewed or interacted with.", - "retention": "5 years", + "description": "OCD_ki_s_description", + "retention": "OCD_retention_5_years", "dataController": "Qualaroo", "gdprUrl": "https://www.proprofs.com/policies/privacy/", "wildcard": "0" @@ -18700,8 +19575,8 @@ "category": "Marketing", "name": "ki_u", "domain": "qualaroo.com", - "description": "This cookie is used to store a unique user identifier.", - "retention": "5 years", + "description": "OCD_ki_u_description", + "retention": "OCD_retention_5_years", "dataController": "Qualaroo", "gdprUrl": "https://www.proprofs.com/policies/privacy/", "wildcard": "0" @@ -18713,8 +19588,8 @@ "category": "Marketing", "name": "barometric[cuid]", "domain": "trkn.us", - "description": "This cookie is used to identify users for Veritone/Barometric Podcast Conversion.", - "retention": "1 year", + "description": "OCD_barometric_cuid__description", + "retention": "OCD_retention_1_year", "dataController": "Claritas", "gdprUrl": "https://claritas.com/privacy-legal/", "wildcard": "0" @@ -18726,8 +19601,8 @@ "category": "Marketing", "name": "barometric[idfa]", "domain": "trkn.us", - "description": "This cookie is used to to collect visitor statistics. This data is used to categorize users and improve the effectiveness of website advertising.", - "retention": "10 seconds", + "description": "OCD_barometric_idfa__description", + "retention": "OCD_retention_10_seconds", "dataController": "Claritas", "gdprUrl": "https://claritas.com/privacy-legal/", "wildcard": "0" @@ -18739,8 +19614,8 @@ "category": "Analytics", "name": "__gfp_64b", "domain": ".gemius.pl", - "description": "Stores data on the time spent on the website and its sub-pages, during the current session.", - "retention": "13 months", + "description": "OCD___gfp_64b_description", + "retention": "OCD_retention_13_months", "dataController": "Gemius", "gdprUrl": "https://www.gemius.pl/privacy-notice-for-panelists.html", "wildcard": "0" @@ -18752,8 +19627,8 @@ "category": "Analytics", "name": "__gfp_s_64b", "domain": ".gemius.pl", - "description": "Registers data on the performance of the website’s embedded video-content.", - "retention": "13 months", + "description": "OCD___gfp_s_64b_description", + "retention": "OCD_retention_13_months", "dataController": "Gemius", "gdprUrl": "https://www.gemius.pl/privacy-notice-for-panelists.html", "wildcard": "0" @@ -18765,8 +19640,8 @@ "category": "Analytics", "name": "Gdyn", "domain": ".gemius.pl", - "description": "\tCollects statistics on the visitor's visits to the website, such as the number of visits, average time spent on the website and what pages have been read.", - "retention": "13 months", + "description": "OCD_Gdyn_description", + "retention": "OCD_retention_13_months", "dataController": "Gemius", "gdprUrl": "https://www.gemius.pl/privacy-notice-for-panelists.html", "wildcard": "0" @@ -18778,8 +19653,8 @@ "category": "Marketing", "name": "opt_out", "domain": "postrelease.com", - "description": "This cookie is used to remember not to serve that user targeted Ads if they opt out.", - "retention": "1 year", + "description": "OCD_opt_out_description", + "retention": "OCD_retention_1_year", "dataController": "Nativo", "gdprUrl": "https://www.nativo.com/privacy-policy", "wildcard": "0" @@ -18791,8 +19666,8 @@ "category": "Marketing", "name": "visitor", "domain": "postrelease.com", - "description": "This cookie is used to identify a unique visitor to the site.", - "retention": "1 year", + "description": "OCD_visitor_description", + "retention": "OCD_retention_1_year", "dataController": "Nativo", "gdprUrl": "https://www.nativo.com/privacy-policy", "wildcard": "0" @@ -18804,8 +19679,8 @@ "category": "Marketing", "name": "tp", "domain": ".ml314.com", - "description": "This cookie is used to target the audience", - "retention": "14 days", + "description": "OCD_tp_description", + "retention": "OCD_retention_14_days", "dataController": "Bombora", "gdprUrl": "https://bombora.com/privacy-philosophy/", "wildcard": "0" @@ -18817,8 +19692,8 @@ "category": "Marketing", "name": "lkqdid", "domain": "lkqd.net", - "description": "This cookie is used to identify the physical location of mobile devices and operating system device identifiers.", - "retention": "1 year", + "description": "OCD_lkqdid_description", + "retention": "OCD_retention_1_year", "dataController": "Verve", "gdprUrl": "https://verve.com/website-privacy-policy/", "wildcard": "0" @@ -18830,8 +19705,8 @@ "category": "Marketing", "name": "lkqdidts", "domain": "lkqd.net", - "description": "This cookie is used to identify the physical location of mobile devices and operating system device.", - "retention": "1 year", + "description": "OCD_lkqdidts_description", + "retention": "OCD_retention_1_year", "dataController": "Verve", "gdprUrl": "https://verve.com/website-privacy-policy/", "wildcard": "0" @@ -18843,8 +19718,8 @@ "category": "Marketing", "name": "33x_ps", "domain": ".33across.com", - "description": "This cookie is used targeted and behavioural advertising services.", - "retention": "1 year", + "description": "OCD_33x_ps_description", + "retention": "OCD_retention_1_year", "dataController": "33Across", "gdprUrl": "https://33across.com/privacy-policy/", "wildcard": "0" @@ -18856,8 +19731,8 @@ "category": "Functional", "name": "COOKIELAW_ADS", "domain": "", - "description": "Keeps track of whether marketing cookies are allowed", - "retention": "1 year", + "description": "OCD_COOKIELAW_ADS_description", + "retention": "OCD_retention_1_year", "dataController": "Lightspeed", "gdprUrl": "https://www.lightspeedhq.com/legal/privacy-policy/", "wildcard": "0" @@ -18869,8 +19744,8 @@ "category": "Functional", "name": "COOKIELAW_SOCIAL", "domain": "", - "description": "Keeps track of whether social cookies are allowed", - "retention": "1 year", + "description": "OCD_COOKIELAW_SOCIAL_description", + "retention": "OCD_retention_1_year", "dataController": "Lightspeed", "gdprUrl": "https://www.lightspeedhq.com/legal/privacy-policy/", "wildcard": "0" @@ -18882,8 +19757,8 @@ "category": "Functional", "name": "COOKIELAW_STATS", "domain": "", - "description": "Keeps track of whether analytics cookies are allowed", - "retention": "1 year", + "description": "OCD_COOKIELAW_STATS_description", + "retention": "OCD_retention_1_year", "dataController": "Lightspeed", "gdprUrl": "https://www.lightspeedhq.com/legal/privacy-policy/", "wildcard": "0" @@ -18895,8 +19770,8 @@ "category": "Functional", "name": "COOKIELAW", "domain": "", - "description": "These cookies are used for platform stability and to store cookie preferences. They do not collect personally identifiable information and cannot be disabled.", - "retention": "1 year", + "description": "OCD_COOKIELAW_description", + "retention": "OCD_retention_1_year", "dataController": "Lightspeed", "gdprUrl": "https://www.lightspeedhq.com/legal/privacy-policy/", "wildcard": "0" @@ -18908,8 +19783,8 @@ "category": "Analytics", "name": "dm_timezone_offset", "domain": "", - "description": "Cookie used by the hosting provider (duda.co), the cookie is set in order to enable and measure personalization rules and statistics.", - "retention": "15 days", + "description": "OCD_dm_timezone_offset_description", + "retention": "OCD_retention_15_days", "dataController": "Duda", "gdprUrl": "https://www.duda.co/legal/privacy", "wildcard": "0" @@ -18921,8 +19796,8 @@ "category": "Analytics", "name": "dm_last_visit", "domain": "", - "description": "Cookie used by the hosting provider (duda.co), the cookie is set in order to enable and measure personalization rules and statistics.", - "retention": "1 year", + "description": "OCD_dm_last_visit_description", + "retention": "OCD_retention_1_year", "dataController": "Duda", "gdprUrl": "https://www.duda.co/legal/privacy", "wildcard": "0" @@ -18934,8 +19809,8 @@ "category": "Analytics", "name": "dm_total_visits", "domain": "", - "description": "Cookie used by the hosting provider (duda.co), the cookie is set in order to enable and measure personalization rules and statistics.", - "retention": "1 year", + "description": "OCD_dm_total_visits_description", + "retention": "OCD_retention_1_year", "dataController": "Duda", "gdprUrl": "https://www.duda.co/legal/privacy", "wildcard": "0" @@ -18947,8 +19822,8 @@ "category": "Analytics", "name": "dm_last_page_view", "domain": "", - "description": "Cookie used by the hosting provider (duda.co), the cookie is set in order to enable and measure personalization rules and statistics.", - "retention": "1 year", + "description": "OCD_dm_last_page_view_description", + "retention": "OCD_retention_1_year", "dataController": "Duda", "gdprUrl": "https://www.duda.co/legal/privacy", "wildcard": "0" @@ -18960,8 +19835,8 @@ "category": "Analytics", "name": "dm_this_page_view", "domain": "", - "description": "Cookie used by the hosting provider (duda.co), the cookie is set in order to enable and measure personalization rules and statistics.", - "retention": "1 year", + "description": "OCD_dm_this_page_view_description", + "retention": "OCD_retention_1_year", "dataController": "Duda", "gdprUrl": "https://www.duda.co/legal/privacy", "wildcard": "0" @@ -18973,8 +19848,8 @@ "category": "Functional", "name": "CookieControl", "domain": "", - "description": "This cookie is used to remember the user's cookie consent preferences.", - "retention": "3 months", + "description": "OCD_CookieControl_description", + "retention": "OCD_retention_3_months", "dataController": "Civic UK", "gdprUrl": "https://www.civicuk.com/privacy", "wildcard": "0" @@ -18986,8 +19861,8 @@ "category": "Analytics", "name": "intercom-id-*", "domain": "", - "description": "Anonymous visitor identifier cookie. As people visit your site they get this cookie.", - "retention": "9 months", + "description": "OCD_intercom_id___description", + "retention": "OCD_retention_9_months", "dataController": "Intercom", "gdprUrl": "https://www.intercom.com/legal/cookie-policy", "wildcard": "1" @@ -18999,8 +19874,8 @@ "category": "Analytics", "name": "intercom-session-*", "domain": "", - "description": "Identifier for each unique browser session. This session cookie is refreshed on each successful logged-in ping, extending it one week from that moment. The user can access their conversations and have data communicated on logged-out pages for 1 week, as long as the session isn't intentionally terminated with Intercom('shutdown');, which usually happens on logout.", - "retention": "1 week", + "description": "OCD_intercom_session___description", + "retention": "OCD_retention_1_week", "dataController": "Intercom", "gdprUrl": "https://www.intercom.com/legal/cookie-policy", "wildcard": "1" @@ -19012,8 +19887,8 @@ "category": "Analytics", "name": "intercom-device-id-*", "domain": "", - "description": "Identifier for each unique device that interacts with the Messenger. It is refreshed on each successful ping, extending it another 9 months. We use this cookie to determine the unique devices interacting with the Intercom Messenger to prevent abuse.", - "retention": "9 months", + "description": "OCD_intercom_device_id___description", + "retention": "OCD_retention_9_months", "dataController": "Intercom", "gdprUrl": "https://www.intercom.com/legal/cookie-policy", "wildcard": "1" @@ -19025,8 +19900,8 @@ "category": "Analytics", "name": "mp_*", "domain": "", - "description": "This cookie is used to store a user's unique identifier.", - "retention": "1 year", + "description": "OCD_mp___description", + "retention": "OCD_retention_1_year", "dataController": "Mixpanel", "gdprUrl": "https://mixpanel.com/legal/privacy-policy/", "wildcard": "1" @@ -19038,8 +19913,8 @@ "category": "Analytics", "name": "pvc_visits[0]", "domain": "", - "description": "It counts the number of visits to a post. The cookie is used to prevent repeat views of a post by a visitor.", - "retention": "1 day", + "description": "OCD_pvc_visits_0__description", + "retention": "OCD_retention_1_day", "dataController": "Postviewscounter", "gdprUrl": "https://postviewscounter.com/privacy-policy/", "wildcard": "0" @@ -19051,8 +19926,8 @@ "category": "Analytics", "name": "client_bslstaid", "domain": "", - "description": "Registers statistical data about the behavior of visitors to the website. Used for internal analysis by the website administrator.", - "retention": "540 days", + "description": "OCD_client_bslstaid_description", + "retention": "OCD_retention_540_days", "dataController": "Beslist.nl", "gdprUrl": "https://www.beslist.nl/information/overprivacy.html", "wildcard": "0" @@ -19064,8 +19939,8 @@ "category": "Analytics", "name": "client_bslstmatch", "domain": "", - "description": "Registers statistical data about the behavior of visitors to the website. Used for internal analysis by the website administrator.", - "retention": "1 day", + "description": "OCD_client_bslstmatch_description", + "retention": "OCD_retention_1_day", "dataController": "Beslist.nl", "gdprUrl": "https://www.beslist.nl/information/overprivacy.html", "wildcard": "0" @@ -19077,8 +19952,8 @@ "category": "Analytics", "name": "client_bslstsid", "domain": "", - "description": "Unique identifier of the user session.", - "retention": "1 day", + "description": "OCD_client_bslstsid_description", + "retention": "OCD_retention_1_day", "dataController": "Beslist.nl", "gdprUrl": "https://www.beslist.nl/information/overprivacy.html", "wildcard": "0" @@ -19090,8 +19965,8 @@ "category": "Analytics", "name": "client_bslstuid", "domain": "", - "description": "Registers statistical data about the behavior of visitors to the website. Used for internal analysis by the website administrator.", - "retention": "540 days", + "description": "OCD_client_bslstuid_description", + "retention": "OCD_retention_540_days", "dataController": "Beslist.nl", "gdprUrl": "https://www.beslist.nl/information/overprivacy.html", "wildcard": "0" @@ -19103,8 +19978,8 @@ "category": "Functional", "name": "CGISESSID", "domain": "", - "description": "Cookie generated by applications based on the Perl language. This is a general purpose identifier used to maintain user session variables.", - "retention": "session", + "description": "OCD_CGISESSID_description", + "retention": "OCD_retention_session", "dataController": "Perl", "gdprUrl": "https://www.perl.org/siteinfo.html", "wildcard": "0" @@ -19116,8 +19991,8 @@ "category": "Marketing", "name": "vglnk.Agent.p", "domain": "disqus.com", - "description": "Cookie set by Disqus. Used to collect visitor behaviour in order to present more relevant advertisements.", - "retention": "1 year", + "description": "OCD_vglnk_Agent_p_description", + "retention": "OCD_retention_1_year", "dataController": "Disqus", "gdprUrl": "https://help.disqus.com/en/articles/1717103-disqus-privacy-policy", "wildcard": "0" @@ -19129,8 +20004,8 @@ "category": "Marketing", "name": "vglnk.PartnerRfsh.p", "domain": "disqus.com", - "description": "This cookie is used to collect data from various website in order to present more relevant advertisement.", - "retention": "1 year", + "description": "OCD_vglnk_PartnerRfsh_p_description", + "retention": "OCD_retention_1_year", "dataController": "Disqus", "gdprUrl": "https://help.disqus.com/en/articles/1717103-disqus-privacy-policy", "wildcard": "0" @@ -19142,8 +20017,8 @@ "category": "Marketing", "name": "deuxesse_uxid", "domain": "twiago.com", - "description": "Sets a unique ID for the visitor, that allows third party advertisers to target the visitor with relevant advertisement. This pairing service is provided by third party advertisement hubs, which facilitates real-time bidding for advertisers.", - "retention": "29 days", + "description": "OCD_deuxesse_uxid_description", + "retention": "OCD_retention_29_days", "dataController": "Twiago", "gdprUrl": "https://www.twiago.com/datenschutz/", "wildcard": "0" @@ -19155,8 +20030,8 @@ "category": "Marketing", "name": "pid_short", "domain": "xplosion.de", - "description": "This cookie is used by Xplosion/emetriq. Used to analyze the behavior of visitors to the website and derive preferences. These allow for interest-based advertising on third party websites.", - "retention": "1 year", + "description": "OCD_pid_short_description", + "retention": "OCD_retention_1_year", "dataController": "Emetric", "gdprUrl": "https://www.emetriq.com/datenschutz/", "wildcard": "0" @@ -19168,8 +20043,8 @@ "category": "Marketing", "name": "pid_signature", "domain": "xplosion.de", - "description": "This cookie is used by Xplosion/emetriq. Used to analyze the behavior of visitors to the website and derive preferences. These allow for interest-based advertising on third party websites.", - "retention": "1 year", + "description": "OCD_pid_signature_description", + "retention": "OCD_retention_1_year", "dataController": "Emetric", "gdprUrl": "https://www.emetriq.com/datenschutz/", "wildcard": "0" @@ -19181,8 +20056,8 @@ "category": "Marketing", "name": "ep", "domain": "xplosion.de", - "description": "This cookie Is used by Xplosion / emetriq. Used to analyze the behavior of visitors to the website and derive preferences. These allow for interest-based advertising on third party websites.", - "retention": "1 year", + "description": "OCD_ep_description", + "retention": "OCD_retention_1_year", "dataController": "Emetric", "gdprUrl": "https://www.emetriq.com/datenschutz/", "wildcard": "0" @@ -19194,8 +20069,8 @@ "category": "Marketing", "name": "_pangle", "domain": "analytics.pangle-ads.com", - "description": "This cookie is to measure and improve the performance of your advertising campaigns and to personalize the user's ad experiences delivered by the Pangle ad network.", - "retention": "13 months", + "description": "OCD__pangle_description", + "retention": "OCD_retention_13_months", "dataController": "Pangle", "gdprUrl": "https://www.pangleglobal.com/privacy", "wildcard": "0" @@ -19207,11 +20082,33 @@ "category": "Marketing", "name": "u", "domain": "t.tailtarget.com", - "description": "This cookie is Used for audience segmentation for advertising", + "description": "OCD_u_description", + "retention": "OCD_retention_1_year", + "dataController": "Totvs", + "gdprUrl": "https://www.totvs.com/protecao-e-privacidade-de-dados/", + "wildcard": "0" + }, + { + "platform": "Tailtarget", + "category": "Marketing", + "name": "u", + "domain": ".t.tailtarget.com", + "description": "This cookie is used for user segmentation identifying the user uniquely", "retention": "1 year", "dataController": "Totvs", "gdprUrl": "https://www.totvs.com/protecao-e-privacidade-de-dados/", "wildcard": "0" + }, + { + "platform": "CreativeCDN", + "category": "Marketing", + "name": "u", + "domain": "creativecdn.com", + "description": "This cookie is associated with creativecdn.com. It collects unidentifiable data that is sent to an unidentifiable source. The source's identity is kept secret by the company, Perfect Privacy LLC.", + "retention": "365 days", + "dataController": "RTB House", + "gdprUrl": "https://rtbhouse.com/privacy-center", + "wildcard": "0" } ], "_ssc": [ @@ -19220,21 +20117,32 @@ "category": "Marketing", "name": "_ssc", "domain": "t.tailtarget.com", - "description": "This is a cookie used for generating access and internet traffic statistics.", - "retention": "1 day", + "description": "OCD__ssc_description", + "retention": "OCD_retention_1_day", "dataController": "Totvs", "gdprUrl": "https://www.totvs.com/protecao-e-privacidade-de-dados/", "wildcard": "0" - } - ], - "NSC_*": [ + }, { - "platform": "Citrix", - "category": "Functional", - "name": "NSC_*", + "platform": "Tailtarget", + "category": "Marketing", + "name": "_ssc", + "domain": ".t.tailtarget.com", + "description": "This cookie is used for indicate user access to the same site", + "retention": "2 days", + "dataController": "Totvs", + "gdprUrl": "https://www.totvs.com/protecao-e-privacidade-de-dados/", + "wildcard": "0" + } + ], + "NSC_*": [ + { + "platform": "Citrix", + "category": "Functional", + "name": "NSC_*", "domain": "", - "description": "This cookie name is associated with the Netscaler load balancing service from Citrix. This is a pattern type cookie with the root being NSC_ and the rest of the name being a unique encrypted alpha numeric identifier for the virtual server it originated from. The cookie is used to ensure traffic and user data is routed to the correct locations where a site is hosted on multiple servers, so that the end user has a consistent experience.", - "retention": "12 hours", + "description": "OCD_NSC___description", + "retention": "OCD_retention_12_hours", "dataController": "Citrix", "gdprUrl": "https://www.citrix.com/about/trust-center/privacy-compliance/", "wildcard": "1" @@ -19246,8 +20154,8 @@ "category": "Marketing", "name": "bitoIsSecure", "domain": "bidr.io", - "description": "This cookie is associated with bidr.io. It allows third party advertisers to target the visitor with relevant advertising. This pairing service is provided by third party advertisement hubs, which facilitate real-time bidding for advertisers.", - "retention": "1 year", + "description": "OCD_bitoIsSecure_description", + "retention": "OCD_retention_1_year", "dataController": "Beeswax", "gdprUrl": "https://www.beeswax.com/privacy/", "wildcard": "0" @@ -19259,8 +20167,8 @@ "category": "Marketing", "name": "bito", "domain": "bidr.io", - "description": "This cookie is generally provided by bidr.io and is used for advertising purposes.", - "retention": "1 year", + "description": "OCD_bito_description", + "retention": "OCD_retention_1_year", "dataController": "Beeswax", "gdprUrl": "https://www.beeswax.com/privacy/", "wildcard": "0" @@ -19272,8 +20180,8 @@ "category": "Analytics", "name": "WMF-Last-Access", "domain": ".wikimedia.org", - "description": "This cookie is used by the Wikimedia Foundation. It is used to determine the last time a user visited a page, and is used for various statistics.", - "retention": "session", + "description": "OCD_WMF_Last_Access_description", + "retention": "OCD_retention_session", "dataController": "Wikipedia", "gdprUrl": "https://foundation.wikimedia.org/wiki/Policy:Privacy_policy", "wildcard": "0" @@ -19285,8 +20193,8 @@ "category": "Functional", "name": "loginnotify_prevlogins", "domain": ".wikimedia.org", - "description": "This cookie verifies that you are logging in from a known device. This affects the threshold for how many unsuccessful login attempts trigger a notification to the user..", - "retention": "180 days", + "description": "OCD_loginnotify_prevlogins_description", + "retention": "OCD_retention_180_days", "dataController": "Wikipedia", "gdprUrl": "https://foundation.wikimedia.org/wiki/Policy:Privacy_policy", "wildcard": "0" @@ -19298,8 +20206,8 @@ "category": "Functional", "name": "stopMobileRedirect", "domain": ".wikimedia.org", - "description": "This cookie tells us not to redirect to the mobile site if you do not like that..", - "retention": "30 days", + "description": "OCD_stopMobileRedirect_description", + "retention": "OCD_retention_30_days", "dataController": "Wikipedia", "gdprUrl": "https://foundation.wikimedia.org/wiki/Policy:Privacy_policy", "wildcard": "0" @@ -19311,8 +20219,8 @@ "category": "Analytics", "name": "centralnotice_bucket", "domain": ".wikimedia.org", - "description": "This cookie helps us understand the effectiveness of notices provided to users through the CentralNotice extension..", - "retention": "session", + "description": "OCD_centralnotice_bucket_description", + "retention": "OCD_retention_session", "dataController": "Wikipedia", "gdprUrl": "https://foundation.wikimedia.org/wiki/Policy:Privacy_policy", "wildcard": "0" @@ -19324,8 +20232,8 @@ "category": "Analytics", "name": "GeoIP", "domain": ".wikimedia.org", - "description": "This cookie is used to try and understand the user's geographical location (country) based on their IP address.", - "retention": "session", + "description": "OCD_GeoIP_description", + "retention": "OCD_retention_session", "dataController": "Wikipedia", "gdprUrl": "https://foundation.wikimedia.org/wiki/Policy:Privacy_policy", "wildcard": "0" @@ -19337,8 +20245,8 @@ "category": "Analytics", "name": "NetWorkProbeLimit", "domain": ".wikimedia.org", - "description": "This cookie is used to set NetworkProbeLimit cookie to override the default network probe limit value.", - "retention": "1 hour", + "description": "OCD_NetWorkProbeLimit_description", + "retention": "OCD_retention_1_hour", "dataController": "Wikipedia", "gdprUrl": "https://foundation.wikimedia.org/wiki/Policy:Privacy_policy", "wildcard": "0" @@ -19350,8 +20258,8 @@ "category": "Marketing", "name": "auid", "domain": ".acuityplatform.com", - "description": "This cookie is used to identify the visitor and cookie-tracking solutions and marketing and advertising services..", - "retention": "1 year", + "description": "OCD_auid_description", + "retention": "OCD_retention_1_year", "dataController": "Acuity", "gdprUrl": "", "wildcard": "0" @@ -19363,8 +20271,8 @@ "category": "Marketing", "name": "aum", "domain": ".acuityplatform.com", - "description": "This cookie is used to identify the visitor and the company provides a range of cookie-tracking solutions and marketing and advertising services.", - "retention": "1 year", + "description": "OCD_aum_description", + "retention": "OCD_retention_1_year", "dataController": "Acuity", "gdprUrl": "", "wildcard": "0" @@ -19376,8 +20284,8 @@ "category": "Analytics", "name": "ablyft_exps", "domain": "", - "description": "Is set and updated when a visitor is bucketed into an experiment/variation.", - "retention": "1 year", + "description": "OCD_ablyft_exps_description", + "retention": "OCD_retention_1_year", "dataController": "ABlyft", "gdprUrl": "https://ablyft.com/en/privacy-notice", "wildcard": "0" @@ -19389,8 +20297,8 @@ "category": "Analytics", "name": "ablyft_queue", "domain": "", - "description": "Is set when a visitor triggers an event/goal. After sending the event to ABlyft it is cleared.", - "retention": "1 year", + "description": "OCD_ablyft_queue_description", + "retention": "OCD_retention_1_year", "dataController": "ABlyft", "gdprUrl": "https://ablyft.com/en/privacy-notice", "wildcard": "0" @@ -19402,8 +20310,8 @@ "category": "Analytics", "name": "ablyft_uvs", "domain": "", - "description": "Is set on the first pageview and update with every further pageview of a visitor.", - "retention": "1 year", + "description": "OCD_ablyft_uvs_description", + "retention": "OCD_retention_1_year", "dataController": "ABlyft", "gdprUrl": "https://ablyft.com/en/privacy-notice", "wildcard": "0" @@ -19415,8 +20323,8 @@ "category": "Analytics", "name": "ablyft_tracking_consent", "domain": "", - "description": "Is set when enableTrackingConsent or disableTrackingConsent is triggered via API.", - "retention": "1 year", + "description": "OCD_ablyft_tracking_consent_description", + "retention": "OCD_retention_1_year", "dataController": "ABlyft", "gdprUrl": "https://ablyft.com/en/privacy-notice", "wildcard": "0" @@ -19428,8 +20336,8 @@ "category": "Marketing", "name": "_d2id", "domain": ".mercadolibre.com", - "description": "This cookie is required for shopping cart functionality on the website.", - "retention": "1 year", + "description": "OCD__d2id_description", + "retention": "OCD_retention_1_year", "dataController": "MercadoLibre", "gdprUrl": "https://www.mercadolibre.com.ar/privacidad", "wildcard": "0" @@ -19441,8 +20349,8 @@ "category": "Marketing", "name": "edsid", "domain": ".mercadolibre.com", - "description": "This cookie is used to identify users to implement fraud prevention", - "retention": "1 year", + "description": "OCD_edsid_description", + "retention": "OCD_retention_1_year", "dataController": "MercadoLibre", "gdprUrl": "https://www.mercadolibre.com.ar/privacidad", "wildcard": "" @@ -19454,8 +20362,8 @@ "category": "Marketing", "name": "ftid", "domain": ".mercadolibre.com", - "description": "This cookie is used to identify users to implement fraud prevention", - "retention": "1 year", + "description": "OCD_ftid_description", + "retention": "OCD_retention_1_year", "dataController": "MercadoLibre", "gdprUrl": "https://www.mercadolibre.com.ar/privacidad", "wildcard": "0" @@ -19467,8 +20375,8 @@ "category": "Marketing", "name": "aniC", "domain": ".aniview.com", - "description": "This cookie is used in context with video-advertisement. The cookie limits the number of times a user is shown the same advertisement. The cookie is also used to ensure relevance of the video-advertisement to the specific user.", - "retention": "20 Days", + "description": "OCD_aniC_description", + "retention": "OCD_retention_20_Days", "dataController": "Aniview", "gdprUrl": "https://www.aniview.com/privacy-policy/", "wildcard": "0" @@ -19476,15 +20384,3954 @@ ], "version": [ { - "platform": "Anivew", + "platform": "Aniview", "category": "Marketing", "name": "version", "domain": "track1.aniview.com", - "description": "This cookie is used by the website's operator in context with multi-variate testing. This is a tool used to combine or change content on the website. This allows the website to find the best variation/edition of the site.", - "retention": "Session", + "description": "OCD_version_description", + "retention": "OCD_retention_Session", "dataController": "Aniview", "gdprUrl": "https://www.aniview.com/privacy-policy/", "wildcard": "0" } + ], + "mics_vid": [ + { + "platform": "Mediarithmics", + "category": "Marketing", + "name": "mics_vid", + "domain": ".mediarithmics.com", + "description": "OCD_mics_vid_description", + "retention": "OCD_retention_1_year", + "dataController": "Mediarithmics", + "gdprUrl": "https://developer.mediarithmics.io/advanced-usages/data-privacy-compliance/cookies", + "wildcard": "0" + } + ], + "mics_uaid": [ + { + "platform": "Mediarithmics", + "category": "Marketing", + "name": "mics_uaid", + "domain": ".mediarithmics.com", + "description": "OCD_mics_uaid_description", + "retention": "OCD_retention_1_year", + "dataController": "Mediarithmics", + "gdprUrl": "https://developer.mediarithmics.io/advanced-usages/data-privacy-compliance/cookies", + "wildcard": "0" + } + ], + "mics_lts": [ + { + "platform": "Mediarithmics", + "category": "Marketing", + "name": "mics_lts", + "domain": ".mediarithmics.com", + "description": "OCD_mics_lts_description", + "retention": "OCD_retention_1_year", + "dataController": "Mediarithmics", + "gdprUrl": "https://developer.mediarithmics.io/advanced-usages/data-privacy-compliance/cookies", + "wildcard": "0" + } + ], + "n360_thirtythreeacross": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_thirtythreeacross", + "domain": ".nexx360.io", + "description": "OCD_n360_thirtythreeacross_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-thirtythreeacross": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-thirtythreeacross", + "domain": ".nexx360.io", + "description": "OCD_n360_thirtythreeacross_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_appnexus": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_appnexus", + "domain": ".nexx360.io", + "description": "OCD_n360_appnexus_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-appnexus": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-appnexus", + "domain": ".nexx360.io", + "description": "OCD_n360_appnexus_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_bliink": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_bliink", + "domain": ".nexx360.io", + "description": "OCD_n360_bliink_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-bliink": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-bliink", + "domain": ".nexx360.io", + "description": "OCD_n360_bliink_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_amx": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_amx", + "domain": ".nexx360.io", + "description": "OCD_n360_amx_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-amx": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-amx", + "domain": ".nexx360.io", + "description": "OCD_n360_amx_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_adform": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_adform", + "domain": ".nexx360.io", + "description": "OCD_n360_adform_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-adform": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-adform", + "domain": ".nexx360.io", + "description": "OCD_n360_adform_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_adnuntius": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_adnuntius", + "domain": ".nexx360.io", + "description": "OCD_n360_adnuntius_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-adnuntius": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-adnuntius", + "domain": ".nexx360.io", + "description": "OCD_n360_adnuntius_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_adot": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_adot", + "domain": ".nexx360.io", + "description": "OCD_n360_adot_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-adot": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-adot", + "domain": ".nexx360.io", + "description": "OCD_n360_adot_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_adyoulike": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_adyoulike", + "domain": ".nexx360.io", + "description": "OCD_n360_adyoulike_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-adyoulike": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-adyoulike", + "domain": ".nexx360.io", + "description": "OCD_n360_adyoulike_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_connectad": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_connectad", + "domain": ".nexx360.io", + "description": "OCD_n360_connectad_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-connectad": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-connectad", + "domain": ".nexx360.io", + "description": "OCD_n360_connectad_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_conversant": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_conversant", + "domain": ".nexx360.io", + "description": "OCD_n360_conversant_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-conversant": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-conversant", + "domain": ".nexx360.io", + "description": "OCD_n360_conversant_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_cwire": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_cwire", + "domain": ".nexx360.io", + "description": "OCD_n360_cwire_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-cwire": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-cwire", + "domain": ".nexx360.io", + "description": "OCD_n360_cwire_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_firstid": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_firstid", + "domain": ".nexx360.io", + "description": "OCD_n360_firstid_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-firstid": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-firstid", + "domain": ".nexx360.io", + "description": "OCD_n360_firstid_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_freewheel": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_freewheel", + "domain": ".nexx360.io", + "description": "OCD_n360_freewheel_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-freewheel": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-freewheel", + "domain": ".nexx360.io", + "description": "OCD_n360_freewheel_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_fueldigitalmedia": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_fueldigitalmedia", + "domain": ".nexx360.io", + "description": "OCD_n360_fueldigitalmedia_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-fueldigitalmedia": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-fueldigitalmedia", + "domain": ".nexx360.io", + "description": "OCD_n360_fueldigitalmedia_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_fueldigitalix": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_fueldigitalix", + "domain": ".nexx360.io", + "description": "OCD_n360_fueldigitalix_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-fueldigitalix": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-fueldigitalix", + "domain": ".nexx360.io", + "description": "OCD_n360_fueldigitalix_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_fueldigital": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_fueldigital", + "domain": ".nexx360.io", + "description": "OCD_n360_fueldigital_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-fueldigital": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-fueldigital", + "domain": ".nexx360.io", + "description": "OCD_n360_fueldigital_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_gingerad": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_gingerad", + "domain": ".nexx360.io", + "description": "OCD_n360_gingerad_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-gingerad": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-gingerad", + "domain": ".nexx360.io", + "description": "OCD_n360_gingerad_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_goodad": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_goodad", + "domain": ".nexx360.io", + "description": "OCD_n360_goodad_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-goodad": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-goodad", + "domain": ".nexx360.io", + "description": "OCD_n360_goodad_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_gravity": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_gravity", + "domain": ".nexx360.io", + "description": "OCD_n360_gravity_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-gravity": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-gravity", + "domain": ".nexx360.io", + "description": "OCD_n360_gravity_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_groupm": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_groupm", + "domain": ".nexx360.io", + "description": "OCD_n360_groupm_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-groupm": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-groupm", + "domain": ".nexx360.io", + "description": "OCD_n360_groupm_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_improve": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_improve", + "domain": ".nexx360.io", + "description": "OCD_n360_improve_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-improve": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-improve", + "domain": ".nexx360.io", + "description": "OCD_n360_improve_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_ix": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_ix", + "domain": ".nexx360.io", + "description": "OCD_n360_ix_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-ix": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-ix", + "domain": ".nexx360.io", + "description": "OCD_n360_ix_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_medianet": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_medianet", + "domain": ".nexx360.io", + "description": "OCD_n360_medianet_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-medianet": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-medianet", + "domain": ".nexx360.io", + "description": "OCD_n360_medianet_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_moneytag": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_moneytag", + "domain": ".nexx360.io", + "description": "OCD_n360_moneytag_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-moneytag": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-moneytag", + "domain": ".nexx360.io", + "description": "OCD_n360_moneytag_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_nextmillennium": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_nextmillennium", + "domain": ".nexx360.io", + "description": "OCD_n360_nextmillennium_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-nextmillennium": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-nextmillennium", + "domain": ".nexx360.io", + "description": "OCD_n360_nextmillennium_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_onetag": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_onetag", + "domain": ".nexx360.io", + "description": "OCD_n360_onetag_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-onetag": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-onetag", + "domain": ".nexx360.io", + "description": "OCD_n360_onetag_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_openx": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_openx", + "domain": ".nexx360.io", + "description": "OCD_n360_openx_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-openx": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-openx", + "domain": ".nexx360.io", + "description": "OCD_n360_openx_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_outbrain": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_outbrain", + "domain": ".nexx360.io", + "description": "OCD_n360_outbrain_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-outbrain": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-outbrain", + "domain": ".nexx360.io", + "description": "OCD_n360_outbrain_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_plista": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_plista", + "domain": ".nexx360.io", + "description": "OCD_n360_plista_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-plista": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-plista", + "domain": ".nexx360.io", + "description": "OCD_n360_plista_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_pubmatic": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_pubmatic", + "domain": ".nexx360.io", + "description": "OCD_n360_pubmatic_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-pubmatic": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-pubmatic", + "domain": ".nexx360.io", + "description": "OCD_n360_pubmatic_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_pulsepoint": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_pulsepoint", + "domain": ".nexx360.io", + "description": "OCD_n360_pulsepoint_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-pulsepoint": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-pulsepoint", + "domain": ".nexx360.io", + "description": "OCD_n360_pulsepoint_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_quantum": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_quantum", + "domain": ".nexx360.io", + "description": "OCD_n360_quantum_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-quantum": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-quantum", + "domain": ".nexx360.io", + "description": "OCD_n360_quantum_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_richaudience": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_richaudience", + "domain": ".nexx360.io", + "description": "OCD_n360_richaudience_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-richaudience": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-richaudience", + "domain": ".nexx360.io", + "description": "OCD_n360_richaudience_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_rtbhouse": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_rtbhouse", + "domain": ".nexx360.io", + "description": "OCD_n360_rtbhouse_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-rtbhouse": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-rtbhouse", + "domain": ".nexx360.io", + "description": "OCD_n360_rtbhouse_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_rubicon": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_rubicon", + "domain": ".nexx360.io", + "description": "OCD_n360_rubicon_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-rubicon": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-rubicon", + "domain": ".nexx360.io", + "description": "OCD_n360_rubicon_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_sharethrough": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_sharethrough", + "domain": ".nexx360.io", + "description": "OCD_n360_sharethrough_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-sharethrough": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-sharethrough", + "domain": ".nexx360.io", + "description": "OCD_n360_sharethrough_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_smaato": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_smaato", + "domain": ".nexx360.io", + "description": "OCD_n360_smaato_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-smaato": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-smaato", + "domain": ".nexx360.io", + "description": "OCD_n360_smaato_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_smartadserver": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_smartadserver", + "domain": ".nexx360.io", + "description": "OCD_n360_smartadserver_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-smartadserver": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-smartadserver", + "domain": ".nexx360.io", + "description": "OCD_n360_smartadserver_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_smartyads": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_smartyads", + "domain": ".nexx360.io", + "description": "OCD_n360_smartyads_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-smartyads": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-smartyads", + "domain": ".nexx360.io", + "description": "OCD_n360_smartyads_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_smilewanted": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_smilewanted", + "domain": ".nexx360.io", + "description": "OCD_n360_smilewanted_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-smilewanted": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-smilewanted", + "domain": ".nexx360.io", + "description": "OCD_n360_smilewanted_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_staila": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_staila", + "domain": ".nexx360.io", + "description": "OCD_n360_staila_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-staila": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-staila", + "domain": ".nexx360.io", + "description": "OCD_n360_staila_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_tappx": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_tappx", + "domain": ".nexx360.io", + "description": "OCD_n360_tappx_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-tappx": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-tappx", + "domain": ".nexx360.io", + "description": "OCD_n360_tappx_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_mediagrid": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_mediagrid", + "domain": ".nexx360.io", + "description": "OCD_n360_mediagrid_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-mediagrid": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-mediagrid", + "domain": ".nexx360.io", + "description": "OCD_n360_mediagrid_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_ttd": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_ttd", + "domain": ".nexx360.io", + "description": "OCD_n360_ttd_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-ttd": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-ttd", + "domain": ".nexx360.io", + "description": "OCD_n360_ttd_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_traffective": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_traffective", + "domain": ".nexx360.io", + "description": "OCD_n360_traffective_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-traffective": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-traffective", + "domain": ".nexx360.io", + "description": "OCD_n360_traffective_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_triplelift": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_triplelift", + "domain": ".nexx360.io", + "description": "OCD_n360_triplelift_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-triplelift": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-triplelift", + "domain": ".nexx360.io", + "description": "OCD_n360_triplelift_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_yahoo": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_yahoo", + "domain": ".nexx360.io", + "description": "OCD_n360_yahoo_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-yahoo": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-yahoo", + "domain": ".nexx360.io", + "description": "OCD_n360_yahoo_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_vidoomy": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_vidoomy", + "domain": ".nexx360.io", + "description": "OCD_n360_vidoomy_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-vidoomy": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-vidoomy", + "domain": ".nexx360.io", + "description": "OCD_n360_vidoomy_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360_yieldlab": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360_yieldlab", + "domain": ".nexx360.io", + "description": "OCD_n360_yieldlab_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "n360-yieldlab": [ + { + "platform": "Nexx360", + "category": "Marketing", + "name": "n360-yieldlab", + "domain": ".nexx360.io", + "description": "OCD_n360_yieldlab_description", + "retention": "OCD_retention_3_months", + "dataController": "Nexx360", + "gdprUrl": "https://nexx360.io/en/privacy-policy-and-cookies/", + "wildcard": "0" + } + ], + "mv_tokens": [ + { + "platform": "MediaVine", + "category": "Marketing", + "name": "mv_tokens", + "domain": "exchange.mediavine.com", + "description": "OCD_mv_tokens_description", + "retention": "OCD_retention_14_days", + "dataController": "MediaVine", + "gdprUrl": "https://www.mediavine.com/privacy-policy/#cookie-policy", + "wildcard": "0" + } + ], + "mv_tokens_invalidate-verizon-pushes": [ + { + "platform": "MediaVine", + "category": "Marketing", + "name": "mv_tokens_invalidate-verizon-pushes", + "domain": "exchange.mediavine.com", + "description": "OCD_mv_tokens_invalidate_verizon_pushes_description", + "retention": "OCD_retention_14_days", + "dataController": "MediaVine", + "gdprUrl": "https://www.mediavine.com/privacy-policy/#cookie-policy", + "wildcard": "0" + } + ], + "am_tokens": [ + { + "platform": "MediaVine", + "category": "Marketing", + "name": "am_tokens", + "domain": "exchange.mediavine.com", + "description": "OCD_am_tokens_description", + "retention": "OCD_retention_14_days", + "dataController": "MediaVine", + "gdprUrl": "https://www.mediavine.com/privacy-policy/#cookie-policy", + "wildcard": "0" + } + ], + "am_tokens_invalidate-verizon-pushes": [ + { + "platform": "MediaVine", + "category": "Marketing", + "name": "am_tokens_invalidate-verizon-pushes", + "domain": "exchange.mediavine.com", + "description": "OCD_am_tokens_invalidate_verizon_pushes_description", + "retention": "OCD_retention_14_days", + "dataController": "MediaVine", + "gdprUrl": "https://www.mediavine.com/privacy-policy/#cookie-policy", + "wildcard": "0" + } + ], + "_bit": [ + { + "platform": "Bit.ly", + "category": "Analytics", + "name": "_bit", + "domain": ".bit.ly", + "description": "OCD__bit_description", + "retention": "OCD_retention_6_months", + "dataController": "Bit.ly", + "gdprUrl": "https://bitly.com/pages/privacy", + "wildcard": "0" + } + ], + "ptrcriteo": [ + { + "platform": "Yieldmo", + "category": "Marketing", + "name": "ptrcriteo", + "domain": "ads.yieldmo.com", + "description": "OCD_ptrcriteo_description", + "retention": "OCD_retention_1_Year", + "dataController": "Yieldmo", + "gdprUrl": "https://yieldmo.com/privacy-policy/", + "wildcard": "0" + } + ], + "ptrrhs": [ + { + "platform": "Yieldmo", + "category": "Marketing", + "name": "ptrrhs", + "domain": "ads.yieldmo.com", + "description": "OCD_ptrrhs_description", + "retention": "OCD_retention_1_Year", + "dataController": "Yieldmo", + "gdprUrl": "https://yieldmo.com/privacy-policy/", + "wildcard": "0" + } + ], + "yieldmo_id": [ + { + "platform": "Yieldmo", + "category": "Analytics", + "name": "yieldmo_id", + "domain": ".yieldmo.com", + "description": "OCD_yieldmo_id_description", + "retention": "OCD_retention_1_Year", + "dataController": "Yieldmo", + "gdprUrl": "https://yieldmo.com/privacy-policy/", + "wildcard": "0" + } + ], + "ssid": [ + { + "platform": "Springserve", + "category": "Marketing", + "name": "ssid", + "domain": ".springserve.com", + "description": "OCD_ssid_description", + "retention": "OCD_retention_365_days", + "dataController": "Magnite", + "gdprUrl": "https://www.magnite.com/legal/platform-cookie-policy/", + "wildcard": "0" + } + ], + "sst": [ + { + "platform": "Springserve", + "category": "Marketing", + "name": "sst", + "domain": ".springserve.com", + "description": "OCD_sst_description", + "retention": "OCD_retention_365_days", + "dataController": "Magnite", + "gdprUrl": "https://www.magnite.com/legal/platform-cookie-policy/", + "wildcard": "0" + } + ], + "dgzsdl08v4": [ + { + "platform": "Bouncex", + "category": "Marketing", + "name": "dgzsdl08v4", + "domain": ".bounceexchange.com", + "description": "OCD_dgzsdl08v4_description", + "retention": "OCD_retention_0_days", + "dataController": "Wunderkind", + "gdprUrl": "https://www.wunderkind.co/privacy/", + "wildcard": "0" + } + ], + "bounceClientVisit": [ + { + "platform": "Bouncex", + "category": "Marketing", + "name": "bounceClientVisit", + "domain": ".bounceexchange.com", + "description": "OCD_bounceClientVisit_description", + "retention": "OCD_retention_Session", + "dataController": "Wunderkind", + "gdprUrl": "https://www.wunderkind.co/privacy/", + "wildcard": "0" + } + ], + "_vfa": [ + { + "platform": "Viafoura", + "category": "Analytics", + "name": "_vfa", + "domain": "", + "description": "OCD__vfa_description", + "retention": "OCD_retention_1_year", + "dataController": "Viafoura", + "gdprUrl": "https://viafoura.com/privacy-policy/", + "wildcard": "0" + } + ], + "_vfb": [ + { + "platform": "Viafoura", + "category": "Analytics", + "name": "_vfb", + "domain": "", + "description": "OCD__vfb_description", + "retention": "OCD_retention_30_minutes", + "dataController": "Viafoura", + "gdprUrl": "https://viafoura.com/privacy-policy/", + "wildcard": "0" + } + ], + "_vfz": [ + { + "platform": "Viafoura", + "category": "Analytics", + "name": "_vfz", + "domain": "", + "description": "OCD__vfz_description", + "retention": "OCD_retention_6_minutes", + "dataController": "Viafoura", + "gdprUrl": "https://viafoura.com/privacy-policy/", + "wildcard": "0" + } + ], + "_vf_rd_test*": [ + { + "platform": "Viafoura", + "category": "Functional", + "name": "_vf_rd_test*", + "domain": "", + "description": "OCD__vf_rd_test__description", + "retention": "OCD_retention_1_second", + "dataController": "Viafoura", + "gdprUrl": "https://viafoura.com/privacy-policy/", + "wildcard": "1" + } + ], + "VfSess": [ + { + "platform": "Viafoura", + "category": "Functional", + "name": "VfSess", + "domain": ".viafoura.co", + "description": "OCD_VfSess_description", + "retention": "OCD_retention_30_days", + "dataController": "Viafoura", + "gdprUrl": "https://viafoura.com/privacy-policy/", + "wildcard": "0" + } + ], + "VfRefresh": [ + { + "platform": "Viafoura", + "category": "Functional", + "name": "VfRefresh", + "domain": ".viafoura.co", + "description": "OCD_VfRefresh_description", + "retention": "OCD_retention_1_year", + "dataController": "Viafoura", + "gdprUrl": "https://viafoura.com/privacy-policy/", + "wildcard": "0" + } + ], + "VfAccess*": [ + { + "platform": "Viafoura", + "category": "Functional", + "name": "VfAccess*", + "domain": ".viafoura.co", + "description": "OCD_VfAccess__description", + "retention": "OCD_retention_5_minutes", + "dataController": "Viafoura", + "gdprUrl": "https://viafoura.com/privacy-policy/", + "wildcard": "1" + } + ], + "vfThirdpartyCookiesEnabled": [ + { + "platform": "Viafoura", + "category": "Functional", + "name": "vfThirdpartyCookiesEnabled", + "domain": ".viafoura.co", + "description": "OCD_vfThirdpartyCookiesEnabled_description", + "retention": "OCD_retention_Session", + "dataController": "Viafoura", + "gdprUrl": "https://viafoura.com/privacy-policy/", + "wildcard": "0" + } + ], + "ttbprf": [ + { + "platform": "Tailtarget", + "category": "Marketing", + "name": "ttbprf", + "domain": ".t.tailtarget.com", + "description": "OCD_ttbprf_description", + "retention": "OCD_retention_1_month", + "dataController": "Totvs", + "gdprUrl": "https://www.totvs.com/protecao-e-privacidade-de-dados/", + "wildcard": "0" + } + ], + "ttc": [ + { + "platform": "Tailtarget", + "category": "Marketing", + "name": "ttc", + "domain": ".t.tailtarget.com", + "description": "OCD_ttc_description", + "retention": "OCD_retention_1_day", + "dataController": "Totvs", + "gdprUrl": "https://www.totvs.com/protecao-e-privacidade-de-dados/", + "wildcard": "0" + } + ], + "ttnprf": [ + { + "platform": "Tailtarget", + "category": "Marketing", + "name": "ttnprf", + "domain": ".t.tailtarget.com", + "description": "OCD_ttnprf_description", + "retention": "OCD_retention_1_month", + "dataController": "Totvs", + "gdprUrl": "https://www.totvs.com/protecao-e-privacidade-de-dados/", + "wildcard": "0" + } + ], + "n": [ + { + "platform": "Tailtarget", + "category": "Marketing", + "name": "n", + "domain": ".t.tailtarget.com", + "description": "OCD_n_description", + "retention": "OCD_retention_1_month", + "dataController": "Totvs", + "gdprUrl": "https://www.totvs.com/protecao-e-privacidade-de-dados/", + "wildcard": "0" + } + ], + "trk": [ + { + "platform": "Tailtarget", + "category": "Marketing", + "name": "trk", + "domain": ".t.tailtarget.com", + "description": "OCD_trk_description", + "retention": "OCD_retention_1_month", + "dataController": "Totvs", + "gdprUrl": "https://www.totvs.com/protecao-e-privacidade-de-dados/", + "wildcard": "0" + } + ], + "ttca": [ + { + "platform": "Tailtarget", + "category": "Marketing", + "name": "ttca", + "domain": ".t.tailtarget.com", + "description": "OCD_ttca_description", + "retention": "OCD_retention_30_days", + "dataController": "Totvs", + "gdprUrl": "https://www.totvs.com/protecao-e-privacidade-de-dados/", + "wildcard": "0" + } + ], + "tp*": [ + { + "platform": "Tailtarget", + "category": "Marketing", + "name": "tp*", + "domain": ".t.tailtarget.com", + "description": "OCD_tp__description", + "retention": "OCD_retention_30_days", + "dataController": "Totvs", + "gdprUrl": "https://www.totvs.com/protecao-e-privacidade-de-dados/", + "wildcard": "1" + } + ], + "ttgcm": [ + { + "platform": "Tailtarget", + "category": "Marketing", + "name": "ttgcm", + "domain": ".t.tailtarget.com", + "description": "OCD_ttgcm_description", + "retention": "OCD_retention_14_days", + "dataController": "Totvs", + "gdprUrl": "https://www.totvs.com/protecao-e-privacidade-de-dados/", + "wildcard": "0" + } + ], + "_ut*": [ + { + "platform": "Marfeel", + "category": "Analytics", + "name": "_ut*", + "domain": "events.newsroom.bi", + "description": "OCD__ut__description", + "retention": "OCD_retention_30_days", + "dataController": "Marfeel", + "gdprUrl": "https://community.marfeel.com/t/marfeel-com-privacy-policy/10383", + "wildcard": "1" + } + ], + "_u*": [ + { + "platform": "Marfeel", + "category": "Analytics", + "name": "_u*", + "domain": "events.newsroom.bi", + "description": "OCD__u__description", + "retention": "OCD_retention_180_days", + "dataController": "Marfeel", + "gdprUrl": "https://community.marfeel.com/t/marfeel-com-privacy-policy/10383", + "wildcard": "1" + } + ], + "_s*": [ + { + "platform": "Marfeel", + "category": "Analytics", + "name": "_s*", + "domain": "events.newsroom.bi", + "description": "OCD__s__description", + "retention": "OCD_retention_Session", + "dataController": "Marfeel", + "gdprUrl": "https://community.marfeel.com/t/marfeel-com-privacy-policy/10383", + "wildcard": "1" + } + ], + "_lv*": [ + { + "platform": "Marfeel", + "category": "Analytics", + "name": "_lv*", + "domain": "events.newsroom.bi", + "description": "OCD__lv__description", + "retention": "OCD_retention_180_days", + "dataController": "Marfeel", + "gdprUrl": "https://community.marfeel.com/t/marfeel-com-privacy-policy/10383", + "wildcard": "1" + } + ], + "_nrbi": [ + { + "platform": "Marfeel", + "category": "Analytics", + "name": "_nrbi", + "domain": "events.newsroom.bi", + "description": "OCD__nrbi_description", + "retention": "OCD_retention_180_days", + "dataController": "Marfeel", + "gdprUrl": "https://community.marfeel.com/t/marfeel-com-privacy-policy/10383", + "wildcard": "0" + } + ], + "compass_sid": [ + { + "platform": "Marfeel", + "category": "Analytics", + "name": "compass_sid", + "domain": "events.newsroom.bi", + "description": "OCD_compass_sid_description", + "retention": "OCD_retention_Session", + "dataController": "Marfeel", + "gdprUrl": "https://community.marfeel.com/t/marfeel-com-privacy-policy/10383", + "wildcard": "0" + } + ], + "compass_uid": [ + { + "platform": "Marfeel", + "category": "Analytics", + "name": "compass_uid", + "domain": "events.newsroom.bi", + "description": "OCD_compass_uid_description", + "retention": "OCD_retention_180_days", + "dataController": "Marfeel", + "gdprUrl": "https://community.marfeel.com/t/marfeel-com-privacy-policy/10383", + "wildcard": "0" + } + ], + "___m_rec": [ + { + "platform": "Marfeel", + "category": "Analytics", + "name": "___m_rec", + "domain": "events.newsroom.bi", + "description": "OCD____m_rec_description", + "retention": "OCD_retention_180_days", + "dataController": "Marfeel", + "gdprUrl": "https://community.marfeel.com/t/marfeel-com-privacy-policy/10383", + "wildcard": "0" + } + ], + "GRV_BHV_UID*": [ + { + "platform": "Groovinads", + "category": "Marketing", + "name": "GRV_BHV_UID*", + "domain": ".groovinads.com", + "description": "OCD_GRV_BHV_UID__description", + "retention": "OCD_retention_45_days", + "dataController": "Groovinads", + "gdprUrl": "https://shopping.groovinads.com/privacy", + "wildcard": "1" + } + ], + "GRV_BHV_IDCC*": [ + { + "platform": "Groovinads", + "category": "Marketing", + "name": "GRV_BHV_IDCC*", + "domain": ".groovinads.com", + "description": "OCD_GRV_BHV_IDCC__description", + "retention": "OCD_retention_45_days", + "dataController": "Groovinads", + "gdprUrl": "https://shopping.groovinads.com/privacy", + "wildcard": "1" + } + ], + "GRV_BHV_SKU*": [ + { + "platform": "Groovinads", + "category": "Marketing", + "name": "GRV_BHV_SKU*", + "domain": ".groovinads.com", + "description": "OCD_GRV_BHV_SKU__description", + "retention": "OCD_retention_45_days", + "dataController": "Groovinads", + "gdprUrl": "https://shopping.groovinads.com/privacy", + "wildcard": "1" + } + ], + "GRV_BHV_IDCAT*": [ + { + "platform": "Groovinads", + "category": "Marketing", + "name": "GRV_BHV_IDCAT*", + "domain": ".groovinads.com", + "description": "OCD_GRV_BHV_IDCAT__description", + "retention": "OCD_retention_45_days", + "dataController": "Groovinads", + "gdprUrl": "https://shopping.groovinads.com/privacy", + "wildcard": "1" + } + ], + "GRV_BHV_DATE*": [ + { + "platform": "Groovinads", + "category": "Marketing", + "name": "GRV_BHV_DATE*", + "domain": ".groovinads.com", + "description": "OCD_GRV_BHV_DATE__description", + "retention": "OCD_retention_45_days", + "dataController": "Groovinads", + "gdprUrl": "https://shopping.groovinads.com/privacy", + "wildcard": "1" + } + ], + "GRV_IDU": [ + { + "platform": "Groovinads", + "category": "Marketing", + "name": "GRV_IDU", + "domain": ".groovinads.com", + "description": "OCD_GRV_IDU_description", + "retention": "OCD_retention_296_days", + "dataController": "Groovinads", + "gdprUrl": "https://shopping.groovinads.com/privacy", + "wildcard": "0" + } + ], + "NPA*": [ + { + "platform": "Groovinads", + "category": "Marketing", + "name": "NPA*", + "domain": ".groovinads.com", + "description": "OCD_NPA__description", + "retention": "OCD_retention_45_days", + "dataController": "Groovinads", + "gdprUrl": "https://shopping.groovinads.com/privacy", + "wildcard": "1" + } + ], + "GRV_BHV_BRND_*": [ + { + "platform": "Groovinads", + "category": "Marketing", + "name": "GRV_BHV_BRND_*", + "domain": ".groovinads.com", + "description": "OCD_GRV_BHV_BRND___description", + "retention": "OCD_retention_45_days", + "dataController": "Groovinads", + "gdprUrl": "https://shopping.groovinads.com/privacy", + "wildcard": "1" + } + ], + "GRV_google": [ + { + "platform": "Groovinads", + "category": "Marketing", + "name": "GRV_google", + "domain": ".groovinads.com", + "description": "OCD_GRV_google_description", + "retention": "OCD_retention_45_days", + "dataController": "Groovinads", + "gdprUrl": "https://shopping.groovinads.com/privacy", + "wildcard": "0" + } + ], + "duid_update_time": [ + { + "platform": "Bidence", + "category": "Marketing", + "name": "duid_update_time", + "domain": ".bidence.net", + "description": "OCD_duid_update_time_description", + "retention": "OCD_retention_2_years", + "dataController": "Bidence", + "gdprUrl": "https://bidence.com/page/pp.html", + "wildcard": "0" + } + ], + "_ssp_update_time*": [ + { + "platform": "Bidence", + "category": "Marketing", + "name": "_ssp_update_time*", + "domain": ".bidence.net", + "description": "OCD__ssp_update_time__description", + "retention": "OCD_retention_2_years", + "dataController": "Bidence", + "gdprUrl": "https://bidence.com/page/pp.html", + "wildcard": "1" + } + ], + "_dsp_uid*": [ + { + "platform": "Bidence", + "category": "Marketing", + "name": "_dsp_uid*", + "domain": ".bidence.net", + "description": "OCD__dsp_uid__description", + "retention": "OCD_retention_2_years", + "dataController": "Bidence", + "gdprUrl": "https://bidence.com/page/pp.html", + "wildcard": "1" + } + ], + "connect.sid": [ + { + "platform": "Zendesk", + "category": "Functional", + "name": "connect.sid", + "domain": ".zendesk.com", + "description": "OCD_connect_sid_description", + "retention": "OCD_retention_Session", + "dataController": "Zendesk", + "gdprUrl": "https://www.zendesk.com/company/agreements-and-terms/privacy-notice/", + "wildcard": "0" + } + ], + "_zendesk_cookie": [ + { + "platform": "Zendesk", + "category": "Functional", + "name": "_zendesk_cookie", + "domain": "", + "description": "OCD__zendesk_cookie_description", + "retention": "OCD_retention_1_year", + "dataController": "Zendesk", + "gdprUrl": "https://www.zendesk.com/company/agreements-and-terms/privacy-notice/", + "wildcard": "0" + } + ], + "zte2095": [ + { + "platform": "Zendesk", + "category": "Functional", + "name": "zte2095", + "domain": ".zendesk.com", + "description": "OCD_zte2095_description", + "retention": "OCD_retention_Session", + "dataController": "Zendesk", + "gdprUrl": "https://www.zendesk.com/company/agreements-and-terms/privacy-notice/", + "wildcard": "0" + } + ], + "__zlcprivacy": [ + { + "platform": "Zendesk", + "category": "Functional", + "name": "__zlcprivacy", + "domain": ".zendesk.com", + "description": "OCD___zlcprivacy_description", + "retention": "OCD_retention_1_year", + "dataController": "Zendesk", + "gdprUrl": "https://www.zendesk.com/company/agreements-and-terms/privacy-notice/", + "wildcard": "0" + } + ], + "_answer_bot_service_session": [ + { + "platform": "Zendesk", + "category": "Functional", + "name": "_answer_bot_service_session", + "domain": ".zendesk.com", + "description": "OCD__answer_bot_service_session_description", + "retention": "OCD_retention_Session", + "dataController": "Zendesk", + "gdprUrl": "https://www.zendesk.com/company/agreements-and-terms/privacy-notice/", + "wildcard": "0" + } + ], + "ZD-zE_oauth": [ + { + "platform": "Zendesk", + "category": "Functional", + "name": "ZD-zE_oauth", + "domain": ".zendesk.com", + "description": "OCD_ZD_zE_oauth_description", + "retention": "OCD_retention_2_hours", + "dataController": "Zendesk", + "gdprUrl": "https://www.zendesk.com/company/agreements-and-terms/privacy-notice/", + "wildcard": "0" + } + ], + "_zendesk_session": [ + { + "platform": "Zendesk", + "category": "Functional", + "name": "_zendesk_session", + "domain": "", + "description": "OCD__zendesk_session_description", + "retention": "OCD_retention_8_hours", + "dataController": "Zendesk", + "gdprUrl": "https://www.zendesk.com/company/agreements-and-terms/privacy-notice/", + "wildcard": "0" + } + ], + "zendesk_thirdparty_test": [ + { + "platform": "Zendesk", + "category": "Functional", + "name": "zendesk_thirdparty_test", + "domain": "", + "description": "OCD_zendesk_thirdparty_test_description", + "retention": "OCD_retention_8_hours", + "dataController": "Zendesk", + "gdprUrl": "https://www.zendesk.com/company/agreements-and-terms/privacy-notice/", + "wildcard": "0" + } + ], + "_zendesk_shared_session": [ + { + "platform": "Zendesk", + "category": "Functional", + "name": "_zendesk_shared_session", + "domain": "", + "description": "OCD__zendesk_shared_session_description", + "retention": "OCD_retention_8_hours", + "dataController": "Zendesk", + "gdprUrl": "https://www.zendesk.com/company/agreements-and-terms/privacy-notice/", + "wildcard": "0" + } + ], + "_zendesk_authenticated": [ + { + "platform": "Zendesk", + "category": "Functional", + "name": "_zendesk_authenticated", + "domain": "", + "description": "OCD__zendesk_authenticated_description", + "retention": "OCD_retention_Session", + "dataController": "Zendesk", + "gdprUrl": "https://www.zendesk.com/company/agreements-and-terms/privacy-notice/", + "wildcard": "0" + } + ], + "_help_center_session": [ + { + "platform": "Zendesk", + "category": "Functional", + "name": "_help_center_session", + "domain": "", + "description": "OCD__help_center_session_description", + "retention": "OCD_retention_Session", + "dataController": "Zendesk", + "gdprUrl": "https://www.zendesk.com/company/agreements-and-terms/privacy-notice/", + "wildcard": "0" + } + ], + "help_center_data": [ + { + "platform": "Zendesk", + "category": "Functional", + "name": "help_center_data", + "domain": "", + "description": "OCD_help_center_data_description", + "retention": "OCD_retention_48_hours", + "dataController": "Zendesk", + "gdprUrl": "https://www.zendesk.com/company/agreements-and-terms/privacy-notice/", + "wildcard": "0" + } + ], + "_zdshared_user_session_analytics": [ + { + "platform": "Zendesk", + "category": "Analytics", + "name": "_zdshared_user_session_analytics", + "domain": "", + "description": "OCD__zdshared_user_session_analytics_description", + "retention": "OCD_retention_90_days", + "dataController": "Zendesk", + "gdprUrl": "https://www.zendesk.com/company/agreements-and-terms/privacy-notice/", + "wildcard": "0" + } + ], + "_zendesk_nps_session": [ + { + "platform": "Zendesk", + "category": "Functional", + "name": "_zendesk_nps_session", + "domain": "", + "description": "OCD__zendesk_nps_session_description", + "retention": "OCD_retention_Session", + "dataController": "Zendesk", + "gdprUrl": "https://www.zendesk.com/company/agreements-and-terms/privacy-notice/", + "wildcard": "0" + } + ], + "ZD-settings": [ + { + "platform": "Zendesk", + "category": "Functional", + "name": "ZD-settings", + "domain": "", + "description": "OCD_ZD_settings_description", + "retention": "OCD_retention_forever", + "dataController": "Zendesk", + "gdprUrl": "https://www.zendesk.com/company/agreements-and-terms/privacy-notice/", + "wildcard": "0" + } + ], + "ZD-suid": [ + { + "platform": "Zendesk", + "category": "Analytics", + "name": "ZD-suid", + "domain": "", + "description": "OCD_ZD_suid_description", + "retention": "OCD_retention_20_minutes", + "dataController": "Zendesk", + "gdprUrl": "https://www.zendesk.com/company/agreements-and-terms/privacy-notice/", + "wildcard": "0" + } + ], + "ZD-buid": [ + { + "platform": "Zendesk", + "category": "Analytics", + "name": "ZD-buid", + "domain": "", + "description": "OCD_ZD_buid_description", + "retention": "OCD_retention_forever", + "dataController": "Zendesk", + "gdprUrl": "https://www.zendesk.com/company/agreements-and-terms/privacy-notice/", + "wildcard": "0" + } + ], + "ZD-store": [ + { + "platform": "Zendesk", + "category": "Functional", + "name": "ZD-store", + "domain": "", + "description": "OCD_ZD_store_description", + "retention": "OCD_retention_forever", + "dataController": "Zendesk", + "gdprUrl": "https://www.zendesk.com/company/agreements-and-terms/privacy-notice/", + "wildcard": "0" + } + ], + "ZD-widgetOpen": [ + { + "platform": "Zendesk", + "category": "Functional", + "name": "ZD-widgetOpen", + "domain": "", + "description": "OCD_ZD_widgetOpen_description", + "retention": "OCD_retention_forever", + "dataController": "Zendesk", + "gdprUrl": "https://www.zendesk.com/company/agreements-and-terms/privacy-notice/", + "wildcard": "0" + } + ], + "ZD-launcherLabelRemoved": [ + { + "platform": "Zendesk", + "category": "Functional", + "name": "ZD-launcherLabelRemoved", + "domain": "", + "description": "OCD_ZD_launcherLabelRemoved_description", + "retention": "OCD_retention_forever", + "dataController": "Zendesk", + "gdprUrl": "https://www.zendesk.com/company/agreements-and-terms/privacy-notice/", + "wildcard": "0" + } + ], + "_zdsession_talk_embeddables_service": [ + { + "platform": "Zendesk", + "category": "Functional", + "name": "_zdsession_talk_embeddables_service", + "domain": "", + "description": "OCD__zdsession_talk_embeddables_service_description", + "retention": "OCD_retention_Session", + "dataController": "Zendesk", + "gdprUrl": "https://www.zendesk.com/company/agreements-and-terms/privacy-notice/", + "wildcard": "0" + } + ], + "RT": [ + { + "platform": "Tripadvisor", + "category": "Functional", + "name": "RT", + "domain": "www.tamgrt.com", + "description": "OCD_RT_description", + "retention": "OCD_retention_399_days", + "dataController": "Tripadvisor", + "gdprUrl": "https://tripadvisor.mediaroom.com/us-privacy-policy", + "wildcard": "0" + } + ], + "TADCID": [ + { + "platform": "Tripadvisor", + "category": "Marketing", + "name": "TADCID", + "domain": "www.tamgrt.com", + "description": "OCD_TADCID_description", + "retention": "OCD_retention_10_years", + "dataController": "Tripadvisor", + "gdprUrl": "https://tripadvisor.mediaroom.com/us-privacy-policy", + "wildcard": "0" + } + ], + "ServerPool": [ + { + "platform": "Tripadvisor", + "category": "Marketing", + "name": "ServerPool", + "domain": "www.tamgrt.com", + "description": "OCD_ServerPool_description", + "retention": "OCD_retention_Session", + "dataController": "Tripadvisor", + "gdprUrl": "https://tripadvisor.mediaroom.com/us-privacy-policy", + "wildcard": "0" + } + ], + "TATravelInfo": [ + { + "platform": "Tripadvisor", + "category": "Analytics", + "name": "TATravelInfo", + "domain": "", + "description": "OCD_TATravelInfo_description", + "retention": "OCD_retention_2_years", + "dataController": "Tripadvisor", + "gdprUrl": "https://tripadvisor.mediaroom.com/us-privacy-policy", + "wildcard": "0" + } + ], + "TAUnique": [ + { + "platform": "Tripadvisor", + "category": "Analytics", + "name": "TAUnique", + "domain": "", + "description": "OCD_TAUnique_description", + "retention": "OCD_retention_Session", + "dataController": "Tripadvisor", + "gdprUrl": "https://tripadvisor.mediaroom.com/us-privacy-policy", + "wildcard": "0" + } + ], + "TAReturnTo": [ + { + "platform": "Tripadvisor", + "category": "Analytics", + "name": "TAReturnTo", + "domain": "", + "description": "OCD_TAReturnTo_description", + "retention": "OCD_retention_Session", + "dataController": "Tripadvisor", + "gdprUrl": "https://tripadvisor.mediaroom.com/us-privacy-policy", + "wildcard": "0" + } + ], + "dt": [ + { + "platform": "Underdog Media", + "category": "Marketing", + "name": "dt", + "domain": "udmserve.net", + "description": "OCD_dt_description", + "retention": "OCD_retention_1_year", + "dataController": "Underdog Media", + "gdprUrl": "https://underdogmedia.com/privacy/", + "wildcard": "0" + } + ], + "rtbh": [ + { + "platform": "Underdog Media", + "category": "Marketing", + "name": "rtbh", + "domain": "udmserve.net", + "description": "OCD_rtbh_description", + "retention": "OCD_retention_1_year", + "dataController": "Underdog Media", + "gdprUrl": "https://underdogmedia.com/privacy/", + "wildcard": "0" + } + ], + "udmts": [ + { + "platform": "Underdog Media", + "category": "Marketing", + "name": "udmts", + "domain": "udmserve.net", + "description": "OCD_udmts_description", + "retention": "OCD_retention_89_days", + "dataController": "Underdog Media", + "gdprUrl": "https://underdogmedia.com/privacy/", + "wildcard": "0" + } + ], + "api_token": [ + { + "platform": "Twitch", + "category": "Functional", + "name": "api_token", + "domain": ".twitch.tv", + "description": "OCD_api_token_description", + "retention": "OCD_retention_3652_days", + "dataController": "Twitch", + "gdprUrl": "https://www.twitch.tv/p/en/legal/privacy-notice/", + "wildcard": "0" + } + ], + "unique_id": [ + { + "platform": "Twitch", + "category": "Functional", + "name": "unique_id", + "domain": ".twitch.tv", + "description": "OCD_unique_id_description", + "retention": "OCD_retention_3652_days", + "dataController": "Twitch", + "gdprUrl": "https://www.twitch.tv/p/en/legal/privacy-notice/", + "wildcard": "0" + } + ], + "unique_id_durable": [ + { + "platform": "Twitch", + "category": "Functional", + "name": "unique_id_durable", + "domain": ".twitch.tv", + "description": "OCD_unique_id_durable_description", + "retention": "OCD_retention_3652_days", + "dataController": "Twitch", + "gdprUrl": "https://www.twitch.tv/p/en/legal/privacy-notice/", + "wildcard": "0" + } + ], + "session_unique_id": [ + { + "platform": "Twitch", + "category": "Functional", + "name": "session_unique_id", + "domain": ".twitch.tv", + "description": "OCD_session_unique_id_description", + "retention": "OCD_retention_Session", + "dataController": "Twitch", + "gdprUrl": "https://www.twitch.tv/p/en/legal/privacy-notice/", + "wildcard": "0" + } + ], + "server_session_id": [ + { + "platform": "Twitch", + "category": "Functional", + "name": "server_session_id", + "domain": ".twitch.tv", + "description": "OCD_server_session_id_description", + "retention": "OCD_retention_Session", + "dataController": "Twitch", + "gdprUrl": "https://www.twitch.tv/p/en/legal/privacy-notice/", + "wildcard": "0" + } + ], + "twitch.lohp.countryCode": [ + { + "platform": "Twitch", + "category": "Functional", + "name": "twitch.lohp.countryCode", + "domain": ".twitch.tv", + "description": "OCD_twitch_lohp_countryCode_description", + "retention": "OCD_retention_3650_days", + "dataController": "Twitch", + "gdprUrl": "https://www.twitch.tv/p/en/legal/privacy-notice/", + "wildcard": "0" + } + ], + "auth-token": [ + { + "platform": "Twitch", + "category": "Functional", + "name": "auth-token", + "domain": ".twitch.tv", + "description": "OCD_auth_token_description", + "retention": "OCD_retention_Session", + "dataController": "Twitch", + "gdprUrl": "https://www.twitch.tv/p/en/legal/privacy-notice/", + "wildcard": "0" + } + ], + "enable-compact-scene-listing": [ + { + "platform": "Twitch", + "category": "Functional", + "name": "enable-compact-scene-listing", + "domain": ".twitch.tv", + "description": "OCD_enable_compact_scene_listing_description", + "retention": "OCD_retention_3650_days", + "dataController": "Twitch", + "gdprUrl": "https://www.twitch.tv/p/en/legal/privacy-notice/", + "wildcard": "0" + } + ], + "videoChat.notice_dismissed": [ + { + "platform": "Twitch", + "category": "Functional", + "name": "videoChat.notice_dismissed", + "domain": ".twitch.tv", + "description": "OCD_videoChat_notice_dismissed_description", + "retention": "OCD_retention_3650_days", + "dataController": "Twitch", + "gdprUrl": "https://www.twitch.tv/p/en/legal/privacy-notice/", + "wildcard": "0" + } + ], + "chat_rules_shown": [ + { + "platform": "Twitch", + "category": "Functional", + "name": "chat_rules_shown", + "domain": ".twitch.tv", + "description": "OCD_chat_rules_shown_description", + "retention": "OCD_retention_3650_days", + "dataController": "Twitch", + "gdprUrl": "https://www.twitch.tv/p/en/legal/privacy-notice/", + "wildcard": "0" + } + ], + "algoliasearch-client-js": [ + { + "platform": "Twitch", + "category": "Functional", + "name": "algoliasearch-client-js", + "domain": ".twitch.tv", + "description": "OCD_algoliasearch_client_js_description", + "retention": "OCD_retention_3650_days", + "dataController": "Twitch", + "gdprUrl": "https://www.twitch.tv/p/en/legal/privacy-notice/", + "wildcard": "0" + } + ], + "device_id": [ + { + "platform": "Twitch", + "category": "Functional", + "name": "device_id", + "domain": "embed.twitch.tv", + "description": "OCD_device_id_description", + "retention": "OCD_retention_3650_days", + "dataController": "Twitch", + "gdprUrl": "https://www.twitch.tv/p/en/legal/privacy-notice/", + "wildcard": "0" + } + ], + "referrer_url": [ + { + "platform": "Twitch", + "category": "Functional", + "name": "referrer_url", + "domain": ".twitch.tv", + "description": "OCD_referrer_url_description", + "retention": "OCD_retention_3650_days", + "dataController": "Twitch", + "gdprUrl": "https://www.twitch.tv/p/en/legal/privacy-notice/", + "wildcard": "0" + } + ], + "sentry_device_id": [ + { + "platform": "Twitch", + "category": "Functional", + "name": "sentry_device_id", + "domain": "player.twitch.tv", + "description": "OCD_sentry_device_id_description", + "retention": "OCD_retention_3650_days", + "dataController": "Twitch", + "gdprUrl": "https://www.twitch.tv/p/en/legal/privacy-notice/", + "wildcard": "0" + } + ], + "session_storage_last_visited_twitch_url": [ + { + "platform": "Twitch", + "category": "Functional", + "name": "session_storage_last_visited_twitch_url", + "domain": "player.twitch.tv", + "description": "OCD_session_storage_last_visited_twitch_url_description", + "retention": "OCD_retention_3650_days", + "dataController": "Twitch", + "gdprUrl": "https://www.twitch.tv/p/en/legal/privacy-notice/", + "wildcard": "0" + } + ], + "local_storage_app_session_id": [ + { + "platform": "Twitch", + "category": "Functional", + "name": "local_storage_app_session_id", + "domain": "player.twitch.tv", + "description": "OCD_local_storage_app_session_id_description", + "retention": "OCD_retention_3650_days", + "dataController": "Twitch", + "gdprUrl": "https://www.twitch.tv/p/en/legal/privacy-notice/", + "wildcard": "0" + } + ], + "_ceir": [ + { + "platform": "Crazy Egg", + "category": "Analytics", + "name": "_ceir", + "domain": "", + "description": "OCD__ceir_description", + "retention": "OCD_retention_1_Year", + "dataController": "Crazy Egg", + "gdprUrl": "https://www.crazyegg.com/cookies", + "wildcard": "0" + } + ], + "_CEFT": [ + { + "platform": "Crazy Egg", + "category": "Analytics", + "name": "_CEFT", + "domain": "", + "description": "OCD__CEFT_description", + "retention": "OCD_retention_1_Year", + "dataController": "Crazy Egg", + "gdprUrl": "https://www.crazyegg.com/cookies", + "wildcard": "0" + } + ], + "_cer.v": [ + { + "platform": "Crazy Egg", + "category": "Analytics", + "name": "_cer.v", + "domain": "", + "description": "OCD__cer_v_description", + "retention": "OCD_retention_31_days", + "dataController": "Crazy Egg", + "gdprUrl": "https://www.crazyegg.com/cookies", + "wildcard": "0" + } + ], + "_ce.s": [ + { + "platform": "Crazy Egg", + "category": "Analytics", + "name": "_ce.s", + "domain": "", + "description": "OCD__ce_s_description", + "retention": "OCD_retention_1_year", + "dataController": "Crazy Egg", + "gdprUrl": "https://www.crazyegg.com/cookies", + "wildcard": "0" + } + ], + "_ce.cch": [ + { + "platform": "Crazy Egg", + "category": "Analytics", + "name": "_ce.cch", + "domain": "", + "description": "OCD__ce_cch_description", + "retention": "OCD_retention_1_second", + "dataController": "Crazy Egg", + "gdprUrl": "https://www.crazyegg.com/cookies", + "wildcard": "0" + } + ], + "_ce.gtld": [ + { + "platform": "Crazy Egg", + "category": "Analytics", + "name": "_ce.gtld", + "domain": "", + "description": "OCD__ce_gtld_description", + "retention": "OCD_retention_1_second", + "dataController": "Crazy Egg", + "gdprUrl": "https://www.crazyegg.com/cookies", + "wildcard": "0" + } + ], + "ce_need_secure_cookie": [ + { + "platform": "Crazy Egg", + "category": "Analytics", + "name": "ce_need_secure_cookie", + "domain": "", + "description": "OCD_ce_need_secure_cookie_description", + "retention": "OCD_retention_1_second", + "dataController": "Crazy Egg", + "gdprUrl": "https://www.crazyegg.com/cookies", + "wildcard": "0" + } + ], + "ce_successful_csp_check": [ + { + "platform": "Crazy Egg", + "category": "Analytics", + "name": "ce_successful_csp_check", + "domain": "", + "description": "OCD_ce_successful_csp_check_description", + "retention": "OCD_retention_24_hours", + "dataController": "Crazy Egg", + "gdprUrl": "https://www.crazyegg.com/cookies", + "wildcard": "0" + } + ], + "cebs": [ + { + "platform": "Crazy Egg", + "category": "Analytics", + "name": "cebs", + "domain": "crazyegg.com", + "description": "OCD_cebs_description", + "retention": "OCD_retention_Session", + "dataController": "Crazy Egg", + "gdprUrl": "https://www.crazyegg.com/cookies", + "wildcard": "0" + } + ], + "cebsp_": [ + { + "platform": "Crazy Egg", + "category": "Analytics", + "name": "cebsp_", + "domain": "crazyegg.com", + "description": "OCD_cebsp__description", + "retention": "OCD_retention_Session", + "dataController": "Crazy Egg", + "gdprUrl": "https://www.crazyegg.com/cookies", + "wildcard": "0" + } + ], + "_ce.clock_event": [ + { + "platform": "Crazy Egg", + "category": "Analytics", + "name": "_ce.clock_event", + "domain": "", + "description": "OCD__ce_clock_event_description", + "retention": "OCD_retention_1_Day", + "dataController": "Crazy Egg", + "gdprUrl": "https://www.crazyegg.com/cookies", + "wildcard": "0" + } + ], + "_ce.clock_data": [ + { + "platform": "Crazy Egg", + "category": "Analytics", + "name": "_ce.clock_data", + "domain": "", + "description": "OCD__ce_clock_data_description", + "retention": "OCD_retention_1_Day", + "dataController": "Crazy Egg", + "gdprUrl": "https://www.crazyegg.com/cookies", + "wildcard": "0" + } + ], + "_ce.irv": [ + { + "platform": "Crazy Egg", + "category": "Analytics", + "name": "_ce.irv", + "domain": "", + "description": "OCD__ce_irv_description", + "retention": "OCD_retention_Session", + "dataController": "Crazy Egg", + "gdprUrl": "https://www.crazyegg.com/cookies", + "wildcard": "0" + } + ], + "ceft_variant_override": [ + { + "platform": "Crazy Egg", + "category": "Analytics", + "name": "ceft_variant_override", + "domain": "", + "description": "OCD_ceft_variant_override_description", + "retention": "OCD_retention_Session", + "dataController": "Crazy Egg", + "gdprUrl": "https://www.crazyegg.com/cookies", + "wildcard": "0" + } + ], + "_crazyegg": [ + { + "platform": "Crazy Egg", + "category": "Marketing", + "name": "_crazyegg", + "domain": "", + "description": "OCD__crazyegg_description", + "retention": "OCD_retention_Session", + "dataController": "Crazy Egg", + "gdprUrl": "https://www.crazyegg.com/cookies", + "wildcard": "0" + } + ], + "ce_login": [ + { + "platform": "Crazy Egg", + "category": "Functional", + "name": "ce_login", + "domain": "crazyegg.com", + "description": "OCD_ce_login_description", + "retention": "OCD_retention_1_year", + "dataController": "Crazy Egg", + "gdprUrl": "https://www.crazyegg.com/cookies", + "wildcard": "0" + } + ], + "ce_signup_flow": [ + { + "platform": "Crazy Egg", + "category": "Functional", + "name": "ce_signup_flow", + "domain": "crazyegg.com", + "description": "OCD_ce_signup_flow_description", + "retention": "OCD_retention_1_year", + "dataController": "Crazy Egg", + "gdprUrl": "https://www.crazyegg.com/cookies", + "wildcard": "0" + } + ], + "ce_signup_partner": [ + { + "platform": "Crazy Egg", + "category": "Marketing", + "name": "ce_signup_partner", + "domain": "crazyegg.com", + "description": "OCD_ce_signup_partner_description", + "retention": "OCD_retention_1_year", + "dataController": "Crazy Egg", + "gdprUrl": "https://www.crazyegg.com/cookies", + "wildcard": "0" + } + ], + "ceac": [ + { + "platform": "Crazy Egg", + "category": "Functional", + "name": "ceac", + "domain": "crazyegg.com", + "description": "OCD_ceac_description", + "retention": "OCD_retention_1_year", + "dataController": "Crazy Egg", + "gdprUrl": "https://www.crazyegg.com/cookies", + "wildcard": "0" + } + ], + "cean": [ + { + "platform": "Crazy Egg", + "category": "Functional", + "name": "cean", + "domain": "crazyegg.com", + "description": "OCD_cean_description", + "retention": "OCD_retention_1_month", + "dataController": "Crazy Egg", + "gdprUrl": "https://www.crazyegg.com/cookies", + "wildcard": "0" + } + ], + "cehc": [ + { + "platform": "Crazy Egg", + "category": "Functional", + "name": "cehc", + "domain": "crazyegg.com", + "description": "OCD_cehc_description", + "retention": "OCD_retention_Session", + "dataController": "Crazy Egg", + "gdprUrl": "https://www.crazyegg.com/cookies", + "wildcard": "0" + } + ], + "celi": [ + { + "platform": "Crazy Egg", + "category": "Functional", + "name": "celi", + "domain": "crazyegg.com", + "description": "OCD_celi_description", + "retention": "OCD_retention_Session", + "dataController": "Crazy Egg", + "gdprUrl": "https://www.crazyegg.com/cookies", + "wildcard": "0" + } + ], + "cean_assoc": [ + { + "platform": "Crazy Egg", + "category": "Functional", + "name": "cean_assoc", + "domain": "crazyegg.com", + "description": "OCD_cean_assoc_description", + "retention": "OCD_retention_1_month", + "dataController": "Crazy Egg", + "gdprUrl": "https://www.crazyegg.com/cookies", + "wildcard": "0" + } + ], + "first_snapshot_url": [ + { + "platform": "Crazy Egg", + "category": "Functional", + "name": "first_snapshot_url", + "domain": "crazyegg.com", + "description": "OCD_first_snapshot_url_description", + "retention": "OCD_retention_1_year", + "dataController": "Crazy Egg", + "gdprUrl": "https://www.crazyegg.com/cookies", + "wildcard": "0" + } + ], + "sharing_*": [ + { + "platform": "Crazy Egg", + "category": "Functional", + "name": "sharing_*", + "domain": "crazyegg.com", + "description": "OCD_sharing___description", + "retention": "OCD_retention_30_minutes", + "dataController": "Crazy Egg", + "gdprUrl": "https://www.crazyegg.com/cookies", + "wildcard": "1" + } + ], + "ce_sid": [ + { + "platform": "Crazy Egg", + "category": "Functional", + "name": "ce_sid", + "domain": "crazyegg.com", + "description": "OCD_ce_sid_description", + "retention": "OCD_retention_Session", + "dataController": "Crazy Egg", + "gdprUrl": "https://www.crazyegg.com/cookies", + "wildcard": "0" + } + ], + "cecu": [ + { + "platform": "Crazy Egg", + "category": "Functional", + "name": "cecu", + "domain": "crazyegg.com", + "description": "OCD_cecu_description", + "retention": "OCD_retention_Session", + "dataController": "Crazy Egg", + "gdprUrl": "https://www.crazyegg.com/cookies", + "wildcard": "0" + } + ], + "TiPMix": [ + { + "platform": "Lightbox CDN", + "category": "Functional", + "name": "TiPMix", + "domain": "api.lightboxcdn.com", + "description": "OCD_TiPMix_description", + "retention": "OCD_retention_0_day", + "dataController": "Lightbox", + "gdprUrl": "https://www.lightboxcdn.com/privacy-policy/", + "wildcard": "0" + } + ], + "x-ms-routing-name": [ + { + "platform": "Lightbox CDN", + "category": "Functional", + "name": "x-ms-routing-name", + "domain": "api.lightboxcdn.com", + "description": "OCD_x_ms_routing_name_description", + "retention": "OCD_retention_0_day", + "dataController": "Lightbox", + "gdprUrl": "https://www.lightboxcdn.com/privacy-policy/", + "wildcard": "0" + } + ], + "_distillery": [ + { + "platform": "Wistia", + "category": "Functional", + "name": "_distillery", + "domain": "", + "description": "OCD__distillery_description", + "retention": "OCD_retention_1_Year", + "dataController": "Wistia", + "gdprUrl": "https://wistia.com/privacy", + "wildcard": "0" + } + ], + "muxData": [ + { + "platform": "Wistia", + "category": "Functional", + "name": "muxData", + "domain": "", + "description": "OCD_muxData_description", + "retention": "OCD_retention_1_Year", + "dataController": "Wistia", + "gdprUrl": "https://wistia.com/privacy", + "wildcard": "0" + } + ], + "wistia-http2-push-disabled": [ + { + "platform": "Wistia", + "category": "Functional", + "name": "wistia-http2-push-disabled", + "domain": "", + "description": "OCD_wistia_http2_push_disabled_description", + "retention": "OCD_retention_2_Weeks", + "dataController": "Wistia", + "gdprUrl": "https://wistia.com/privacy", + "wildcard": "0" + } + ], + "_simplex": [ + { + "platform": "Wistia", + "category": "Functional", + "name": "_simplex", + "domain": "", + "description": "OCD__simplex_description", + "retention": "OCD_retention_1_Year", + "dataController": "Wistia", + "gdprUrl": "https://wistia.com/privacy", + "wildcard": "0" + } + ], + "wistia": [ + { + "platform": "Wistia", + "category": "Functional", + "name": "wistia", + "domain": "", + "description": "OCD_wistia_description", + "retention": "OCD_retention_Session", + "dataController": "Wistia", + "gdprUrl": "https://wistia.com/privacy", + "wildcard": "0" + } + ], + "wistia-video-progress-*": [ + { + "platform": "Wistia", + "category": "Functional", + "name": "wistia-video-progress-*", + "domain": "", + "description": "OCD_wistia_video_progress___description", + "retention": "OCD_retention_Session", + "dataController": "Wistia", + "gdprUrl": "https://wistia.com/privacy", + "wildcard": "1" + } + ], + "__uin_rh": [ + { + "platform": "Sonobi", + "category": "Marketing", + "name": "__uin_rh", + "domain": "go.sonobi.com", + "description": "OCD___uin_rh_description", + "retention": "OCD_retention_364_Days", + "dataController": "Sonobi", + "gdprUrl": "https://sonobi.com/privacy-policy/", + "wildcard": "0" + } + ], + "__uir_rh": [ + { + "platform": "Sonobi", + "category": "Marketing", + "name": "__uir_rh", + "domain": "go.sonobi.com", + "description": "OCD___uir_rh_description", + "retention": "OCD_retention_32_Days", + "dataController": "Sonobi", + "gdprUrl": "https://sonobi.com/privacy-policy/", + "wildcard": "0" + } + ], + "HAPLB3A": [ + { + "platform": "Sonobi", + "category": "Marketing", + "name": "HAPLB3A", + "domain": "go.sonobi.com", + "description": "OCD_HAPLB3A_description", + "retention": "OCD_retention_Session", + "dataController": "Sonobi", + "gdprUrl": "https://sonobi.com/privacy-policy/", + "wildcard": "0" + } + ], + "SSPR_*": [ + { + "platform": "Adkernel", + "category": "Marketing", + "name": "SSPR_*", + "domain": ".adkernel.com", + "description": "OCD_SSPR___description", + "retention": "OCD_retention_Session", + "dataController": "AdKernel", + "gdprUrl": "https://adkernel.com/privacy-policy/", + "wildcard": "0" + } + ], + "SSPZ": [ + { + "platform": "Adkernel", + "category": "Marketing", + "name": "SSPZ", + "domain": ".adkernel.com", + "description": "OCD_SSPZ_description", + "retention": "OCD_retention_Session", + "dataController": "AdKernel", + "gdprUrl": "https://adkernel.com/privacy-policy/", + "wildcard": "0" + } + ], + "DSP2F_*": [ + { + "platform": "Adkernel", + "category": "Marketing", + "name": "DSP2F_*", + "domain": ".adkernel.com", + "description": "OCD_DSP2F___description", + "retention": "OCD_retention_Session", + "dataController": "AdKernel", + "gdprUrl": "https://adkernel.com/privacy-policy/", + "wildcard": "0" + } + ], + "ADKUID": [ + { + "platform": "Adkernel", + "category": "Marketing", + "name": "ADKUID", + "domain": ".adkernel.com", + "description": "OCD_ADKUID_description", + "retention": "OCD_retention_30_Days", + "dataController": "AdKernel", + "gdprUrl": "https://adkernel.com/privacy-policy/", + "wildcard": "0" + } + ], + "SCM": [ + { + "platform": "Smaato", + "category": "Marketing", + "name": "SCM", + "domain": ".smaato.net", + "description": "OCD_SCM_description", + "retention": "OCD_retention_3_weeks", + "dataController": "Smaato", + "gdprUrl": "https://www.smaato.com/privacy/", + "wildcard": "0" + } + ], + "SCMaps": [ + { + "platform": "Smaato", + "category": "Marketing", + "name": "SCMaps", + "domain": ".smaato.net", + "description": "OCD_SCMaps_description", + "retention": "OCD_retention_3_weeks", + "dataController": "Smaato", + "gdprUrl": "https://www.smaato.com/privacy/", + "wildcard": "0" + } + ], + "SCMsovrn": [ + { + "platform": "Smaato", + "category": "Marketing", + "name": "SCMsovrn", + "domain": ".smaato.net", + "description": "OCD_SCMsovrn_description", + "retention": "OCD_retention_3_weeks", + "dataController": "Smaato", + "gdprUrl": "https://www.smaato.com/privacy/", + "wildcard": "0" + } + ], + "SCMinf": [ + { + "platform": "Smaato", + "category": "Marketing", + "name": "SCMinf", + "domain": ".smaato.net", + "description": "OCD_SCMinf_description", + "retention": "OCD_retention_3_weeks", + "dataController": "Smaato", + "gdprUrl": "https://www.smaato.com/privacy/", + "wildcard": "0" + } + ], + "SCMo": [ + { + "platform": "Smaato", + "category": "Marketing", + "name": "SCMo", + "domain": ".smaato.net", + "description": "OCD_SCMo_description", + "retention": "OCD_retention_3_weeks", + "dataController": "Smaato", + "gdprUrl": "https://www.smaato.com/privacy/", + "wildcard": "0" + } + ], + "SCMg": [ + { + "platform": "Smaato", + "category": "Marketing", + "name": "SCMg", + "domain": ".smaato.net", + "description": "OCD_SCMg_description", + "retention": "OCD_retention_3_weeks", + "dataController": "Smaato", + "gdprUrl": "https://www.smaato.com/privacy/", + "wildcard": "0" + } + ], + "SCM*": [ + { + "platform": "Smaato", + "category": "Marketing", + "name": "SCM*", + "domain": ".smaato.net", + "description": "OCD_SCM__description", + "retention": "OCD_retention_3_weeks", + "dataController": "Smaato", + "gdprUrl": "https://www.smaato.com/privacy/", + "wildcard": "0" + } + ], + "UID_EXT_*": [ + { + "platform": "Undertone", + "category": "Marketing", + "name": "UID_EXT_*", + "domain": ".undertone.com", + "description": "OCD_UID_EXT___description", + "retention": "OCD_retention_1_year", + "dataController": "Undertone", + "gdprUrl": "https://www.undertone.com/cookies-policy/", + "wildcard": "0" + } + ], + "UTID": [ + { + "platform": "Undertone", + "category": "Marketing", + "name": "UTID", + "domain": ".undertone.com", + "description": "OCD_UTID_description", + "retention": "OCD_retention_1_year", + "dataController": "Undertone", + "gdprUrl": "https://www.undertone.com/cookies-policy/", + "wildcard": "0" + } + ], + "UTID_ENC": [ + { + "platform": "Undertone", + "category": "Marketing", + "name": "UTID_ENC", + "domain": ".undertone.com", + "description": "OCD_UTID_ENC_description", + "retention": "OCD_retention_1_year", + "dataController": "Undertone", + "gdprUrl": "https://www.undertone.com/cookies-policy/", + "wildcard": "0" + } + ], + "DYID": [ + { + "platform": "Dynamic Yield", + "category": "Functional", + "name": "DYID", + "domain": "dynamicyield.com", + "description": "OCD_DYID_description", + "retention": "OCD_retention_1_Year", + "dataController": "Dynamic Yield", + "gdprUrl": "https://www.dynamicyield.com/gdpr-and-privacy/", + "wildcard": "0" + } + ], + "_dyid": [ + { + "platform": "Dynamic Yield", + "category": "Functional", + "name": "_dyid", + "domain": "", + "description": "OCD__dyid_description", + "retention": "OCD_retention_30_Days", + "dataController": "Dynamic Yield", + "gdprUrl": "https://www.dynamicyield.com/gdpr-and-privacy/", + "wildcard": "0" + } + ], + "_dyid_server": [ + { + "platform": "Dynamic Yield", + "category": "Functional", + "name": "_dyid_server", + "domain": "", + "description": "OCD__dyid_server_description", + "retention": "OCD_retention_1_Year", + "dataController": "Dynamic Yield", + "gdprUrl": "https://www.dynamicyield.com/gdpr-and-privacy/", + "wildcard": "0" + } + ], + "DYSES": [ + { + "platform": "Dynamic Yield", + "category": "Functional", + "name": "DYSES", + "domain": "dynamicyield.com", + "description": "OCD_DYSES_description", + "retention": "OCD_retention_Session", + "dataController": "Dynamic Yield", + "gdprUrl": "https://www.dynamicyield.com/gdpr-and-privacy/", + "wildcard": "0" + } + ], + "_dyjsession": [ + { + "platform": "Dynamic Yield", + "category": "Functional", + "name": "_dyjsession", + "domain": "", + "description": "OCD__dyjsession_description", + "retention": "OCD_retention_Session", + "dataController": "Dynamic Yield", + "gdprUrl": "https://www.dynamicyield.com/gdpr-and-privacy/", + "wildcard": "0" + } + ], + "_dy_csc_ses": [ + { + "platform": "Dynamic Yield", + "category": "Analytics", + "name": "_dy_csc_ses", + "domain": "", + "description": "OCD__dy_csc_ses_description", + "retention": "OCD_retention_Session", + "dataController": "Dynamic Yield", + "gdprUrl": "https://www.dynamicyield.com/gdpr-and-privacy/", + "wildcard": "0" + } + ], + "_dycmc": [ + { + "platform": "Dynamic Yield", + "category": "Analytics", + "name": "_dycmc", + "domain": "", + "description": "OCD__dycmc_description", + "retention": "OCD_retention_30_Days", + "dataController": "Dynamic Yield", + "gdprUrl": "https://www.dynamicyield.com/gdpr-and-privacy/", + "wildcard": "0" + } + ], + "_dycnst": [ + { + "platform": "Dynamic Yield", + "category": "Functional", + "name": "_dycnst", + "domain": "", + "description": "OCD__dycnst_description", + "retention": "OCD_retention_30_Days", + "dataController": "Dynamic Yield", + "gdprUrl": "https://www.dynamicyield.com/gdpr-and-privacy/", + "wildcard": "0" + } + ], + "_dy_lu_ses": [ + { + "platform": "Dynamic Yield", + "category": "Functional", + "name": "_dy_lu_ses", + "domain": "", + "description": "OCD__dy_lu_ses_description", + "retention": "OCD_retention_Session", + "dataController": "Dynamic Yield", + "gdprUrl": "https://www.dynamicyield.com/gdpr-and-privacy/", + "wildcard": "0" + } + ], + "_dy_df_geo": [ + { + "platform": "Dynamic Yield", + "category": "Analytics", + "name": "_dy_df_geo", + "domain": "", + "description": "OCD__dy_df_geo_description", + "retention": "OCD_retention_30_Days", + "dataController": "Dynamic Yield", + "gdprUrl": "https://www.dynamicyield.com/gdpr-and-privacy/", + "wildcard": "0" + } + ], + "_dy_geo": [ + { + "platform": "Dynamic Yield", + "category": "Analytics", + "name": "_dy_geo", + "domain": "", + "description": "OCD__dy_geo_description", + "retention": "OCD_retention_30_Days", + "dataController": "Dynamic Yield", + "gdprUrl": "https://www.dynamicyield.com/gdpr-and-privacy/", + "wildcard": "0" + } + ], + "_dycst": [ + { + "platform": "Dynamic Yield", + "category": "Analytics", + "name": "_dycst", + "domain": "", + "description": "OCD__dycst_description", + "retention": "OCD_retention_30_Days", + "dataController": "Dynamic Yield", + "gdprUrl": "https://www.dynamicyield.com/gdpr-and-privacy/", + "wildcard": "0" + } + ], + "_dy_ses_load_seq": [ + { + "platform": "Dynamic Yield", + "category": "Functional", + "name": "_dy_ses_load_seq", + "domain": "", + "description": "OCD__dy_ses_load_seq_description", + "retention": "OCD_retention_30_Days", + "dataController": "Dynamic Yield", + "gdprUrl": "https://www.dynamicyield.com/gdpr-and-privacy/", + "wildcard": "0" + } + ], + "_dy_soct": [ + { + "platform": "Dynamic Yield", + "category": "Functional", + "name": "_dy_soct", + "domain": "", + "description": "OCD__dy_soct_description", + "retention": "OCD_retention_1_Year", + "dataController": "Dynamic Yield", + "gdprUrl": "https://www.dynamicyield.com/gdpr-and-privacy/", + "wildcard": "0" + } + ], + "_dy_toffset": [ + { + "platform": "Dynamic Yield", + "category": "Functional", + "name": "_dy_toffset", + "domain": "", + "description": "OCD__dy_toffset_description", + "retention": "OCD_retention_30_Days", + "dataController": "Dynamic Yield", + "gdprUrl": "https://www.dynamicyield.com/gdpr-and-privacy/", + "wildcard": "0" + } + ], + "dy_fs_page": [ + { + "platform": "Dynamic Yield", + "category": "Functional", + "name": "dy_fs_page", + "domain": "", + "description": "OCD_dy_fs_page_description", + "retention": "OCD_retention_Session", + "dataController": "Dynamic Yield", + "gdprUrl": "https://www.dynamicyield.com/gdpr-and-privacy/", + "wildcard": "0" + } + ], + "_dy_cs_storage_items": [ + { + "platform": "Dynamic Yield", + "category": "Marketing", + "name": "_dy_cs_storage_items", + "domain": "", + "description": "OCD__dy_cs_storage_items_description", + "retention": "OCD_retention_30_Days", + "dataController": "Dynamic Yield", + "gdprUrl": "https://www.dynamicyield.com/gdpr-and-privacy/", + "wildcard": "0" + } + ], + "_dy_cs_cookie_items": [ + { + "platform": "Dynamic Yield", + "category": "Marketing", + "name": "_dy_cs_cookie_items", + "domain": "", + "description": "OCD__dy_cs_cookie_items_description", + "retention": "OCD_retention_30_Days", + "dataController": "Dynamic Yield", + "gdprUrl": "https://www.dynamicyield.com/gdpr-and-privacy/", + "wildcard": "0" + } + ], + "ckid": [ + { + "platform": "Blue", + "category": "Marketing", + "name": "ckid", + "domain": ".getblue.io", + "description": "OCD_ckid_description", + "retention": "OCD_retention_1_Year", + "dataController": "Blue", + "gdprUrl": "https://getblue.io/privacy/en/", + "wildcard": "0" + } + ], + "hash": [ + { + "platform": "Blue", + "category": "Marketing", + "name": "hash", + "domain": ".getblue.io", + "description": "OCD_hash_description", + "retention": "OCD_retention_1_Year", + "dataController": "Blue", + "gdprUrl": "https://getblue.io/privacy/en/", + "wildcard": "0" + } + ], + "BLUEID": [ + { + "platform": "Blue", + "category": "Marketing", + "name": "BLUEID", + "domain": ".getblue.io", + "description": "OCD_BLUEID_description", + "retention": "OCD_retention_1_Year", + "dataController": "Blue", + "gdprUrl": "https://getblue.io/privacy/en/", + "wildcard": "0" + } + ], + "ig_did": [ + { + "platform": "Instagram", + "category": "Marketing", + "name": "ig_did", + "domain": "instagram.com", + "description": "OCD_ig_did_description", + "retention": "OCD_retention_9_years", + "dataController": "Meta", + "gdprUrl": "https://www.facebook.com/privacy/policies/cookies", + "wildcard": "0" + } + ], + "ig_cb": [ + { + "platform": "Instagram", + "category": "Marketing", + "name": "ig_cb", + "domain": "instagram.com", + "description": "OCD_ig_cb_description", + "retention": "OCD_retention_9_years", + "dataController": "Meta", + "gdprUrl": "https://www.facebook.com/privacy/policies/cookies", + "wildcard": "0" + } + ], + "ds_user_id": [ + { + "platform": "Instagram", + "category": "Marketing", + "name": "ds_user_id", + "domain": "instagram.com", + "description": "OCD_ds_user_id_description", + "retention": "OCD_retention_3_months", + "dataController": "Meta", + "gdprUrl": "https://www.facebook.com/privacy/policies/cookies", + "wildcard": "0" + } + ], + "mid": [ + { + "platform": "Instagram", + "category": "Functional", + "name": "mid", + "domain": "instagram.com", + "description": "OCD_mid_description", + "retention": "OCD_retention_9_years", + "dataController": "Meta", + "gdprUrl": "https://www.facebook.com/privacy/policies/cookies", + "wildcard": "0" + } + ], + "fbm_*": [ + { + "platform": "Instagram", + "category": "Marketing", + "name": "fbm_*", + "domain": "instagram.com", + "description": "OCD_fbm___description", + "retention": "OCD_retention_1_year", + "dataController": "Meta", + "gdprUrl": "https://www.facebook.com/privacy/policies/cookies", + "wildcard": "1" + } + ], + "shbid": [ + { + "platform": "Instagram", + "category": "Marketing", + "name": "shbid", + "domain": "instagram.com", + "description": "OCD_shbid_description", + "retention": "OCD_retention_1_year", + "dataController": "Meta", + "gdprUrl": "https://www.facebook.com/privacy/policies/cookies", + "wildcard": "0" + } + ], + "shbts": [ + { + "platform": "Instagram", + "category": "Marketing", + "name": "shbts", + "domain": "instagram.com", + "description": "OCD_shbts_description", + "retention": "OCD_retention_1_year", + "dataController": "Meta", + "gdprUrl": "https://www.facebook.com/privacy/policies/cookies", + "wildcard": "0" + } + ], + "sessionid": [ + { + "platform": "Instagram", + "category": "Marketing", + "name": "sessionid", + "domain": "instagram.com", + "description": "OCD_sessionid_description", + "retention": "OCD_retention_1_year", + "dataController": "Meta", + "gdprUrl": "https://www.facebook.com/privacy/policies/cookies", + "wildcard": "0" + } + ], + "_parsely_visitor": [ + { + "platform": "Parse.ly", + "category": "Functional", + "name": "_parsely_visitor", + "domain": "", + "description": "OCD__parsely_visitor_description", + "retention": "OCD_retention_13_months", + "dataController": "Parse.ly", + "gdprUrl": "https://docs.parse.ly/privacy/", + "wildcard": "0" + } + ], + "_parsely_tpa_blocked": [ + { + "platform": "Parse.ly", + "category": "Functional", + "name": "_parsely_tpa_blocked", + "domain": "", + "description": "OCD__parsely_tpa_blocked_description", + "retention": "OCD_retention_12_hours", + "dataController": "Parse.ly", + "gdprUrl": "https://docs.parse.ly/privacy/", + "wildcard": "0" + } + ], + "_parsely_slot_click": [ + { + "platform": "Parse.ly", + "category": "Functional", + "name": "_parsely_slot_click", + "domain": "", + "description": "OCD__parsely_slot_click_description", + "retention": "OCD_retention_Session", + "dataController": "Parse.ly", + "gdprUrl": "https://docs.parse.ly/privacy/", + "wildcard": "0" + } + ], + "_parsely_session": [ + { + "platform": "Parse.ly", + "category": "Functional", + "name": "_parsely_session", + "domain": "", + "description": "OCD__parsely_session_description", + "retention": "OCD_retention_30_minutes", + "dataController": "Parse.ly", + "gdprUrl": "https://docs.parse.ly/privacy/", + "wildcard": "0" + } + ], + "test": [ + { + "platform": "Parse.ly", + "category": "Functional", + "name": "test", + "domain": "", + "description": "OCD_test_description", + "retention": "OCD_retention_Session", + "dataController": "Parse.ly", + "gdprUrl": "https://docs.parse.ly/privacy/", + "wildcard": "0" + } + ], + "__editor_layout": [ + { + "platform": "Codepen", + "category": "Functional", + "name": "__editor_layout", + "domain": "codepen.io", + "description": "OCD___editor_layout_description", + "retention": "OCD_retention_29_days", + "dataController": "Codepen", + "gdprUrl": "https://blog.codepen.io/documentation/privacy-policy/", + "wildcard": "0" + } + ], + "codepen_session": [ + { + "platform": "Codepen", + "category": "Functional", + "name": "codepen_session", + "domain": "codepen.io", + "description": "OCD_codepen_session_description", + "retention": "OCD_retention_29_days", + "dataController": "Codepen", + "gdprUrl": "https://blog.codepen.io/documentation/privacy-policy/", + "wildcard": "0" + } + ], + "codepen_signup_referrer": [ + { + "platform": "Codepen", + "category": "Functional", + "name": "codepen_signup_referrer", + "domain": "codepen.io", + "description": "OCD_codepen_signup_referrer_description", + "retention": "OCD_retention_1_year", + "dataController": "Codepen", + "gdprUrl": "https://blog.codepen.io/documentation/privacy-policy/", + "wildcard": "0" + } + ], + "codepen_signup_referrer_date": [ + { + "platform": "Codepen", + "category": "Functional", + "name": "codepen_signup_referrer_date", + "domain": "codepen.io", + "description": "OCD_codepen_signup_referrer_date_description", + "retention": "OCD_retention_1_year", + "dataController": "Codepen", + "gdprUrl": "https://blog.codepen.io/documentation/privacy-policy/", + "wildcard": "0" + } + ], + "rl_user_id": [ + { + "platform": "Rudderstack", + "category": "Analytics", + "name": "rl_user_id", + "domain": "", + "description": "OCD_rl_user_id_description", + "retention": "OCD_retention_Session", + "dataController": "Rudderstack", + "gdprUrl": "https://www.rudderstack.com/privacy-policy/", + "wildcard": "0" + } + ], + "rl_trait": [ + { + "platform": "Rudderstack", + "category": "Analytics", + "name": "rl_trait", + "domain": "", + "description": "OCD_rl_trait_description", + "retention": "OCD_retention_Session", + "dataController": "Rudderstack", + "gdprUrl": "https://www.rudderstack.com/privacy-policy/", + "wildcard": "0" + } + ], + "rl_anonymous_id": [ + { + "platform": "Rudderstack", + "category": "Analytics", + "name": "rl_anonymous_id", + "domain": "", + "description": "OCD_rl_anonymous_id_description", + "retention": "OCD_retention_Session", + "dataController": "Rudderstack", + "gdprUrl": "https://www.rudderstack.com/privacy-policy/", + "wildcard": "0" + } + ], + "rl_group_id": [ + { + "platform": "Rudderstack", + "category": "Analytics", + "name": "rl_group_id", + "domain": "", + "description": "OCD_rl_group_id_description", + "retention": "OCD_retention_Session", + "dataController": "Rudderstack", + "gdprUrl": "https://www.rudderstack.com/privacy-policy/", + "wildcard": "0" + } + ], + "rl_group_trait": [ + { + "platform": "Rudderstack", + "category": "Analytics", + "name": "rl_group_trait", + "domain": "", + "description": "OCD_rl_group_trait_description", + "retention": "OCD_retention_Session", + "dataController": "Rudderstack", + "gdprUrl": "https://www.rudderstack.com/privacy-policy/", + "wildcard": "0" + } + ], + "rl_page_init_referrer": [ + { + "platform": "Rudderstack", + "category": "Analytics", + "name": "rl_page_init_referrer", + "domain": "", + "description": "OCD_rl_page_init_referrer_description", + "retention": "OCD_retention_Session", + "dataController": "Rudderstack", + "gdprUrl": "https://www.rudderstack.com/privacy-policy/", + "wildcard": "0" + } + ], + "rl_page_init_referring_domain": [ + { + "platform": "Rudderstack", + "category": "Analytics", + "name": "rl_page_init_referring_domain", + "domain": "", + "description": "OCD_rl_page_init_referring_domain_description", + "retention": "OCD_retention_Session", + "dataController": "Rudderstack", + "gdprUrl": "https://www.rudderstack.com/privacy-policy/", + "wildcard": "0" + } + ], + "test_rudder_cookie": [ + { + "platform": "Rudderstack", + "category": "Analytics", + "name": "test_rudder_cookie", + "domain": "", + "description": "OCD_test_rudder_cookie_description", + "retention": "OCD_retention_Session", + "dataController": "Rudderstack", + "gdprUrl": "https://www.rudderstack.com/privacy-policy/", + "wildcard": "0" + } + ], + "rl_session": [ + { + "platform": "Rudderstack", + "category": "Analytics", + "name": "rl_session", + "domain": "", + "description": "OCD_rl_session_description", + "retention": "OCD_retention_Session", + "dataController": "Rudderstack", + "gdprUrl": "https://www.rudderstack.com/privacy-policy/", + "wildcard": "0" + } + ], + "rl_auth_token": [ + { + "platform": "Rudderstack", + "category": "Analytics", + "name": "rl_auth_token", + "domain": "", + "description": "OCD_rl_auth_token_description", + "retention": "OCD_retention_Session", + "dataController": "Rudderstack", + "gdprUrl": "https://www.rudderstack.com/privacy-policy/", + "wildcard": "0" + } + ], + "_biz_uid": [ + { + "platform": "Marketo", + "category": "Analytics", + "name": "_biz_uid", + "domain": "", + "description": "OCD__biz_uid_description", + "retention": "OCD_retention_1_year", + "dataController": "Adobe", + "gdprUrl": "https://www.adobe.com/privacy.html", + "wildcard": "0" + } + ], + "_biz_nA": [ + { + "platform": "Marketo", + "category": "Analytics", + "name": "_biz_nA", + "domain": "", + "description": "OCD__biz_nA_description", + "retention": "OCD_retention_1_year", + "dataController": "Adobe", + "gdprUrl": "https://www.adobe.com/privacy.html", + "wildcard": "0" + } + ], + "_biz_flagsA": [ + { + "platform": "Marketo", + "category": "Analytics", + "name": "_biz_flagsA", + "domain": "", + "description": "OCD__biz_flagsA_description", + "retention": "OCD_retention_1_year", + "dataController": "Adobe", + "gdprUrl": "https://www.adobe.com/privacy.html", + "wildcard": "0" + } + ], + "_biz_pendingA": [ + { + "platform": "Marketo", + "category": "Analytics", + "name": "_biz_pendingA", + "domain": "", + "description": "OCD__biz_pendingA_description", + "retention": "OCD_retention_1_year", + "dataController": "Adobe", + "gdprUrl": "https://www.adobe.com/privacy.html", + "wildcard": "0" + } + ], + "_biz_ABTestA": [ + { + "platform": "Marketo", + "category": "Analytics", + "name": "_biz_ABTestA", + "domain": "", + "description": "OCD__biz_ABTestA_description", + "retention": "OCD_retention_1_year", + "dataController": "Adobe", + "gdprUrl": "https://www.adobe.com/privacy.html", + "wildcard": "0" + } + ], + "_biz_su": [ + { + "platform": "Marketo", + "category": "Analytics", + "name": "_biz_su", + "domain": "", + "description": "OCD__biz_su_description", + "retention": "OCD_retention_1_year", + "dataController": "Adobe", + "gdprUrl": "https://www.adobe.com/privacy.html", + "wildcard": "0" + } + ], + "_biz_EventA": [ + { + "platform": "Marketo", + "category": "Analytics", + "name": "_biz_EventA", + "domain": "", + "description": "OCD__biz_EventA_description", + "retention": "OCD_retention_1_year", + "dataController": "Adobe", + "gdprUrl": "https://www.adobe.com/privacy.html", + "wildcard": "0" + } + ], + "g": [ + { + "platform": "CreativeCDN", + "category": "Marketing", + "name": "g", + "domain": "creativecdn.com", + "description": "OCD_g_description", + "retention": "OCD_retention_364_days", + "dataController": "RTB House", + "gdprUrl": "https://rtbhouse.com/privacy-center", + "wildcard": "0" + } ] -} \ No newline at end of file +} diff --git a/bin/chrome-3pcd-ps.bat b/bin/chrome-3pcd-ps.bat index ce9e68edb..89196f567 100644 --- a/bin/chrome-3pcd-ps.bat +++ b/bin/chrome-3pcd-ps.bat @@ -1,7 +1,7 @@ :: Chrome 3pcd with PS Extension :: Download PS Extension -set "ps_analysis_tool_version=v0.8.0" +set "ps_analysis_tool_version=v0.9.0" cd /d %TEMP% if not exist %TEMP%\ps-analysis-tool-%ps_analysis_tool_version% ( mkdir %TEMP%\ps-analysis-tool-%ps_analysis_tool_version% diff --git a/bin/chrome-default-ps.bat b/bin/chrome-default-ps.bat index 81d33a468..79e44061d 100644 --- a/bin/chrome-default-ps.bat +++ b/bin/chrome-default-ps.bat @@ -1,7 +1,7 @@ :: Default Chrome with PS Extension :: Download PS Extension -set "ps_analysis_tool_version=v0.8.0" +set "ps_analysis_tool_version=v0.9.0" cd /d %TEMP% if not exist %TEMP%\ps-analysis-tool-%ps_analysis_tool_version% ( mkdir %TEMP%\ps-analysis-tool-%ps_analysis_tool_version% diff --git a/bin/chrome_launcher.sh b/bin/chrome_launcher.sh index 3e957010e..03d666c2f 100644 --- a/bin/chrome_launcher.sh +++ b/bin/chrome_launcher.sh @@ -2,7 +2,7 @@ # Download Extension extension_setup() { - ps_analysis_tool_version=v0.8.0 + ps_analysis_tool_version=v0.9.0 extension_dir="/var/tmp" cd $extension_dir if [ ! -d $extension_dir/ps-analysis-tool-$ps_analysis_tool_version ]; then diff --git a/bin/setup-local-registry.sh b/bin/setup-local-registry.sh new file mode 100644 index 000000000..09e1ee1e8 --- /dev/null +++ b/bin/setup-local-registry.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +# +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Echo every command being executed +set -ex + +registry_url=http://localhost:4873 + +echo "Starting up local npm registry..." + +# Start local registry. +tmp_registry_log=`mktemp` + +echo "Registry output file: $tmp_registry_log" + +curdir=$(dirname "$(realpath $0)") + +(cd && nohup npx verdaccio --config "$curdir/verdaccio-config.yml" &>$tmp_registry_log &) + +npm i --global verdaccio-memory + +# Wait for Verdaccio to boot. +grep -q 'http address' <(tail -f $tmp_registry_log) + +echo "Local registry up and running! ${registry_url}" + +# Set registry to local registry +export NPM_CONFIG_REGISTRY="$registry_url" + +echo "Logging in..." + +# Log in to Verdaccio so we can publish packages +npx npm-cli-login -u admin -p password -e test@example.com -r $registry_url \ No newline at end of file diff --git a/bin/stop-local-registry.sh b/bin/stop-local-registry.sh new file mode 100644 index 000000000..7cd770883 --- /dev/null +++ b/bin/stop-local-registry.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +# +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +lsof -nti:4873 | xargs kill -9 + +unset NPM_CONFIG_REGISTRY \ No newline at end of file diff --git a/bin/verdaccio-config.yml b/bin/verdaccio-config.yml new file mode 100644 index 000000000..79f05971d --- /dev/null +++ b/bin/verdaccio-config.yml @@ -0,0 +1,63 @@ +# +# This is the default config file. It allows all users to do anything, +# so don't use it on production systems. +# +# Look here for more config file examples: +# https://github.com/verdaccio/verdaccio/tree/master/conf +# + +# path to a directory with all packages +storage: /tmp/verdaccio-workspace/storage + +web: + title: Verdaccio + +auth: + htpasswd: + file: /tmp/verdaccio-workspace/htpasswd + +# a list of other known repositories we can talk to +uplinks: + npmjs: + url: https://registry.npmjs.org/ + +packages: + '@*/*': + # scoped packages + access: $all + publish: $authenticated + unpublish: $authenticated + proxy: npmjs + + '**': + # allow all users (including non-authenticated users) to read and + # publish all packages + # + # you can specify usernames/groupnames (depending on your auth plugin) + # and three keywords: "$all", "$anonymous", "$authenticated" + access: $all + + # allow all known users to publish/publish packages + # (anyone can register by default, remember?) + publish: $authenticated + unpublish: $authenticated + + # if package is not available locally, proxy requests to 'npmjs' registry + proxy: npmjs + +server: + # deprecated + keepAliveTimeout: 60 + +middlewares: + audit: + enabled: true + +# log settings +logs: + # Logger as STDOUT + { type: stdout, format: pretty, level: warn } + +# This affect the web and api (not developed yet) +i18n: + web: en-US diff --git a/cli.webpack.config.cjs b/cli.webpack.config.cjs new file mode 100644 index 000000000..fdce58896 --- /dev/null +++ b/cli.webpack.config.cjs @@ -0,0 +1,50 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const CopyPlugin = require('copy-webpack-plugin'); +const path = require('path'); +const commonConfig = require('./webpack.shared.cjs'); +const webpack = require('webpack'); +const WebpackBar = require('webpackbar'); + +module.exports = { + entry: './src/index.ts', + target: 'node', // Important for Node.js modules + externalsPresets: { node: true }, // in order to ignore built-in modules like path, fs, etc. + output: { + path: path.resolve(__dirname, './packages/cli/dist'), + filename: '[name].js', + }, + plugins: [ + new WebpackBar({ + name: 'CLI', + color: '#FF9B71', + }), + new webpack.BannerPlugin({ banner: '#!/usr/bin/env node', raw: true }), + new CopyPlugin({ + patterns: [ + { from: '../../assets', to: 'assets' }, + { from: '../../data', to: 'assets/data' }, + { + from: '../../node_modules/wappalyzer/technologies', + to: 'technologies', + }, + { from: '../../node_modules/wappalyzer/categories.json', to: '' }, + ], + }), + ], + stats: 'errors-only', + ...commonConfig, +}; diff --git a/dashboard.webpack.config.cjs b/dashboard.webpack.config.cjs index ebbe78c8e..156a68b07 100644 --- a/dashboard.webpack.config.cjs +++ b/dashboard.webpack.config.cjs @@ -22,10 +22,10 @@ const commonConfig = require('./webpack.shared.cjs'); const report = { entry: { - index: './packages/report/src/index.tsx', + index: '../report/src/index.tsx', }, output: { - path: path.resolve(__dirname, './dist/cli-dashboard/report'), + path: path.resolve(__dirname, './packages/cli-dashboard/dist/report'), filename: '[name].js', publicPath: '/', }, @@ -36,7 +36,7 @@ const report = { }), new HtmlWebpackPlugin({ title: 'Report', - template: './packages/report/public/index.html', + template: '../report/public/index.html', filename: 'index.html', inject: true, }), @@ -47,19 +47,12 @@ const report = { const dashboard = { entry: { - index: './packages/cli-dashboard/src/index.tsx', + index: './src/index.tsx', }, output: { - path: path.resolve(__dirname, './dist/cli-dashboard'), + path: path.resolve(__dirname, './packages/cli-dashboard/dist'), filename: '[name].js', }, - devServer: { - static: { - directory: path.join(__dirname, './dist/cli-dashboard'), - }, - compress: true, - port: 9000, - }, plugins: [ new WebpackBar({ name: 'Dashboard', @@ -67,12 +60,17 @@ const dashboard = { }), new HtmlWebpackPlugin({ title: 'Report', - template: './packages/cli-dashboard/public/index.html', + template: '../cli-dashboard/public/index.html', filename: 'index.html', inject: false, }), new CopyPlugin({ - patterns: [{ from: './out', to: 'out' }], + patterns: [ + { + from: '../i18n/_locales/messages', + to: './_locales/', + }, + ], }), ], ...commonConfig, diff --git a/data/PSInfo.json b/data/PSInfo.json index c226b4bca..3eb0bbb1b 100644 --- a/data/PSInfo.json +++ b/data/PSInfo.json @@ -1,7 +1,7 @@ { "private-state-token": { "name": "Private State Token", - "description": "Allows websites and services to evaluate a user's authenticity without needing to know the user's identity. With this API, a website/service can issue a batch of Private State Tokens (renamed to Private state tokens from Trust Tokens) to a user's browser. The tokens are stored on the user’s browser and can then be “redeemed” by other sites and services as a signal of the user's authenticity.", + "description": "PSPrivateStateToken", "proposal": "https://github.com/WICG/trust-token-api", "publicDiscussion": "https://github.com/WICG/trust-token-api/issues", "videoOverview": "https://www.youtube.com/watch?v=bXB1Iwq6Eq4", @@ -9,23 +9,24 @@ }, "topics": { "name": "Topics", - "description": "Provide a way for advertising to reach users based on interests inferred from the sites or apps the user visits, without needing to know the specific sites or apps the user has visited.", + "description": "PSTopics", "proposal": "https://github.com/patcg-individual-drafts/topics", "publicDiscussion": "https://github.com/patcg-individual-drafts/topics/issues", "videoOverview": "https://youtu.be/hEBzWuXjeTQ", "devDocumentation": "https://developers.google.com/privacy-sandbox/relevance/topics" }, "protected-audience": { - "name": "Protected Audience (FLEDGE)", - "description": "A proposal to serve remarketing and custom audience use cases by allowing custom defined interested groups to be stored on-device and allowing an on-device auction that then matches appropriate ads with people in a desired interest group.", + "name": "Protected Audience", + "description": "The Protected Audience API is a Privacy Sandbox technology to serve remarketing and custom audience use cases, designed so third parties cannot track user browsing behavior across sites.", + "useI18n": false, "proposal": "https://github.com/WICG/turtledove", "publicDiscussion": "https://github.com/WICG/turtledove/issues", - "videoOverview": "https://www.youtube.com/watch?v=HkvmYKqnytw", + "publicExplainer": "https://github.com/WICG/turtledove/blob/main/FLEDGE.md", "devDocumentation": "https://developers.google.com/privacy-sandbox/relevance/protected-audience" }, "attribution-reporting": { "name": "Attribution Reporting", - "description": "Allow the recording and matching of ad events with conversion events to occur on-device.
Event-level: Determine the effectiveness of specific ad interactions to help drive optimization.
Summary Reporting: Allows for more detail about the overall conversions (e.g. region, revenue, time of day, etc) that their advertising has delivered, while minimizing details about individual conversions.", + "description": "PSAttributionReporting", "proposal": "https://github.com/WICG/attribution-reporting-api", "publicDiscussion": "https://github.com/WICG/attribution-reporting-api/issues", "videoOverview": "https://www.youtube.com/watch?v=UGA74CIcom8", @@ -33,15 +34,14 @@ }, "related-website-sets": { "name": "Related Website Sets", - "description": "A new web platform mechanism that would allow a company that owns multiple sites to declare a collection of related domains as being in a Related Website Sets. Sites that are part of a Related Website Set would be able to access cookies across the set of included domains.", - "proposal": "https://github.com/WICG/first-party-sets", + "description": "PSRelatedWebsiteSets", "publicDiscussion": "https://github.com/WICG/first-party-sets/issues", "videoOverview": "https://www.youtube.com/watch?v=cNJ8mZ-J3F8", "devDocumentation": "https://developers.google.com/privacy-sandbox/3pcd/related-website-sets" }, "shared-storage": { "name": "Shared Storage", - "description": "The Shared Storage API allows sites to store and access unpartitioned cross-site data as to prevent cross-site user tracking, browsers are partitioning all forms of storage (cookies, localStorage, caches, etc). However, there are a number of legitimate use cases that rely on unpartitioned storage which would be impossible without help from new web APIs.", + "description": "PSSharedStorage", "proposal": "https://github.com/WICG/shared-storage", "publicDiscussion": "https://github.com/WICG/shared-storage/issues", "videoOverview": "", @@ -49,7 +49,7 @@ }, "chips": { "name": "Cookies Having Independent Partitioned State (CHIPS)", - "description": "A new way to enable 3rd party developers to access cookies on sites where their services are embedded on a per-site basis (meaning a different cookie on each site), restricting the ability to track users across sites.", + "description": "PSChips", "proposal": "https://github.com/privacycg/CHIPS", "publicDiscussion": "https://github.com/privacycg/CHIPS/issues", "videoOverview": "", @@ -57,7 +57,7 @@ }, "fenced-frames": { "name": "Fenced Frames", - "description": "A fenced frame () is a proposed HTML element for embedded content, similar to an iframe. Unlike iframes, a fenced frame restricts communication with its embedding context to allow the frame access to cross-site data without sharing it with the embedding context.", + "description": "PSFencedFrames", "proposal": "https://github.com/WICG/fenced-frame", "publicDiscussion": "https://github.com/WICG/fenced-frame/issues", "videoOverview": "https://www.youtube.com/watch?v=CqbluZHZofI", @@ -65,7 +65,7 @@ }, "fedcm": { "name": "Federated Credential Management(FedCM)", - "description": "FedCM is a proposal for a privacy-preserving approach to federated identity services (such as \"Sign in with...\") where users can log into sites without sharing their personal information with the identity service or the site.", + "description": "PSFedCM", "proposal": "https://github.com/fedidcg/FedCM", "publicDiscussion": "https://github.com/fedidcg/FedCM/issues", "videoOverview": "https://www.youtube.com/watch?v=rSOqIR3lzBk", @@ -73,7 +73,7 @@ }, "bounce-tracking": { "name": "Bounce Tracking Mitigation", - "description": "Bounce Tracking Mitigation is a Privacy Sandbox proposal to mitigate bounce tracking, a technique used to track users across sites by abusing the referrer header.", + "description": "PSBounceTracking", "proposal": "https://github.com/privacycg/nav-tracking-mitigations", "publicDiscussion": "https://github.com/privacycg/nav-tracking-mitigations/issues", "videoOverview": "", @@ -81,7 +81,7 @@ }, "user-agent-reduction": { "name": "User Agent Reduction", - "description": "User-Agent (UA) reduction minimizes the identifying information shared in the User-Agent string, which may be used for passive fingerprinting.", + "description": "PSUserAgentReduction", "proposal": "https://github.com/miketaylr/user-agent-reduction/", "publicDiscussion": "https://github.com/miketaylr/user-agent-reduction/issues", "videoOverview": "", diff --git a/data/related_website_sets.json b/data/related_website_sets.json index e2238d035..b1c06ce78 100644 --- a/data/related_website_sets.json +++ b/data/related_website_sets.json @@ -1,5 +1,15 @@ { "sets": [ + { + "contact": "oterrell28@gmail.com", + "primary": "https://sackrace.ai", + "serviceSites": [ + "https://socket-to-me.vip" + ], + "rationaleBySite": { + "https://socket-to-me.vip": "RWS_rationale_https___socket_to_me_vip" + } + }, { "contact": "il.mrbit@gmail.com", "primary": "https://poalim.xyz", @@ -7,7 +17,7 @@ "https://poalim.site" ], "rationaleBySite": { - "https://poalim.site": "one of 3rd party cookie solutions option" + "https://poalim.site": "RWS_rationale_https___poalim_site" } }, { @@ -18,8 +28,8 @@ "https://elfinancierocr.com" ], "rationaleBySite": { - "https://lateja.cr": "Newspaper owned by Grupo Nación", - "https://elfinancierocr.com": "Newspaper owned by Grupo Nación" + "https://lateja.cr": "RWS_rationale_https___lateja_cr", + "https://elfinancierocr.com": "RWS_rationale_https___elfinancierocr_com" } }, { @@ -33,11 +43,11 @@ "https://wpext.pl" ], "rationaleBySite": { - "https://o2.pl": "News portal and email service", - "https://pudelek.pl": "Special Interest Website - celebrity news and gossip", - "https://money.pl": "Special Interest Website - news and information on the stock market, currencies, business and economics", - "https://abczdrowie.pl": "Special Interest Website - healthy living and medical health", - "https://wpext.pl": "Content delivery domain for sensitive content websites" + "https://o2.pl": "RWS_rationale_https___o2_pl", + "https://pudelek.pl": "RWS_rationale_https___pudelek_pl", + "https://money.pl": "RWS_rationale_https___money_pl", + "https://abczdrowie.pl": "RWS_rationale_https___abczdrowie_pl", + "https://wpext.pl": "RWS_rationale_https___wpext_pl" } }, { @@ -48,8 +58,8 @@ "https://nourishingpursuits.com" ], "rationaleBySite": { - "https://cardsayings.net": "Co-branded content site", - "https://nourishingpursuits.com": "Co-branded content site" + "https://cardsayings.net": "RWS_rationale_https___cardsayings_net", + "https://nourishingpursuits.com": "RWS_rationale_https___nourishingpursuits_com" } }, { @@ -65,11 +75,11 @@ "https://www.asadcdn.com" ], "rationaleBySite": { - "https://welt.de": "News Website welt.de", - "https://autobild.de": "Special Interest Website Autobild", - "https://computerbild.de": "Special Interest Website Computerbild", - "https://wieistmeineip.de": "Internet speed Measurement Website of Computerbild", - "https://www.asadcdn.com": "CDN for Ad Files - Frequency Capping" + "https://welt.de": "RWS_rationale_https___welt_de", + "https://autobild.de": "RWS_rationale_https___autobild_de", + "https://computerbild.de": "RWS_rationale_https___computerbild_de", + "https://wieistmeineip.de": "RWS_rationale_https___wieistmeineip_de", + "https://www.asadcdn.com": "RWS_rationale_https___www_asadcdn_com" } }, { @@ -81,9 +91,9 @@ "https://salemovetravel.com" ], "rationaleBySite": { - "https://salemoveadvisor.com": "The domains are very clear that they are all a part of the SaleMove family of sites. These sites are used as demo sites to show how multiple sites can be linked together for the purpose of live chat linking across multiple domains. A user would expect the sites to be linked as they are all a part of the same demo family of sites to show how it works", - "https://salemovefinancial.com": "The domains are very clear that they are all a part of the SaleMove family of sites. These sites are used as demo sites to show how multiple sites can be linked together for the purpose of live chat linking across multiple domains. A user would expect the sites to be linked as they are all a part of the same demo family of sites to show how it works", - "https://salemovetravel.com": "The domains are very clear that they are all a part of the SaleMove family of sites. These sites are used as demo sites to show how multiple sites can be linked together for the purpose of live chat linking across multiple domains. A user would expect the sites to be linked as they are all a part of the same demo family of sites to show how it works" + "https://salemoveadvisor.com": "RWS_rationale_https___salemoveadvisor_com", + "https://salemovefinancial.com": "RWS_rationale_https___salemovefinancial_com", + "https://salemovetravel.com": "RWS_rationale_https___salemovetravel_com" } }, { @@ -94,8 +104,8 @@ "https://mystudentdashboard.com" ], "rationaleBySite": { - "https://teacherdashboard.com": "Portal for Hapara teachers", - "https://mystudentdashboard.com": "Portal for Hapara students" + "https://teacherdashboard.com": "RWS_rationale_https___teacherdashboard_com", + "https://mystudentdashboard.com": "RWS_rationale_https___mystudentdashboard_com" } }, { @@ -105,7 +115,7 @@ "https://songshare.com" ], "rationaleBySite": { - "https://songshare.com": "Specialized Platform for Music Smart Links" + "https://songshare.com": "RWS_rationale_https___songshare_com" } }, { @@ -119,11 +129,11 @@ "https://desimartini.com" ], "rationaleBySite": { - "https://livemint.com": "https://livemint.com", - "https://livehindustan.com": "https://livehindustan.com", - "https://healthshots.com": "https://healthshots.com", - "https://ottplay.com": "https://ottplay.com", - "https://desimartini.com": "https://desimartini.com" + "https://livemint.com": "RWS_rationale_https___livemint_com", + "https://livehindustan.com": "RWS_rationale_https___livehindustan_com", + "https://healthshots.com": "RWS_rationale_https___healthshots_com", + "https://ottplay.com": "RWS_rationale_https___ottplay_com", + "https://desimartini.com": "RWS_rationale_https___desimartini_com" } }, { @@ -133,7 +143,7 @@ "https://landyrev.ru" ], "rationaleBySite": { - "https://landyrev.ru": "Same publisher's website in a different region" + "https://landyrev.ru": "RWS_rationale_https___landyrev_ru" } }, { @@ -144,8 +154,8 @@ "https://punjabijagran.com" ], "rationaleBySite": { - "https://gujaratijagran.com": "News Website for Gujarati audience", - "https://punjabijagran.com": "News Website for Punjabi audience" + "https://gujaratijagran.com": "RWS_rationale_https___gujaratijagran_com", + "https://punjabijagran.com": "RWS_rationale_https___punjabijagran_com" } }, { @@ -159,11 +169,11 @@ "https://tucarro.com" ], "rationaleBySite": { - "https://mercadolivre.com": "Mercado Libre in Brazil", - "https://mercadopago.com": "Mercado Libre's payments ecosystem", - "https://mercadoshops.com": "Mercado Libre's online ecommerce store builder", - "https://portalinmobiliario.com": "Mercado Libre's brand for real estate ecommerce in Chile", - "https://tucarro.com": "Mercado Libre's brand for motors ecommerce in Colombia and Venezuela" + "https://mercadolivre.com": "RWS_rationale_https___mercadolivre_com", + "https://mercadopago.com": "RWS_rationale_https___mercadopago_com", + "https://mercadoshops.com": "RWS_rationale_https___mercadoshops_com", + "https://portalinmobiliario.com": "RWS_rationale_https___portalinmobiliario_com", + "https://tucarro.com": "RWS_rationale_https___tucarro_com" }, "ccTLDs": { "https://mercadolibre.com": [ @@ -220,8 +230,8 @@ "https://cookreactor.com" ], "rationaleBySite": { - "https://reactor.cc": "Domain for fandomes, old version of site and mirror", - "https://cookreactor.com": "Cooking fandome" + "https://reactor.cc": "RWS_rationale_https___reactor_cc", + "https://cookreactor.com": "RWS_rationale_https___cookreactor_com" }, "ccTLDs": { "https://joyreactor.cc": [ @@ -239,8 +249,8 @@ "https://cmxd.com.mx" ], "rationaleBySite": { - "https://clarosports.com": "Member of AMX Contenido websites, as unotv", - "https://cmxd.com.mx": "Used for personalization purposes across AMX Contenido websites." + "https://clarosports.com": "RWS_rationale_https___clarosports_com", + "https://cmxd.com.mx": "RWS_rationale_https___cmxd_com_mx" } }, { @@ -254,11 +264,11 @@ "https://eworkbookrequest.com" ], "rationaleBySite": { - "https://idbs-dev.com": "Dev version of main application IDBS-cloud", - "https://idbs-staging.com": "Staging version of main application IDBS-cloud", - "https://idbs-eworkbook.com": "E-workbook services part of IDBS-cloud product", - "https://eworkbookcloud.com": "E-workbook services part of IDBS-cloud product", - "https://eworkbookrequest.com": "E-workbook request part of IDBS-cloud product" + "https://idbs-dev.com": "RWS_rationale_https___idbs_dev_com", + "https://idbs-staging.com": "RWS_rationale_https___idbs_staging_com", + "https://idbs-eworkbook.com": "RWS_rationale_https___idbs_eworkbook_com", + "https://eworkbookcloud.com": "RWS_rationale_https___eworkbookcloud_com", + "https://eworkbookrequest.com": "RWS_rationale_https___eworkbookrequest_com" } }, { @@ -269,8 +279,8 @@ "https://mighty-app.appspot.com" ], "rationaleBySite": { - "https://textyserver.appspot.com": "API service for mightytext.net", - "https://mighty-app.appspot.com": "alternative domain for API service for mightytext.net" + "https://textyserver.appspot.com": "RWS_rationale_https___textyserver_appspot_com", + "https://mighty-app.appspot.com": "RWS_rationale_https___mighty_app_appspot_com" } }, { @@ -280,7 +290,7 @@ "https://wingify.com" ], "rationaleBySite": { - "https://wingify.com": "Alias" + "https://wingify.com": "RWS_rationale_https___wingify_com" } }, { @@ -290,7 +300,7 @@ "https://wildixin.com" ], "rationaleBySite": { - "https://wildixin.com": "Wildix PBX's domain" + "https://wildixin.com": "RWS_rationale_https___wildixin_com" } }, { @@ -309,14 +319,14 @@ "https://tvid.in" ], "rationaleBySite": { - "https://indiatimes.com": "Indiatimes.com is primary domain for Times Internet which publish stories on Indian Lifestyle, Culture, Relationships, Food, Travel, Entertainment, News and New Technology News", - "https://timesofindia.com": "Times of India (TOI) is Indias largest and most influential news publisher in English. Its digital platform timesofindia.com is powered by Times Internet Limited", - "https://economictimes.com": "Economic Times is India's top business news platform, providing comprehensive coverage of the economy, stock markets, and personal finance to inspire and empower business leaders and entrepreneurs. This digital platform is powered by Times Internet Limited", - "https://samayam.com": "Samayam is a Regional News publisher powered by Times Internet Limited", - "https://cricbuzz.com": "Cricbuzz is an Indian cricket news & live score platform owned by Times Internet Limited", - "https://growthrx.in": "GrowthRx is a customer engagement platform powered by Times Internet", - "https://clmbtech.com": "This is a times-internet data and customer management platform for managing publisher data.", - "https://tvid.in": "TVID is an in-house video management platform powered by Times Internet" + "https://indiatimes.com": "RWS_rationale_https___indiatimes_com", + "https://timesofindia.com": "RWS_rationale_https___timesofindia_com", + "https://economictimes.com": "RWS_rationale_https___economictimes_com", + "https://samayam.com": "RWS_rationale_https___samayam_com", + "https://cricbuzz.com": "RWS_rationale_https___cricbuzz_com", + "https://growthrx.in": "RWS_rationale_https___growthrx_in", + "https://clmbtech.com": "RWS_rationale_https___clmbtech_com", + "https://tvid.in": "RWS_rationale_https___tvid_in" } }, { @@ -330,9 +340,9 @@ "https://hc1cas.global" ], "rationaleBySite": { - "https://hc1.global": "hc1 Insights Lab platform for UK.", - "https://hc1cas.com": "hc1 Insights Lab platform authentication service for US.", - "https://hc1cas.global": "hc1 Insight Lab platform authentication service for UK." + "https://hc1.global": "RWS_rationale_https___hc1_global", + "https://hc1cas.com": "RWS_rationale_https___hc1cas_com", + "https://hc1cas.global": "RWS_rationale_https___hc1cas_global" } }, { @@ -346,11 +356,11 @@ "https://kompas.tv" ], "rationaleBySite": { - "https://tribunnews.com": "Indonesian local news by KG Media", - "https://grid.id": "Indonesian entertainment & lifestyle news by KG Media ", - "https://bolasport.com": "Indonesian sport news by KG Media ", - "https://kompasiana.com": "Indonesian largest blog community by KG Media ", - "https://kompas.tv": "Indonesian national news by KG Media " + "https://tribunnews.com": "RWS_rationale_https___tribunnews_com", + "https://grid.id": "RWS_rationale_https___grid_id", + "https://bolasport.com": "RWS_rationale_https___bolasport_com", + "https://kompasiana.com": "RWS_rationale_https___kompasiana_com", + "https://kompas.tv": "RWS_rationale_https___kompas_tv" } }, { @@ -364,11 +374,11 @@ "https://commentcamarche.com" ], "rationaleBySite": { - "https://commentcamarche.net": "Centralized account for redactors, forums & real-time messages", - "https://linternaute.com": "Centralized account for redactors, forums & real-time messages", - "https://journaldunet.com": "Centralized account for redactors, forums & real-time messages", - "https://phonandroid.com": "Centralized account for redactors, forums & real-time messages", - "https://commentcamarche.com": "Centralized account for redactors, forums & real-time messages" + "https://commentcamarche.net": "RWS_rationale_https___commentcamarche_net", + "https://linternaute.com": "RWS_rationale_https___linternaute_com", + "https://journaldunet.com": "RWS_rationale_https___journaldunet_com", + "https://phonandroid.com": "RWS_rationale_https___phonandroid_com", + "https://commentcamarche.com": "RWS_rationale_https___commentcamarche_com" }, "ccTLDs": { "https://journaldesfemmes.com": [ @@ -382,6 +392,16 @@ ] } }, + { + "contact": "bergmanpatrickr@johndeere.com", + "primary": "https://johndeere.com", + "associatedSites": [ + "https://deere.com" + ], + "rationaleBySite": { + "https://deere.com": "RWS_rationale_https___deere_com" + } + }, { "contact": "jquintana@lanacion.com.ar", "primary": "https://lanacion.com.ar", @@ -389,7 +409,7 @@ "https://bonvivir.com" ], "rationaleBySite": { - "https://bonvivir.com": "Wine club for sell wine by La Nación" + "https://bonvivir.com": "RWS_rationale_https___bonvivir_com" } }, { @@ -401,9 +421,9 @@ "https://nien.co" ], "rationaleBySite": { - "https://chennien.com": "Alias domain for Nien Studio", - "https://nien.org": "Campaign pages for Nien Studio", - "https://nien.co": "URL shortener for Nien Studio" + "https://chennien.com": "RWS_rationale_https___chennien_com", + "https://nien.org": "RWS_rationale_https___nien_org", + "https://nien.co": "RWS_rationale_https___nien_co" } }, { @@ -419,13 +439,13 @@ "https://miss.com.tw" ], "rationaleBySite": { - "https://hearty.app": "API endpoints for Hearty Journal", - "https://hearty.gift": "Campaign pages for Hearty Journal", - "https://hj.rs": "URL shortener for Hearty Journal", - "https://heartymail.com": "Email service for Hearty Journal", - "https://alice.tw": "Alice's blog", - "https://jiayi.life": "Jiayi's blog", - "https://miss.com.tw": "Company website" + "https://hearty.app": "RWS_rationale_https___hearty_app", + "https://hearty.gift": "RWS_rationale_https___hearty_gift", + "https://hj.rs": "RWS_rationale_https___hj_rs", + "https://heartymail.com": "RWS_rationale_https___heartymail_com", + "https://alice.tw": "RWS_rationale_https___alice_tw", + "https://jiayi.life": "RWS_rationale_https___jiayi_life", + "https://miss.com.tw": "RWS_rationale_https___miss_com_tw" } }, { @@ -435,7 +455,7 @@ "https://trytalkdesk.com" ], "rationaleBySite": { - "https://trytalkdesk.com": "Specialized platform for CCaS" + "https://trytalkdesk.com": "RWS_rationale_https___trytalkdesk_com" } }, { @@ -445,7 +465,7 @@ "https://gettalkdesk.com" ], "rationaleBySite": { - "https://gettalkdesk.com": "Specialized platform for CCaS" + "https://gettalkdesk.com": "RWS_rationale_https___gettalkdesk_com" } }, { @@ -461,11 +481,11 @@ "https://ocdn.eu" ], "rationaleBySite": { - "https://fakt.pl": "Special interest news website that is part of the Ringier Axel Springer Polska media group. It provides up-to-date news and covers a wide range of topics including politics, sports, entertainment, and lifestyle. Fakt.pl aims to deliver reliable and engaging content to its readers, keeping them informed about the latest events and trends.", - "https://businessinsider.com.pl": "Special interest news website within the Ringier Axel Springer Polska media group. It focuses on business, finance, technology, and related topics. The website provides in-depth analysis, market insights, and news articles tailored for professionals, entrepreneurs, and those interested in the business world.", - "https://medonet.pl": "Special interest news website within the Ringier Axel Springer Polska media group dedicated to health and medical topics. It offers a wide range of articles, news, and features related to various aspects of health, wellness, medical research, treatments, and lifestyle. Medonet.pl aims to provide reliable and accurate information to its readers, promoting healthy living and informed decision-making.", - "https://ocdn.eu": "Service dedicated to processing events that occur on websites, assists in managing and optimizing multimedia content, including images, videos, or other resources, for websites that rely on event-related functionality.", - "https://plejada.pl": "Special interest news website within the Ringier Axel Springer Polska media group dedicated to exclusive information, interviews, videos, and galleries from the world of show business." + "https://fakt.pl": "RWS_rationale_https___fakt_pl", + "https://businessinsider.com.pl": "RWS_rationale_https___businessinsider_com_pl", + "https://medonet.pl": "RWS_rationale_https___medonet_pl", + "https://ocdn.eu": "RWS_rationale_https___ocdn_eu", + "https://plejada.pl": "RWS_rationale_https___plejada_pl" } }, { @@ -477,9 +497,9 @@ "https://gallito.com.uy" ], "rationaleBySite": { - "https://clubelpais.com.uy": "Domain from El Pais Uruguay", - "https://paula.com.uy": "Domain from El Pais Uruguay", - "https://gallito.com.uy": "Domain from El Pais Uruguay" + "https://clubelpais.com.uy": "RWS_rationale_https___clubelpais_com_uy", + "https://paula.com.uy": "RWS_rationale_https___paula_com_uy", + "https://gallito.com.uy": "RWS_rationale_https___gallito_com_uy" }, "ccTLDs": { "https://elpais.com.uy": [ @@ -497,9 +517,9 @@ "https://iolam.it" ], "rationaleBySite": { - "https://libero.it": "Web portal", - "https://supereva.it": "Gossip web site", - "https://iolam.it": "Used for personalization purposes" + "https://libero.it": "RWS_rationale_https___libero_it", + "https://supereva.it": "RWS_rationale_https___supereva_it", + "https://iolam.it": "RWS_rationale_https___iolam_it" } }, { @@ -510,8 +530,8 @@ "https://rws3nvtvt.com" ], "rationaleBySite": { - "https://rws2nvtvt.com": "Test 1", - "https://rws3nvtvt.com": "Test 2" + "https://rws2nvtvt.com": "RWS_rationale_https___rws2nvtvt_com", + "https://rws3nvtvt.com": "RWS_rationale_https___rws3nvtvt_com" } }, { @@ -522,8 +542,8 @@ "https://stripe.network" ], "rationaleBySite": { - "https://stripecdn.com": "Serves static assets accelerating page loads and sandboxes select Javascript", - "https://stripe.network": "Receives telemetry preventing fraud across Stripe services" + "https://stripecdn.com": "RWS_rationale_https___stripecdn_com", + "https://stripe.network": "RWS_rationale_https___stripe_network" } }, { @@ -534,8 +554,8 @@ "https://human-talk.org" ], "rationaleBySite": { - "https://reshim.org": "Silent login", - "https://human-talk.org": "Silent login" + "https://reshim.org": "RWS_rationale_https___reshim_org", + "https://human-talk.org": "RWS_rationale_https___human_talk_org" } }, { @@ -553,15 +573,15 @@ "https://yastatic.net" ], "rationaleBySite": { - "https://yandex.ru": "Yandex owned website", - "https://yandex.net": "Yandex owned website", - "https://yastatic.net": "Yandex owned website", - "https://auto.ru": "Yandex owned website", - "https://kinopoisk.ru": "Yandex owned website", - "https://clck.ru": "Yandex owned website", - "https://webvisor.com": "Yandex owned website", - "https://turbopages.org": "Yandex owned website", - "https://edadeal.ru": "Yandex owned website" + "https://yandex.ru": "RWS_rationale_https___yandex_ru", + "https://yandex.net": "RWS_rationale_https___yandex_net", + "https://yastatic.net": "RWS_rationale_https___yastatic_net", + "https://auto.ru": "RWS_rationale_https___auto_ru", + "https://kinopoisk.ru": "RWS_rationale_https___kinopoisk_ru", + "https://clck.ru": "RWS_rationale_https___clck_ru", + "https://webvisor.com": "RWS_rationale_https___webvisor_com", + "https://turbopages.org": "RWS_rationale_https___turbopages_org", + "https://edadeal.ru": "RWS_rationale_https___edadeal_ru" }, "ccTLDs": { "https://ya.ru": [ @@ -592,8 +612,8 @@ "https://technology-revealed.com" ], "rationaleBySite": { - "https://standardsandpraiserepurpose.com": "Publisher #1 for RWS Test", - "https://technology-revealed.com": "Frequency Capping Domain #1 for RWS Test" + "https://standardsandpraiserepurpose.com": "RWS_rationale_https___standardsandpraiserepurpose_com", + "https://technology-revealed.com": "RWS_rationale_https___technology_revealed_com" } }, { @@ -604,8 +624,8 @@ "https://startupislandtaiwan.org" ], "rationaleBySite": { - "https://startupislandtaiwan.net": "Domain alias", - "https://startupislandtaiwan.org": "Domain alias" + "https://startupislandtaiwan.net": "RWS_rationale_https___startupislandtaiwan_net", + "https://startupislandtaiwan.org": "RWS_rationale_https___startupislandtaiwan_org" } }, { @@ -632,11 +652,11 @@ "https://hjck.com" ], "rationaleBySite": { - "https://noticiascaracol.com": "Informative news show that covers the most important events in Colombia and around the world, owned by Valorem business group.", - "https://bluradio.com": "Blu Radio is a Colombian radio network, owned by Valorem business group.", - "https://shock.co": "It is a media outlet that publishes specialized content daily about music, movies, and television series. Owned by Valorem business group.", - "https://bumbox.com": "Podcast platform from the media group of Caracol Televisión, El Espectador, and Blu Radio. Owned by Valorem business group.", - "https://hjck.com": "It's a private Colombian radio station with a cultural programming. It belongs to the Valorem business group." + "https://noticiascaracol.com": "RWS_rationale_https___noticiascaracol_com", + "https://bluradio.com": "RWS_rationale_https___bluradio_com", + "https://shock.co": "RWS_rationale_https___shock_co", + "https://bumbox.com": "RWS_rationale_https___bumbox_com", + "https://hjck.com": "RWS_rationale_https___hjck_com" } }, { @@ -646,7 +666,7 @@ "https://firstlook.biz" ], "rationaleBySite": { - "https://firstlook.biz": "Presents core reports and SSO auth flow" + "https://firstlook.biz": "RWS_rationale_https___firstlook_biz" } }, { @@ -656,7 +676,7 @@ "https://wordle.at" ], "rationaleBySite": { - "https://wordle.at": "We are migrating our domain and will soon redirect all traffic from here to the primary, both of which we own. The two sites are almost identical. For convenience we want to transfer session cookies so users stay logged in." + "https://wordle.at": "RWS_rationale_https___wordle_at" } }, { @@ -670,11 +690,11 @@ "https://ishares.com" ], "rationaleBySite": { - "https://blackrockadvisorelite.it": "A site for Italian investment professionals. The branding is clearly visible on the site.", - "https://cachematrix.com": "Cachematrix is a cash management firm acquired by BlackRock. The relationship is described on the About Us page.", - "https://efront.com": "eFront is BlackRock's alternative solutions platform. The BlackRock branding is clearly visible on the site.", - "https://etfacademy.it": "An Italian language education site about ETFs. The iShares by BlackRock branding is clearly visible on the site.", - "https://ishares.com": "iShares is BlackRock's ETF brand. The branding is clearly visible on the site." + "https://blackrockadvisorelite.it": "RWS_rationale_https___blackrockadvisorelite_it", + "https://cachematrix.com": "RWS_rationale_https___cachematrix_com", + "https://efront.com": "RWS_rationale_https___efront_com", + "https://etfacademy.it": "RWS_rationale_https___etfacademy_it", + "https://ishares.com": "RWS_rationale_https___ishares_com" } }, { @@ -686,9 +706,9 @@ "https://zdrowietvn.pl" ], "rationaleBySite": { - "https://player.pl": "Streaming service that is owned by TVN S.A.. Information about the connection with TVN is included in the footer of this website", - "https://zdrowietvn.pl": "Educational service that is owned by TVN S.A.. Information about the connection with TVN is included in the footer of this website", - "https://tvn24.pl": "News service that is owned by TVN S.A.. Information about the connection with TVN is included in the footer of this website" + "https://player.pl": "RWS_rationale_https___player_pl", + "https://zdrowietvn.pl": "RWS_rationale_https___zdrowietvn_pl", + "https://tvn24.pl": "RWS_rationale_https___tvn24_pl" } }, { @@ -700,9 +720,9 @@ "https://smoney.vn" ], "rationaleBySite": { - "https://zingmp3.vn": "Music Website owned by Zalo Group, VNG", - "https://baomoi.com": "News Website owned by Zalo Group, VNG", - "https://smoney.vn": "News Website owned by Zalo Group, VNG" + "https://zingmp3.vn": "RWS_rationale_https___zingmp3_vn", + "https://baomoi.com": "RWS_rationale_https___baomoi_com", + "https://smoney.vn": "RWS_rationale_https___smoney_vn" } }, { @@ -716,9 +736,9 @@ "https://pdmp-apis.no" ], "rationaleBySite": { - "https://prisjakt.no": "Site owned by Schibsted with shared SSO across Schibsted Norway's sites and common branding that clearly shows the relation between the sites.", - "https://mittanbud.no": "Site owned by Schibsted with shared SSO across Schibsted Norway's sites and common branding that clearly shows the relation between the sites.", - "https://pdmp-apis.no": "Site owned by Schibsted that provides APIs for users across Schibsted's sites." + "https://prisjakt.no": "RWS_rationale_https___prisjakt_no", + "https://mittanbud.no": "RWS_rationale_https___mittanbud_no", + "https://pdmp-apis.no": "RWS_rationale_https___pdmp_apis_no" } }, { @@ -731,10 +751,10 @@ "https://grupolpg.sv" ], "rationaleBySite": { - "https://elgrafico.com": "News portal owned by La Prensa Grafica Group", - "https://eleconomista.net": "News portal owned by La Prensa Grafica Group", - "https://ella.sv": "News portal owned by La Prensa Grafica Group", - "https://grupolpg.sv": "Domain owned by La Prensa Grafica Group" + "https://elgrafico.com": "RWS_rationale_https___elgrafico_com", + "https://eleconomista.net": "RWS_rationale_https___eleconomista_net", + "https://ella.sv": "RWS_rationale_https___ella_sv", + "https://grupolpg.sv": "RWS_rationale_https___grupolpg_sv" } }, { @@ -745,19 +765,45 @@ ], "serviceSites": [], "rationaleBySite": { - "https://nidhiacademyonline.com": "This website is a subpart of kaksya.in and is used to host Nidhi Academy educational content videos/notes and more." + "https://nidhiacademyonline.com": "RWS_rationale_https___nidhiacademyonline_com" + } + }, + { + "contact": "profiel@vrt.be", + "primary": "https://vrt.be", + "associatedSites": [ + "https://dewarmsteweek.be", + "https://sporza.be", + "https://een.be", + "https://radio2.be", + "https://radio1.be" + ], + "rationaleBySite": { + "https://dewarmsteweek.be": "RWS_rationale_https___dewarmsteweek_be", + "https://sporza.be": "RWS_rationale_https___sporza_be", + "https://een.be": "RWS_rationale_https___een_be", + "https://radio2.be": "RWS_rationale_https___radio2_be", + "https://radio1.be": "RWS_rationale_https___radio1_be" + } + }, + { + "contact": "abatahi@nvidia.com", + "primary": "https://nvidia.com", + "associatedSites": [ + "https://geforcenow.com" + ], + "rationaleBySite": { + "https://geforcenow.com": "RWS_rationale_https___geforcenow_com" } }, { "contact": "jsteam@intra.sapo.pt", "primary": "https://sapo.pt", "associatedSites": [ - "https://sapo.pt", "https://meo.pt" ], "rationaleBySite": { - "https://sapo.pt": "SAPO is Portugal's leading online platform offering a diverse range of news, information, and services catering to various interests including news, entertainment, sports, technology, and lifestyle.", - "https://meo.pt": "MEO is a prominent telecommunications and entertainment provider in Portugal, offering a wide range of services including television, internet, phone, and mobile." + "https://meo.pt": "RWS_rationale_https___meo_pt" }, "ccTLDs": { "https://sapo.pt": [ @@ -766,21 +812,93 @@ } }, { - "contact": "profiel@vrt.be", - "primary": "https://vrt.be", + "contact": "support@tolteck.com", + "primary": "https://tolteck.com", "associatedSites": [ - "https://dewarmsteweek.be", - "https://sporza.be", - "https://een.be", - "https://radio2.be", - "https://radio1.be" + "https://tolteck.app" + ], + "rationaleBySite": { + "https://tolteck.app": "RWS_rationale_https___tolteck_app" + } + }, + { + "contact": "datateam@centralmediacsoport.hu", + "primary": "https://p24.hu", + "associatedSites": [ + "https://24.hu", + "https://startlap.hu", + "https://nlc.hu", + "https://hazipatika.com", + "https://nosalty.hu" + ], + "rationaleBySite": { + "https://24.hu": "RWS_rationale_https___24_hu", + "https://startlap.hu": "RWS_rationale_https___startlap_hu", + "https://nlc.hu": "RWS_rationale_https___nlc_hu", + "https://hazipatika.com": "RWS_rationale_https___hazipatika_com", + "https://nosalty.hu": "RWS_rationale_https___nosalty_hu" + } + }, + { + "contact": "info@cognitiveai.ru", + "primary": "https://cognitiveai.ru", + "associatedSites": [ + "https://cognitive-ai.ru" + ], + "rationaleBySite": { + "https://cognitive-ai.ru": "RWS_rationale_https___cognitive_ai_ru" + } + }, + { + "contact": "technical@citybibleforum.org", + "primary": "https://citybibleforum.org", + "associatedSites": [ + "https://thirdspace.org.au" + ], + "rationaleBySite": { + "https://thirdspace.org.au": "RWS_rationale_https___thirdspace_org_au" + } + }, + { + "contact": "admin@smpn106jkt.sch.id", + "primary": "https://p106.net", + "associatedSites": [ + "https://smpn106jkt.sch.id" + ], + "rationaleBySite": { + "https://p106.net": "RWS_rationale_https___p106_net", + "https://smpn106jkt.sch.id": "RWS_rationale_https___smpn106jkt_sch_id" + } + }, + { + "contact": "sanjay.nagpal@aajtak.com", + "primary": "https://indiatoday.in", + "associatedSites": [ + "https://aajtak.in", + "https://businesstoday.in", + "https://intoday.in", + "https://gnttv.com", + "https://indiatodayne.in" + ], + "rationaleBySite": { + "https://intoday.in": "RWS_rationale_https___intoday_in", + "https://aajtak.in": "RWS_rationale_https___aajtak_in", + "https://businesstoday.in": "RWS_rationale_https___businesstoday_in", + "https://indiatoday.in": "RWS_rationale_https___indiatoday_in", + "https://gnttv.com": "RWS_rationale_https___gnttv_com", + "https://indiatodayne.in": "RWS_rationale_https___indiatodayne_in" + } + }, + { + "contact": "naukri.comapp@gmail.com", + "primary": "https://naukri.com", + "associatedSites": [ + "https://ambitionbox.com", + "https://infoedgeindia.com" ], "rationaleBySite": { - "https://dewarmsteweek.be": "domain for a yearly campaign", - "https://sporza.be": "sub brand for sports news", - "https://een.be": "sub brand site", - "https://radio2.be": "sub brand site", - "https://radio1.be": "sub brand site" + "https://ambitionbox.com": "RWS_rationale_https___ambitionbox_com", + "https://infoedgeindia.com": "RWS_rationale_https___infoedgeindia_com" } } ] diff --git a/extension.webpack.config.cjs b/extension.webpack.config.cjs index e4694e851..26bec1308 100644 --- a/extension.webpack.config.cjs +++ b/extension.webpack.config.cjs @@ -22,8 +22,8 @@ const commonConfig = require('./webpack.shared.cjs'); const root = { entry: { - 'service-worker': './packages/extension/src/serviceWorker/index.ts', - 'content-script': './packages/extension/src/contentScript/index.ts', + 'service-worker': './src/serviceWorker/index.ts', + 'content-script': './src/contentScript/index.ts', }, output: { path: path.resolve(__dirname, './dist/extension'), @@ -32,10 +32,14 @@ const root = { plugins: [ new CopyPlugin({ patterns: [ - { from: './packages/extension/src/manifest.json', to: '' }, - { from: './packages/extension/icons', to: 'icons' }, - { from: './assets', to: 'assets' }, - { from: './data', to: 'data' }, + { from: './src/manifest.json', to: '' }, + { from: './icons', to: 'icons' }, + { from: '../../assets', to: 'assets' }, + { from: '../../data', to: 'data' }, + { + from: '../i18n/_locales/messages', + to: './_locales/', + }, ], }), new WebpackBar({ @@ -48,9 +52,9 @@ const root = { const devTools = { entry: { - index: './packages/extension/src/view/devtools/index.tsx', - devtools: './packages/extension/src/view/devtools/devtools.ts', - worker: './packages/extension/src/worker/index.ts', + index: './src/view/devtools/index.tsx', + devtools: './src/view/devtools/devtools.ts', + worker: './src/worker/index.ts', }, output: { path: path.resolve(__dirname, './dist/extension/devtools'), @@ -63,13 +67,13 @@ const devTools = { }), new HtmlWebpackPlugin({ title: 'PSAT Devtool', - template: './packages/extension/src/view/devtools/index.html', + template: './src/view/devtools/index.html', filename: 'index.html', inject: false, }), new HtmlWebpackPlugin({ title: 'PSAT', - template: './packages/extension/src/view/devtools/devtools.html', + template: './src/view/devtools/devtools.html', filename: 'devtools.html', inject: true, }), @@ -79,7 +83,7 @@ const devTools = { const popup = { entry: { - index: './packages/extension/src/view/popup/index.tsx', + index: './src/view/popup/index.tsx', }, output: { path: path.resolve(__dirname, './dist/extension/popup'), @@ -92,7 +96,7 @@ const popup = { }), new HtmlWebpackPlugin({ title: 'PSAT Popup', - template: './packages/extension/src/view/popup/index.html', + template: './src/view/popup/index.html', inject: false, }), ], @@ -101,7 +105,7 @@ const popup = { const report = { entry: { - index: './packages/report/src/index.tsx', + index: '../report/src/index.tsx', }, output: { path: path.resolve(__dirname, './dist/extension/report'), @@ -114,7 +118,7 @@ const report = { }), new HtmlWebpackPlugin({ title: 'Report', - template: './packages/report/public/index.html', + template: '../report/public/index.html', filename: 'index.html', inject: true, }), diff --git a/package-lock.json b/package-lock.json index a85654337..84abf32d1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ps-analysis-tool", - "version": "0.8.0", - "lockfileVersion": 2, + "version": "0.9.0", + "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ps-analysis-tool", - "version": "0.8.0", + "version": "0.9.0", "license": "Apache-2.0", "workspaces": [ "packages/*" @@ -16,7 +16,7 @@ "@babel/preset-env": "^7.22.5", "@babel/preset-react": "^7.22.5", "@babel/preset-typescript": "^7.22.5", - "@ps-analysis-tool/eslint-import-resolver": "*", + "@google-psat/eslint-import-resolver": "*", "@storybook/addon-essentials": "^7.0.27", "@storybook/addon-interactions": "^7.0.27", "@storybook/addon-links": "^7.0.27", @@ -80,11 +80,12 @@ "webpack": "^5.86.0", "webpack-cli": "^5.1.4", "webpack-dev-server": "^4.15.1", + "webpack-node-externals": "^3.0.0", "webpackbar": "^5.0.2" } }, "node_modules/@adobe/css-tools": { - "version": "4.3.3", + "version": "4.4.0", "dev": true, "license": "MIT" }, @@ -112,10 +113,10 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.24.2", + "version": "7.24.7", "license": "MIT", "dependencies": { - "@babel/highlight": "^7.24.2", + "@babel/highlight": "^7.24.7", "picocolors": "^1.0.0" }, "engines": { @@ -123,7 +124,7 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.24.4", + "version": "7.24.7", "dev": true, "license": "MIT", "engines": { @@ -131,20 +132,20 @@ } }, "node_modules/@babel/core": { - "version": "7.24.5", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.2", - "@babel/generator": "^7.24.5", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.24.5", - "@babel/helpers": "^7.24.5", - "@babel/parser": "^7.24.5", - "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.5", - "@babel/types": "^7.24.5", + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.24.7", + "@babel/helper-compilation-targets": "^7.24.7", + "@babel/helper-module-transforms": "^7.24.7", + "@babel/helpers": "^7.24.7", + "@babel/parser": "^7.24.7", + "@babel/template": "^7.24.7", + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -160,11 +161,11 @@ } }, "node_modules/@babel/generator": { - "version": "7.24.5", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.24.5", + "@babel/types": "^7.24.7", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -174,34 +175,35 @@ } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.22.5", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.22.15", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.22.15" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.23.6", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-validator-option": "^7.23.5", + "@babel/compat-data": "^7.24.7", + "@babel/helper-validator-option": "^7.24.7", "browserslist": "^4.22.2", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -211,18 +213,18 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.24.5", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-member-expression-to-functions": "^7.24.5", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.24.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.24.5", + "@babel/helper-annotate-as-pure": "^7.24.7", + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-function-name": "^7.24.7", + "@babel/helper-member-expression-to-functions": "^7.24.7", + "@babel/helper-optimise-call-expression": "^7.24.7", + "@babel/helper-replace-supers": "^7.24.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", + "@babel/helper-split-export-declaration": "^7.24.7", "semver": "^6.3.1" }, "engines": { @@ -233,11 +235,11 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.22.15", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-annotate-as-pure": "^7.24.7", "regexpu-core": "^5.3.1", "semver": "^6.3.1" }, @@ -264,68 +266,73 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", + "version": "7.24.7", "dev": true, "license": "MIT", + "dependencies": { + "@babel/types": "^7.24.7" + }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.23.0", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" + "@babel/template": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.24.5", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.24.5" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.24.3", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.24.0" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.24.5", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.24.3", - "@babel/helper-simple-access": "^7.24.5", - "@babel/helper-split-export-declaration": "^7.24.5", - "@babel/helper-validator-identifier": "^7.24.5" + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-simple-access": "^7.24.7", + "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -335,18 +342,18 @@ } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.22.5", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.5", + "version": "7.24.7", "dev": true, "license": "MIT", "engines": { @@ -354,13 +361,13 @@ } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.22.20", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-wrap-function": "^7.22.20" + "@babel/helper-annotate-as-pure": "^7.24.7", + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-wrap-function": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -370,13 +377,13 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-member-expression-to-functions": "^7.23.0", - "@babel/helper-optimise-call-expression": "^7.22.5" + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-member-expression-to-functions": "^7.24.7", + "@babel/helper-optimise-call-expression": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -386,40 +393,42 @@ } }, "node_modules/@babel/helper-simple-access": { - "version": "7.24.5", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.24.5" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.22.5", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.5", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.24.5" + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "engines": { @@ -427,14 +436,14 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.5", + "version": "7.24.7", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.23.5", + "version": "7.24.7", "dev": true, "license": "MIT", "engines": { @@ -442,36 +451,36 @@ } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.24.5", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-function-name": "^7.23.0", - "@babel/template": "^7.24.0", - "@babel/types": "^7.24.5" + "@babel/helper-function-name": "^7.24.7", + "@babel/template": "^7.24.7", + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.24.5", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.5", - "@babel/types": "^7.24.5" + "@babel/template": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.24.5", + "version": "7.24.7", "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.24.5", + "@babel/helper-validator-identifier": "^7.24.7", "chalk": "^2.4.2", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" @@ -481,7 +490,7 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.5", + "version": "7.24.7", "dev": true, "license": "MIT", "bin": { @@ -492,12 +501,12 @@ } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.24.5", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.24.5" + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -507,11 +516,11 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -521,13 +530,13 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.24.1" + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", + "@babel/plugin-transform-optional-chaining": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -537,12 +546,12 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -632,11 +641,11 @@ } }, "node_modules/@babel/plugin-syntax-flow": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -646,11 +655,11 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -660,11 +669,11 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -696,11 +705,11 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -804,11 +813,11 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -833,11 +842,11 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -847,13 +856,13 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.24.3", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-remap-async-to-generator": "^7.22.20", + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-remap-async-to-generator": "^7.24.7", "@babel/plugin-syntax-async-generators": "^7.8.4" }, "engines": { @@ -864,13 +873,13 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.24.1", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-remap-async-to-generator": "^7.22.20" + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-remap-async-to-generator": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -880,11 +889,11 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -894,11 +903,11 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.24.5", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.5" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -908,12 +917,12 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.1", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-create-class-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -923,12 +932,12 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.24.4", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.4", - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-create-class-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-class-static-block": "^7.14.5" }, "engines": { @@ -939,17 +948,17 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.24.5", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.24.5", - "@babel/helper-replace-supers": "^7.24.1", - "@babel/helper-split-export-declaration": "^7.24.5", + "@babel/helper-annotate-as-pure": "^7.24.7", + "@babel/helper-compilation-targets": "^7.24.7", + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-function-name": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-replace-supers": "^7.24.7", + "@babel/helper-split-export-declaration": "^7.24.7", "globals": "^11.1.0" }, "engines": { @@ -960,12 +969,12 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/template": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/template": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -975,11 +984,11 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.24.5", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.5" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -989,12 +998,12 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-create-regexp-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1004,11 +1013,11 @@ } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1018,11 +1027,11 @@ } }, "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-dynamic-import": "^7.8.3" }, "engines": { @@ -1033,12 +1042,12 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1048,11 +1057,11 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" }, "engines": { @@ -1063,12 +1072,12 @@ } }, "node_modules/@babel/plugin-transform-flow-strip-types": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-flow": "^7.24.1" + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/plugin-syntax-flow": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1078,12 +1087,12 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1093,13 +1102,13 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-compilation-targets": "^7.24.7", + "@babel/helper-function-name": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1109,11 +1118,11 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-json-strings": "^7.8.3" }, "engines": { @@ -1124,11 +1133,11 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1138,11 +1147,11 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" }, "engines": { @@ -1153,11 +1162,11 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1167,12 +1176,12 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-module-transforms": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1182,13 +1191,13 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-simple-access": "^7.22.5" + "@babel/helper-module-transforms": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-simple-access": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1198,14 +1207,14 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-validator-identifier": "^7.22.20" + "@babel/helper-hoist-variables": "^7.24.7", + "@babel/helper-module-transforms": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1215,12 +1224,12 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-module-transforms": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1230,12 +1239,12 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.22.5", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-regexp-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1245,11 +1254,11 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1259,11 +1268,11 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" }, "engines": { @@ -1274,11 +1283,11 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-numeric-separator": "^7.10.4" }, "engines": { @@ -1289,14 +1298,14 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.24.5", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-plugin-utils": "^7.24.5", + "@babel/helper-compilation-targets": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.24.5" + "@babel/plugin-transform-parameters": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1306,12 +1315,12 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-replace-supers": "^7.24.1" + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-replace-supers": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1321,11 +1330,11 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" }, "engines": { @@ -1336,12 +1345,12 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.24.5", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", "@babel/plugin-syntax-optional-chaining": "^7.8.3" }, "engines": { @@ -1352,11 +1361,11 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.24.5", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.5" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1366,12 +1375,12 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.1", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-create-class-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1381,13 +1390,13 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.24.5", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.24.5", - "@babel/helper-plugin-utils": "^7.24.5", + "@babel/helper-annotate-as-pure": "^7.24.7", + "@babel/helper-create-class-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" }, "engines": { @@ -1398,11 +1407,11 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1412,11 +1421,11 @@ } }, "node_modules/@babel/plugin-transform-react-constant-elements": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1426,11 +1435,11 @@ } }, "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1440,15 +1449,15 @@ } }, "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.23.4", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-jsx": "^7.23.3", - "@babel/types": "^7.23.4" + "@babel/helper-annotate-as-pure": "^7.24.7", + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/plugin-syntax-jsx": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1458,11 +1467,11 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.22.5", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.22.5" + "@babel/plugin-transform-react-jsx": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1472,12 +1481,12 @@ } }, "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-annotate-as-pure": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1487,11 +1496,11 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.24.7", "regenerator-transform": "^0.15.2" }, "engines": { @@ -1502,11 +1511,11 @@ } }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1516,11 +1525,11 @@ } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1530,12 +1539,12 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1545,11 +1554,11 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1559,11 +1568,11 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1573,11 +1582,11 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.24.5", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.5" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1587,14 +1596,14 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.24.5", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.24.5", - "@babel/helper-plugin-utils": "^7.24.5", - "@babel/plugin-syntax-typescript": "^7.24.1" + "@babel/helper-annotate-as-pure": "^7.24.7", + "@babel/helper-create-class-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/plugin-syntax-typescript": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1604,11 +1613,11 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1618,12 +1627,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-create-regexp-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1633,12 +1642,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-create-regexp-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1648,12 +1657,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-create-regexp-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1663,26 +1672,26 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.24.5", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.24.4", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-plugin-utils": "^7.24.5", - "@babel/helper-validator-option": "^7.23.5", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.24.5", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.1", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.1", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.1", + "@babel/compat-data": "^7.24.7", + "@babel/helper-compilation-targets": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-validator-option": "^7.24.7", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.24.7", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.7", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.7", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.24.1", - "@babel/plugin-syntax-import-attributes": "^7.24.1", + "@babel/plugin-syntax-import-assertions": "^7.24.7", + "@babel/plugin-syntax-import-attributes": "^7.24.7", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", @@ -1694,54 +1703,54 @@ "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.24.1", - "@babel/plugin-transform-async-generator-functions": "^7.24.3", - "@babel/plugin-transform-async-to-generator": "^7.24.1", - "@babel/plugin-transform-block-scoped-functions": "^7.24.1", - "@babel/plugin-transform-block-scoping": "^7.24.5", - "@babel/plugin-transform-class-properties": "^7.24.1", - "@babel/plugin-transform-class-static-block": "^7.24.4", - "@babel/plugin-transform-classes": "^7.24.5", - "@babel/plugin-transform-computed-properties": "^7.24.1", - "@babel/plugin-transform-destructuring": "^7.24.5", - "@babel/plugin-transform-dotall-regex": "^7.24.1", - "@babel/plugin-transform-duplicate-keys": "^7.24.1", - "@babel/plugin-transform-dynamic-import": "^7.24.1", - "@babel/plugin-transform-exponentiation-operator": "^7.24.1", - "@babel/plugin-transform-export-namespace-from": "^7.24.1", - "@babel/plugin-transform-for-of": "^7.24.1", - "@babel/plugin-transform-function-name": "^7.24.1", - "@babel/plugin-transform-json-strings": "^7.24.1", - "@babel/plugin-transform-literals": "^7.24.1", - "@babel/plugin-transform-logical-assignment-operators": "^7.24.1", - "@babel/plugin-transform-member-expression-literals": "^7.24.1", - "@babel/plugin-transform-modules-amd": "^7.24.1", - "@babel/plugin-transform-modules-commonjs": "^7.24.1", - "@babel/plugin-transform-modules-systemjs": "^7.24.1", - "@babel/plugin-transform-modules-umd": "^7.24.1", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", - "@babel/plugin-transform-new-target": "^7.24.1", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.1", - "@babel/plugin-transform-numeric-separator": "^7.24.1", - "@babel/plugin-transform-object-rest-spread": "^7.24.5", - "@babel/plugin-transform-object-super": "^7.24.1", - "@babel/plugin-transform-optional-catch-binding": "^7.24.1", - "@babel/plugin-transform-optional-chaining": "^7.24.5", - "@babel/plugin-transform-parameters": "^7.24.5", - "@babel/plugin-transform-private-methods": "^7.24.1", - "@babel/plugin-transform-private-property-in-object": "^7.24.5", - "@babel/plugin-transform-property-literals": "^7.24.1", - "@babel/plugin-transform-regenerator": "^7.24.1", - "@babel/plugin-transform-reserved-words": "^7.24.1", - "@babel/plugin-transform-shorthand-properties": "^7.24.1", - "@babel/plugin-transform-spread": "^7.24.1", - "@babel/plugin-transform-sticky-regex": "^7.24.1", - "@babel/plugin-transform-template-literals": "^7.24.1", - "@babel/plugin-transform-typeof-symbol": "^7.24.5", - "@babel/plugin-transform-unicode-escapes": "^7.24.1", - "@babel/plugin-transform-unicode-property-regex": "^7.24.1", - "@babel/plugin-transform-unicode-regex": "^7.24.1", - "@babel/plugin-transform-unicode-sets-regex": "^7.24.1", + "@babel/plugin-transform-arrow-functions": "^7.24.7", + "@babel/plugin-transform-async-generator-functions": "^7.24.7", + "@babel/plugin-transform-async-to-generator": "^7.24.7", + "@babel/plugin-transform-block-scoped-functions": "^7.24.7", + "@babel/plugin-transform-block-scoping": "^7.24.7", + "@babel/plugin-transform-class-properties": "^7.24.7", + "@babel/plugin-transform-class-static-block": "^7.24.7", + "@babel/plugin-transform-classes": "^7.24.7", + "@babel/plugin-transform-computed-properties": "^7.24.7", + "@babel/plugin-transform-destructuring": "^7.24.7", + "@babel/plugin-transform-dotall-regex": "^7.24.7", + "@babel/plugin-transform-duplicate-keys": "^7.24.7", + "@babel/plugin-transform-dynamic-import": "^7.24.7", + "@babel/plugin-transform-exponentiation-operator": "^7.24.7", + "@babel/plugin-transform-export-namespace-from": "^7.24.7", + "@babel/plugin-transform-for-of": "^7.24.7", + "@babel/plugin-transform-function-name": "^7.24.7", + "@babel/plugin-transform-json-strings": "^7.24.7", + "@babel/plugin-transform-literals": "^7.24.7", + "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", + "@babel/plugin-transform-member-expression-literals": "^7.24.7", + "@babel/plugin-transform-modules-amd": "^7.24.7", + "@babel/plugin-transform-modules-commonjs": "^7.24.7", + "@babel/plugin-transform-modules-systemjs": "^7.24.7", + "@babel/plugin-transform-modules-umd": "^7.24.7", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", + "@babel/plugin-transform-new-target": "^7.24.7", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", + "@babel/plugin-transform-numeric-separator": "^7.24.7", + "@babel/plugin-transform-object-rest-spread": "^7.24.7", + "@babel/plugin-transform-object-super": "^7.24.7", + "@babel/plugin-transform-optional-catch-binding": "^7.24.7", + "@babel/plugin-transform-optional-chaining": "^7.24.7", + "@babel/plugin-transform-parameters": "^7.24.7", + "@babel/plugin-transform-private-methods": "^7.24.7", + "@babel/plugin-transform-private-property-in-object": "^7.24.7", + "@babel/plugin-transform-property-literals": "^7.24.7", + "@babel/plugin-transform-regenerator": "^7.24.7", + "@babel/plugin-transform-reserved-words": "^7.24.7", + "@babel/plugin-transform-shorthand-properties": "^7.24.7", + "@babel/plugin-transform-spread": "^7.24.7", + "@babel/plugin-transform-sticky-regex": "^7.24.7", + "@babel/plugin-transform-template-literals": "^7.24.7", + "@babel/plugin-transform-typeof-symbol": "^7.24.7", + "@babel/plugin-transform-unicode-escapes": "^7.24.7", + "@babel/plugin-transform-unicode-property-regex": "^7.24.7", + "@babel/plugin-transform-unicode-regex": "^7.24.7", + "@babel/plugin-transform-unicode-sets-regex": "^7.24.7", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.10", "babel-plugin-polyfill-corejs3": "^0.10.4", @@ -1757,13 +1766,13 @@ } }, "node_modules/@babel/preset-flow": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-validator-option": "^7.23.5", - "@babel/plugin-transform-flow-strip-types": "^7.24.1" + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-validator-option": "^7.24.7", + "@babel/plugin-transform-flow-strip-types": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1786,16 +1795,16 @@ } }, "node_modules/@babel/preset-react": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-validator-option": "^7.23.5", - "@babel/plugin-transform-react-display-name": "^7.24.1", - "@babel/plugin-transform-react-jsx": "^7.23.4", - "@babel/plugin-transform-react-jsx-development": "^7.22.5", - "@babel/plugin-transform-react-pure-annotations": "^7.24.1" + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-validator-option": "^7.24.7", + "@babel/plugin-transform-react-display-name": "^7.24.7", + "@babel/plugin-transform-react-jsx": "^7.24.7", + "@babel/plugin-transform-react-jsx-development": "^7.24.7", + "@babel/plugin-transform-react-pure-annotations": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1805,15 +1814,15 @@ } }, "node_modules/@babel/preset-typescript": { - "version": "7.24.1", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-validator-option": "^7.23.5", - "@babel/plugin-syntax-jsx": "^7.24.1", - "@babel/plugin-transform-modules-commonjs": "^7.24.1", - "@babel/plugin-transform-typescript": "^7.24.1" + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-validator-option": "^7.24.7", + "@babel/plugin-syntax-jsx": "^7.24.7", + "@babel/plugin-transform-modules-commonjs": "^7.24.7", + "@babel/plugin-transform-typescript": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1828,7 +1837,7 @@ "license": "MIT" }, "node_modules/@babel/runtime": { - "version": "7.24.5", + "version": "7.24.7", "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" @@ -1838,31 +1847,31 @@ } }, "node_modules/@babel/template": { - "version": "7.24.0", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/parser": "^7.24.0", - "@babel/types": "^7.24.0" + "@babel/code-frame": "^7.24.7", + "@babel/parser": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.24.5", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.24.2", - "@babel/generator": "^7.24.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.24.5", - "@babel/parser": "^7.24.5", - "@babel/types": "^7.24.5", + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.24.7", + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-function-name": "^7.24.7", + "@babel/helper-hoist-variables": "^7.24.7", + "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/parser": "^7.24.7", + "@babel/types": "^7.24.7", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1871,12 +1880,12 @@ } }, "node_modules/@babel/types": { - "version": "7.24.5", + "version": "7.24.7", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.24.1", - "@babel/helper-validator-identifier": "^7.24.5", + "@babel/helper-string-parser": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.7", "to-fast-properties": "^2.0.0" }, "engines": { @@ -1902,7 +1911,7 @@ } }, "node_modules/@emotion/is-prop-valid": { - "version": "1.2.1", + "version": "1.2.2", "dev": true, "license": "MIT", "peer": true, @@ -1917,7 +1926,7 @@ "peer": true }, "node_modules/@emotion/unitless": { - "version": "0.8.0", + "version": "0.8.1", "dev": true, "license": "MIT", "peer": true @@ -1973,7 +1982,7 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", + "version": "4.10.1", "dev": true, "license": "MIT", "engines": { @@ -2052,14 +2061,14 @@ } }, "node_modules/@floating-ui/core": { - "version": "1.6.1", + "version": "1.6.2", "license": "MIT", "dependencies": { "@floating-ui/utils": "^0.2.0" } }, "node_modules/@floating-ui/dom": { - "version": "1.6.4", + "version": "1.6.5", "license": "MIT", "dependencies": { "@floating-ui/core": "^1.0.0", @@ -2067,7 +2076,7 @@ } }, "node_modules/@floating-ui/react-dom": { - "version": "2.0.9", + "version": "2.1.0", "dev": true, "license": "MIT", "dependencies": { @@ -2083,7 +2092,7 @@ "license": "MIT" }, "node_modules/@formatjs/ecma402-abstract": { - "version": "1.18.2", + "version": "2.0.0", "license": "MIT", "dependencies": { "@formatjs/intl-localematcher": "0.5.4", @@ -2098,19 +2107,19 @@ } }, "node_modules/@formatjs/icu-messageformat-parser": { - "version": "2.7.6", + "version": "2.7.8", "license": "MIT", "dependencies": { - "@formatjs/ecma402-abstract": "1.18.2", - "@formatjs/icu-skeleton-parser": "1.8.0", + "@formatjs/ecma402-abstract": "2.0.0", + "@formatjs/icu-skeleton-parser": "1.8.2", "tslib": "^2.4.0" } }, "node_modules/@formatjs/icu-skeleton-parser": { - "version": "1.8.0", + "version": "1.8.2", "license": "MIT", "dependencies": { - "@formatjs/ecma402-abstract": "1.18.2", + "@formatjs/ecma402-abstract": "2.0.0", "tslib": "^2.4.0" } }, @@ -2121,6 +2130,46 @@ "tslib": "^2.4.0" } }, + "node_modules/@google-psat/analysis-utils": { + "resolved": "packages/analysis-utils", + "link": true + }, + "node_modules/@google-psat/cli": { + "resolved": "packages/cli", + "link": true + }, + "node_modules/@google-psat/cli-dashboard": { + "resolved": "packages/cli-dashboard", + "link": true + }, + "node_modules/@google-psat/common": { + "resolved": "packages/common", + "link": true + }, + "node_modules/@google-psat/design-system": { + "resolved": "packages/design-system", + "link": true + }, + "node_modules/@google-psat/eslint-import-resolver": { + "resolved": "packages/eslint-import-resolver", + "link": true + }, + "node_modules/@google-psat/extension": { + "resolved": "packages/extension", + "link": true + }, + "node_modules/@google-psat/i18n": { + "resolved": "packages/i18n", + "link": true + }, + "node_modules/@google-psat/library-detection": { + "resolved": "packages/library-detection", + "link": true + }, + "node_modules/@google-psat/report": { + "resolved": "packages/report", + "link": true + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "dev": true, @@ -2677,24 +2726,10 @@ "node": ">=10" } }, - "node_modules/@jest/reporters/node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@jest/reporters/node_modules/semver": { - "version": "7.6.0", + "version": "7.6.2", "dev": true, "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -2713,11 +2748,6 @@ "node": ">=8" } }, - "node_modules/@jest/reporters/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, "node_modules/@jest/schemas": { "version": "29.6.3", "dev": true, @@ -3027,16 +3057,16 @@ } }, "node_modules/@pmmmwh/react-refresh-webpack-plugin": { - "version": "0.5.13", + "version": "0.5.15", "dev": true, "license": "MIT", "dependencies": { - "ansi-html-community": "^0.0.8", + "ansi-html": "^0.0.9", "core-js-pure": "^3.23.3", "error-stack-parser": "^2.0.6", "html-entities": "^2.1.0", "loader-utils": "^2.0.4", - "schema-utils": "^3.0.0", + "schema-utils": "^4.2.0", "source-map": "^0.7.3" }, "engines": { @@ -3073,149 +3103,73 @@ } } }, - "node_modules/@ps-analysis-tool/cli": { - "resolved": "packages/cli", - "link": true - }, - "node_modules/@ps-analysis-tool/cli-dashboard": { - "resolved": "packages/cli-dashboard", - "link": true - }, - "node_modules/@ps-analysis-tool/common": { - "resolved": "packages/common", - "link": true - }, - "node_modules/@ps-analysis-tool/design-system": { - "resolved": "packages/design-system", - "link": true - }, - "node_modules/@ps-analysis-tool/eslint-import-resolver": { - "resolved": "packages/eslint-import-resolver", - "link": true - }, - "node_modules/@ps-analysis-tool/extension": { - "resolved": "packages/extension", - "link": true - }, - "node_modules/@ps-analysis-tool/i18n": { - "resolved": "packages/i18n", - "link": true - }, - "node_modules/@ps-analysis-tool/library-detection": { - "resolved": "packages/library-detection", - "link": true - }, - "node_modules/@ps-analysis-tool/report": { - "resolved": "packages/report", - "link": true - }, "node_modules/@puppeteer/browsers": { - "version": "1.6.0", - "license": "Apache-2.0", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.2.3.tgz", + "integrity": "sha512-bJ0UBsk0ESOs6RFcLXOt99a3yTDcOKlzfjad+rhFwdaG1Lu/Wzq58GHYCDTlZ9z6mldf4g+NTb+TXEfe0PpnsQ==", "dependencies": { "debug": "4.3.4", "extract-zip": "2.0.1", "progress": "2.0.3", - "proxy-agent": "6.3.0", - "tar-fs": "3.0.4", + "proxy-agent": "6.4.0", + "semver": "7.6.0", + "tar-fs": "3.0.5", "unbzip2-stream": "1.4.3", - "yargs": "17.7.1" + "yargs": "17.7.2" }, "bin": { "browsers": "lib/cjs/main-cli.js" }, "engines": { - "node": ">=16.3.0" + "node": ">=18" } }, - "node_modules/@puppeteer/browsers/node_modules/emoji-regex": { - "version": "8.0.0", - "license": "MIT" - }, - "node_modules/@puppeteer/browsers/node_modules/extract-zip": { - "version": "2.0.1", - "license": "BSD-2-Clause", + "node_modules/@puppeteer/browsers/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" + "ms": "2.1.2" }, "engines": { - "node": ">= 10.17.0" + "node": ">=6.0" }, - "optionalDependencies": { - "@types/yauzl": "^2.9.1" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/@puppeteer/browsers/node_modules/get-stream": { - "version": "5.2.0", - "license": "MIT", + "node_modules/@puppeteer/browsers/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" + "yallist": "^4.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@puppeteer/browsers/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "license": "MIT", "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/@puppeteer/browsers/node_modules/string-width": { - "version": "4.2.3", - "license": "MIT", + "node_modules/@puppeteer/browsers/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "lru-cache": "^6.0.0" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@puppeteer/browsers/node_modules/tar-fs": { - "version": "3.0.4", - "license": "MIT", - "dependencies": { - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^3.1.5" - } - }, - "node_modules/@puppeteer/browsers/node_modules/tar-stream": { - "version": "3.1.7", - "license": "MIT", - "dependencies": { - "b4a": "^1.6.4", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" - } - }, - "node_modules/@puppeteer/browsers/node_modules/yargs": { - "version": "17.7.1", - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">=12" + "node": ">=10" } }, + "node_modules/@puppeteer/browsers/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/@radix-ui/number": { "version": "1.0.1", "dev": true, @@ -3921,11 +3875,11 @@ "license": "(Unlicense OR Apache-2.0)" }, "node_modules/@storybook/addon-actions": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "dependencies": { - "@storybook/core-events": "7.6.18", + "@storybook/core-events": "7.6.19", "@storybook/global": "^5.0.0", "@types/uuid": "^9.0.1", "dequal": "^2.0.2", @@ -3938,7 +3892,7 @@ } }, "node_modules/@storybook/addon-backgrounds": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "dependencies": { @@ -3952,11 +3906,11 @@ } }, "node_modules/@storybook/addon-controls": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "dependencies": { - "@storybook/blocks": "7.6.18", + "@storybook/blocks": "7.6.19", "lodash": "^4.17.21", "ts-dedent": "^2.0.0" }, @@ -3966,25 +3920,25 @@ } }, "node_modules/@storybook/addon-docs": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "dependencies": { "@jest/transform": "^29.3.1", "@mdx-js/react": "^2.1.5", - "@storybook/blocks": "7.6.18", - "@storybook/client-logger": "7.6.18", - "@storybook/components": "7.6.18", - "@storybook/csf-plugin": "7.6.18", - "@storybook/csf-tools": "7.6.18", + "@storybook/blocks": "7.6.19", + "@storybook/client-logger": "7.6.19", + "@storybook/components": "7.6.19", + "@storybook/csf-plugin": "7.6.19", + "@storybook/csf-tools": "7.6.19", "@storybook/global": "^5.0.0", "@storybook/mdx2-csf": "^1.0.0", - "@storybook/node-logger": "7.6.18", - "@storybook/postinstall": "7.6.18", - "@storybook/preview-api": "7.6.18", - "@storybook/react-dom-shim": "7.6.18", - "@storybook/theming": "7.6.18", - "@storybook/types": "7.6.18", + "@storybook/node-logger": "7.6.19", + "@storybook/postinstall": "7.6.19", + "@storybook/preview-api": "7.6.19", + "@storybook/react-dom-shim": "7.6.19", + "@storybook/theming": "7.6.19", + "@storybook/types": "7.6.19", "fs-extra": "^11.1.0", "remark-external-links": "^8.0.0", "remark-slug": "^6.0.0", @@ -4000,23 +3954,23 @@ } }, "node_modules/@storybook/addon-essentials": { - "version": "7.6.18", - "dev": true, - "license": "MIT", - "dependencies": { - "@storybook/addon-actions": "7.6.18", - "@storybook/addon-backgrounds": "7.6.18", - "@storybook/addon-controls": "7.6.18", - "@storybook/addon-docs": "7.6.18", - "@storybook/addon-highlight": "7.6.18", - "@storybook/addon-measure": "7.6.18", - "@storybook/addon-outline": "7.6.18", - "@storybook/addon-toolbars": "7.6.18", - "@storybook/addon-viewport": "7.6.18", - "@storybook/core-common": "7.6.18", - "@storybook/manager-api": "7.6.18", - "@storybook/node-logger": "7.6.18", - "@storybook/preview-api": "7.6.18", + "version": "7.6.19", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/addon-actions": "7.6.19", + "@storybook/addon-backgrounds": "7.6.19", + "@storybook/addon-controls": "7.6.19", + "@storybook/addon-docs": "7.6.19", + "@storybook/addon-highlight": "7.6.19", + "@storybook/addon-measure": "7.6.19", + "@storybook/addon-outline": "7.6.19", + "@storybook/addon-toolbars": "7.6.19", + "@storybook/addon-viewport": "7.6.19", + "@storybook/core-common": "7.6.19", + "@storybook/manager-api": "7.6.19", + "@storybook/node-logger": "7.6.19", + "@storybook/preview-api": "7.6.19", "ts-dedent": "^2.0.0" }, "funding": { @@ -4029,7 +3983,7 @@ } }, "node_modules/@storybook/addon-highlight": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "dependencies": { @@ -4041,12 +3995,12 @@ } }, "node_modules/@storybook/addon-interactions": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "dependencies": { "@storybook/global": "^5.0.0", - "@storybook/types": "7.6.18", + "@storybook/types": "7.6.19", "jest-mock": "^27.0.6", "polished": "^4.2.2", "ts-dedent": "^2.2.0" @@ -4057,7 +4011,7 @@ } }, "node_modules/@storybook/addon-links": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "dependencies": { @@ -4079,7 +4033,7 @@ } }, "node_modules/@storybook/addon-measure": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "dependencies": { @@ -4092,7 +4046,7 @@ } }, "node_modules/@storybook/addon-outline": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "dependencies": { @@ -4157,7 +4111,7 @@ } }, "node_modules/@storybook/addon-toolbars": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "funding": { @@ -4166,7 +4120,7 @@ } }, "node_modules/@storybook/addon-viewport": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "dependencies": { @@ -4305,21 +4259,21 @@ } }, "node_modules/@storybook/blocks": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "dependencies": { - "@storybook/channels": "7.6.18", - "@storybook/client-logger": "7.6.18", - "@storybook/components": "7.6.18", - "@storybook/core-events": "7.6.18", + "@storybook/channels": "7.6.19", + "@storybook/client-logger": "7.6.19", + "@storybook/components": "7.6.19", + "@storybook/core-events": "7.6.19", "@storybook/csf": "^0.1.2", - "@storybook/docs-tools": "7.6.18", + "@storybook/docs-tools": "7.6.19", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "7.6.18", - "@storybook/preview-api": "7.6.18", - "@storybook/theming": "7.6.18", - "@storybook/types": "7.6.18", + "@storybook/manager-api": "7.6.19", + "@storybook/preview-api": "7.6.19", + "@storybook/theming": "7.6.19", + "@storybook/types": "7.6.19", "@types/lodash": "^4.14.167", "color-convert": "^2.0.1", "dequal": "^2.0.2", @@ -4343,19 +4297,19 @@ } }, "node_modules/@storybook/builder-webpack5": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "dependencies": { "@babel/core": "^7.23.2", - "@storybook/channels": "7.6.18", - "@storybook/client-logger": "7.6.18", - "@storybook/core-common": "7.6.18", - "@storybook/core-events": "7.6.18", - "@storybook/core-webpack": "7.6.18", - "@storybook/node-logger": "7.6.18", - "@storybook/preview": "7.6.18", - "@storybook/preview-api": "7.6.18", + "@storybook/channels": "7.6.19", + "@storybook/client-logger": "7.6.19", + "@storybook/core-common": "7.6.19", + "@storybook/core-events": "7.6.19", + "@storybook/core-webpack": "7.6.19", + "@storybook/node-logger": "7.6.19", + "@storybook/preview": "7.6.19", + "@storybook/preview-api": "7.6.19", "@swc/core": "^1.3.82", "@types/node": "^18.0.0", "@types/semver": "^7.3.4", @@ -4397,31 +4351,17 @@ } }, "node_modules/@storybook/builder-webpack5/node_modules/@types/node": { - "version": "18.19.31", + "version": "18.19.34", "dev": true, "license": "MIT", "dependencies": { "undici-types": "~5.26.4" } }, - "node_modules/@storybook/builder-webpack5/node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@storybook/builder-webpack5/node_modules/semver": { - "version": "7.6.0", + "version": "7.6.2", "dev": true, "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -4429,18 +4369,13 @@ "node": ">=10" } }, - "node_modules/@storybook/builder-webpack5/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, "node_modules/@storybook/channels": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "dependencies": { - "@storybook/client-logger": "7.6.18", - "@storybook/core-events": "7.6.18", + "@storybook/client-logger": "7.6.19", + "@storybook/core-events": "7.6.19", "@storybook/global": "^5.0.0", "qs": "^6.10.0", "telejson": "^7.2.0", @@ -4452,7 +4387,7 @@ } }, "node_modules/@storybook/client-logger": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "dependencies": { @@ -4464,17 +4399,17 @@ } }, "node_modules/@storybook/components": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "dependencies": { "@radix-ui/react-select": "^1.2.2", "@radix-ui/react-toolbar": "^1.0.4", - "@storybook/client-logger": "7.6.18", + "@storybook/client-logger": "7.6.19", "@storybook/csf": "^0.1.2", "@storybook/global": "^5.0.0", - "@storybook/theming": "7.6.18", - "@storybook/types": "7.6.18", + "@storybook/theming": "7.6.19", + "@storybook/types": "7.6.19", "memoizerific": "^1.11.3", "use-resize-observer": "^9.1.0", "util-deprecate": "^1.0.2" @@ -4489,12 +4424,12 @@ } }, "node_modules/@storybook/core-client": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "dependencies": { - "@storybook/client-logger": "7.6.18", - "@storybook/preview-api": "7.6.18" + "@storybook/client-logger": "7.6.19", + "@storybook/preview-api": "7.6.19" }, "funding": { "type": "opencollective", @@ -4502,13 +4437,13 @@ } }, "node_modules/@storybook/core-common": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "dependencies": { - "@storybook/core-events": "7.6.18", - "@storybook/node-logger": "7.6.18", - "@storybook/types": "7.6.18", + "@storybook/core-events": "7.6.19", + "@storybook/node-logger": "7.6.19", + "@storybook/types": "7.6.19", "@types/find-cache-dir": "^3.2.1", "@types/node": "^18.0.0", "@types/node-fetch": "^2.6.4", @@ -4536,7 +4471,7 @@ } }, "node_modules/@storybook/core-common/node_modules/@types/node": { - "version": "18.19.31", + "version": "18.19.34", "dev": true, "license": "MIT", "dependencies": { @@ -4592,7 +4527,7 @@ } }, "node_modules/@storybook/core-events": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "dependencies": { @@ -4604,13 +4539,13 @@ } }, "node_modules/@storybook/core-webpack": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "dependencies": { - "@storybook/core-common": "7.6.18", - "@storybook/node-logger": "7.6.18", - "@storybook/types": "7.6.18", + "@storybook/core-common": "7.6.19", + "@storybook/node-logger": "7.6.19", + "@storybook/types": "7.6.19", "@types/node": "^18.0.0", "ts-dedent": "^2.0.0" }, @@ -4620,7 +4555,7 @@ } }, "node_modules/@storybook/core-webpack/node_modules/@types/node": { - "version": "18.19.31", + "version": "18.19.34", "dev": true, "license": "MIT", "dependencies": { @@ -4628,7 +4563,7 @@ } }, "node_modules/@storybook/csf": { - "version": "0.1.5", + "version": "0.1.8", "dev": true, "license": "MIT", "dependencies": { @@ -4636,11 +4571,11 @@ } }, "node_modules/@storybook/csf-plugin": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "dependencies": { - "@storybook/csf-tools": "7.6.18", + "@storybook/csf-tools": "7.6.19", "unplugin": "^1.3.1" }, "funding": { @@ -4649,7 +4584,7 @@ } }, "node_modules/@storybook/csf-tools": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "dependencies": { @@ -4658,7 +4593,7 @@ "@babel/traverse": "^7.23.2", "@babel/types": "^7.23.0", "@storybook/csf": "^0.1.2", - "@storybook/types": "7.6.18", + "@storybook/types": "7.6.19", "fs-extra": "^11.1.0", "recast": "^0.23.1", "ts-dedent": "^2.0.0" @@ -4669,13 +4604,13 @@ } }, "node_modules/@storybook/docs-tools": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "dependencies": { - "@storybook/core-common": "7.6.18", - "@storybook/preview-api": "7.6.18", - "@storybook/types": "7.6.18", + "@storybook/core-common": "7.6.19", + "@storybook/preview-api": "7.6.19", + "@storybook/types": "7.6.19", "@types/doctrine": "^0.0.3", "assert": "^2.1.0", "doctrine": "^3.0.0", @@ -4692,15 +4627,15 @@ "license": "MIT" }, "node_modules/@storybook/instrumenter": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "dependencies": { - "@storybook/channels": "7.6.18", - "@storybook/client-logger": "7.6.18", - "@storybook/core-events": "7.6.18", + "@storybook/channels": "7.6.19", + "@storybook/client-logger": "7.6.19", + "@storybook/core-events": "7.6.19", "@storybook/global": "^5.0.0", - "@storybook/preview-api": "7.6.18", + "@storybook/preview-api": "7.6.19", "@vitest/utils": "^0.34.6", "util": "^0.12.4" }, @@ -4710,18 +4645,18 @@ } }, "node_modules/@storybook/manager-api": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "dependencies": { - "@storybook/channels": "7.6.18", - "@storybook/client-logger": "7.6.18", - "@storybook/core-events": "7.6.18", + "@storybook/channels": "7.6.19", + "@storybook/client-logger": "7.6.19", + "@storybook/core-events": "7.6.19", "@storybook/csf": "^0.1.2", "@storybook/global": "^5.0.0", - "@storybook/router": "7.6.18", - "@storybook/theming": "7.6.18", - "@storybook/types": "7.6.18", + "@storybook/router": "7.6.19", + "@storybook/theming": "7.6.19", + "@storybook/types": "7.6.19", "dequal": "^2.0.2", "lodash": "^4.17.21", "memoizerific": "^1.11.3", @@ -4740,7 +4675,7 @@ "license": "MIT" }, "node_modules/@storybook/node-logger": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "funding": { @@ -4749,7 +4684,7 @@ } }, "node_modules/@storybook/postinstall": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "funding": { @@ -4758,17 +4693,17 @@ } }, "node_modules/@storybook/preset-react-webpack": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "dependencies": { "@babel/preset-flow": "^7.22.15", "@babel/preset-react": "^7.22.15", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.11", - "@storybook/core-webpack": "7.6.18", - "@storybook/docs-tools": "7.6.18", - "@storybook/node-logger": "7.6.18", - "@storybook/react": "7.6.18", + "@storybook/core-webpack": "7.6.19", + "@storybook/docs-tools": "7.6.19", + "@storybook/node-logger": "7.6.19", + "@storybook/react": "7.6.19", "@storybook/react-docgen-typescript-plugin": "1.0.6--canary.9.0c3f3b7.0", "@types/node": "^18.0.0", "@types/semver": "^7.3.4", @@ -4802,31 +4737,17 @@ } }, "node_modules/@storybook/preset-react-webpack/node_modules/@types/node": { - "version": "18.19.31", + "version": "18.19.34", "dev": true, "license": "MIT", "dependencies": { "undici-types": "~5.26.4" } }, - "node_modules/@storybook/preset-react-webpack/node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@storybook/preset-react-webpack/node_modules/semver": { - "version": "7.6.0", + "version": "7.6.2", "dev": true, "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -4834,13 +4755,8 @@ "node": ">=10" } }, - "node_modules/@storybook/preset-react-webpack/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, "node_modules/@storybook/preview": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "funding": { @@ -4849,16 +4765,16 @@ } }, "node_modules/@storybook/preview-api": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "dependencies": { - "@storybook/channels": "7.6.18", - "@storybook/client-logger": "7.6.18", - "@storybook/core-events": "7.6.18", + "@storybook/channels": "7.6.19", + "@storybook/client-logger": "7.6.19", + "@storybook/core-events": "7.6.19", "@storybook/csf": "^0.1.2", "@storybook/global": "^5.0.0", - "@storybook/types": "7.6.18", + "@storybook/types": "7.6.19", "@types/qs": "^6.9.5", "dequal": "^2.0.2", "lodash": "^4.17.21", @@ -4874,17 +4790,17 @@ } }, "node_modules/@storybook/react": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "dependencies": { - "@storybook/client-logger": "7.6.18", - "@storybook/core-client": "7.6.18", - "@storybook/docs-tools": "7.6.18", + "@storybook/client-logger": "7.6.19", + "@storybook/core-client": "7.6.19", + "@storybook/docs-tools": "7.6.19", "@storybook/global": "^5.0.0", - "@storybook/preview-api": "7.6.18", - "@storybook/react-dom-shim": "7.6.18", - "@storybook/types": "7.6.18", + "@storybook/preview-api": "7.6.19", + "@storybook/react-dom-shim": "7.6.19", + "@storybook/types": "7.6.19", "@types/escodegen": "^0.0.6", "@types/estree": "^0.0.51", "@types/node": "^18.0.0", @@ -4937,7 +4853,7 @@ } }, "node_modules/@storybook/react-dom-shim": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "funding": { @@ -4950,13 +4866,13 @@ } }, "node_modules/@storybook/react-webpack5": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "dependencies": { - "@storybook/builder-webpack5": "7.6.18", - "@storybook/preset-react-webpack": "7.6.18", - "@storybook/react": "7.6.18", + "@storybook/builder-webpack5": "7.6.19", + "@storybook/preset-react-webpack": "7.6.19", + "@storybook/react": "7.6.19", "@types/node": "^18.0.0" }, "engines": { @@ -4982,7 +4898,7 @@ } }, "node_modules/@storybook/react-webpack5/node_modules/@types/node": { - "version": "18.19.31", + "version": "18.19.34", "dev": true, "license": "MIT", "dependencies": { @@ -4990,7 +4906,7 @@ } }, "node_modules/@storybook/react/node_modules/@types/node": { - "version": "18.19.31", + "version": "18.19.34", "dev": true, "license": "MIT", "dependencies": { @@ -4998,11 +4914,11 @@ } }, "node_modules/@storybook/router": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "dependencies": { - "@storybook/client-logger": "7.6.18", + "@storybook/client-logger": "7.6.19", "memoizerific": "^1.11.3", "qs": "^6.10.0" }, @@ -5024,12 +4940,12 @@ } }, "node_modules/@storybook/theming": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "dependencies": { "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@storybook/client-logger": "7.6.18", + "@storybook/client-logger": "7.6.19", "@storybook/global": "^5.0.0", "memoizerific": "^1.11.3" }, @@ -5043,11 +4959,11 @@ } }, "node_modules/@storybook/types": { - "version": "7.6.18", + "version": "7.6.19", "dev": true, "license": "MIT", "dependencies": { - "@storybook/channels": "7.6.18", + "@storybook/channels": "7.6.19", "@types/babel__core": "^7.0.0", "@types/express": "^4.7.0", "file-system-cache": "2.3.0" @@ -5301,13 +5217,13 @@ } }, "node_modules/@swc/core": { - "version": "1.4.17", + "version": "1.5.29", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@swc/counter": "^0.1.2", - "@swc/types": "^0.1.5" + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.8" }, "engines": { "node": ">=10" @@ -5317,19 +5233,19 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.4.17", - "@swc/core-darwin-x64": "1.4.17", - "@swc/core-linux-arm-gnueabihf": "1.4.17", - "@swc/core-linux-arm64-gnu": "1.4.17", - "@swc/core-linux-arm64-musl": "1.4.17", - "@swc/core-linux-x64-gnu": "1.4.17", - "@swc/core-linux-x64-musl": "1.4.17", - "@swc/core-win32-arm64-msvc": "1.4.17", - "@swc/core-win32-ia32-msvc": "1.4.17", - "@swc/core-win32-x64-msvc": "1.4.17" + "@swc/core-darwin-arm64": "1.5.29", + "@swc/core-darwin-x64": "1.5.29", + "@swc/core-linux-arm-gnueabihf": "1.5.29", + "@swc/core-linux-arm64-gnu": "1.5.29", + "@swc/core-linux-arm64-musl": "1.5.29", + "@swc/core-linux-x64-gnu": "1.5.29", + "@swc/core-linux-x64-musl": "1.5.29", + "@swc/core-win32-arm64-msvc": "1.5.29", + "@swc/core-win32-ia32-msvc": "1.5.29", + "@swc/core-win32-x64-msvc": "1.5.29" }, "peerDependencies": { - "@swc/helpers": "^0.5.0" + "@swc/helpers": "*" }, "peerDependenciesMeta": { "@swc/helpers": { @@ -5338,7 +5254,7 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.4.17", + "version": "1.5.29", "cpu": [ "arm64" ], @@ -5358,7 +5274,7 @@ "license": "Apache-2.0" }, "node_modules/@swc/types": { - "version": "0.1.6", + "version": "0.1.8", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -5615,7 +5531,8 @@ }, "node_modules/@tootallnate/quickjs-emscripten": { "version": "0.23.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==" }, "node_modules/@trysound/sax": { "version": "0.2.0", @@ -5660,7 +5577,7 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.20.5", + "version": "7.20.6", "dev": true, "license": "MIT", "dependencies": { @@ -5703,11 +5620,6 @@ "@types/har-format": "*" } }, - "node_modules/@types/cli-color": { - "version": "2.0.6", - "dev": true, - "license": "MIT" - }, "node_modules/@types/connect": { "version": "3.4.38", "dev": true, @@ -5815,7 +5727,7 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.19.0", + "version": "4.19.3", "dev": true, "license": "MIT", "dependencies": { @@ -5987,7 +5899,7 @@ } }, "node_modules/@types/lodash": { - "version": "4.17.0", + "version": "4.17.5", "dev": true, "license": "MIT" }, @@ -6002,7 +5914,7 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.12.7", + "version": "20.14.2", "license": "MIT", "dependencies": { "undici-types": "~5.26.4" @@ -6059,7 +5971,7 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "18.3.1", + "version": "18.3.3", "dev": true, "license": "MIT", "dependencies": { @@ -6168,7 +6080,7 @@ "license": "MIT" }, "node_modules/@types/stylis": { - "version": "4.2.0", + "version": "4.2.5", "dev": true, "license": "MIT", "peer": true @@ -6227,7 +6139,8 @@ }, "node_modules/@types/yauzl": { "version": "2.10.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", "optional": true, "dependencies": { "@types/node": "*" @@ -6266,24 +6179,10 @@ } } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.6.0", + "version": "7.6.2", "dev": true, "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -6291,11 +6190,6 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, "node_modules/@typescript-eslint/parser": { "version": "5.62.0", "dev": true, @@ -6402,24 +6296,10 @@ } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.6.0", + "version": "7.6.2", "dev": true, "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -6427,11 +6307,6 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, "node_modules/@typescript-eslint/utils": { "version": "5.62.0", "dev": true, @@ -6457,24 +6332,10 @@ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@typescript-eslint/utils/node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.6.0", + "version": "7.6.2", "dev": true, "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -6482,11 +6343,6 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/utils/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, "node_modules/@typescript-eslint/visitor-keys": { "version": "5.62.0", "dev": true, @@ -6818,6 +6674,7 @@ }, "node_modules/agent-base": { "version": "6.0.2", + "dev": true, "license": "MIT", "dependencies": { "debug": "4" @@ -6858,7 +6715,7 @@ } }, "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.13.0", + "version": "8.16.0", "dev": true, "license": "MIT", "dependencies": { @@ -6910,6 +6767,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ansi-html": { + "version": "0.0.9", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "license": "Apache-2.0", + "bin": { + "ansi-html": "bin/ansi-html" + } + }, "node_modules/ansi-html-community": { "version": "0.0.8", "dev": true, @@ -7147,15 +7015,18 @@ } }, "node_modules/array.prototype.tosorted": { - "version": "1.1.3", + "version": "1.1.4", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", + "call-bind": "^1.0.7", "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.1.0", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/arraybuffer.prototype.slice": { @@ -7280,7 +7151,8 @@ }, "node_modules/b4a": { "version": "1.6.6", - "license": "Apache-2.0" + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz", + "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==" }, "node_modules/babel-jest": { "version": "29.7.0", @@ -7366,32 +7238,6 @@ "webpack": ">=5" } }, - "node_modules/babel-loader/node_modules/ajv": { - "version": "8.13.0", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/babel-loader/node_modules/ajv-keywords": { - "version": "5.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, "node_modules/babel-loader/node_modules/find-cache-dir": { "version": "4.0.0", "dev": true, @@ -7422,11 +7268,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/babel-loader/node_modules/json-schema-traverse": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, "node_modules/babel-loader/node_modules/locate-path": { "version": "7.2.0", "dev": true, @@ -7491,24 +7332,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/babel-loader/node_modules/schema-utils": { - "version": "4.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/babel-loader/node_modules/yocto-queue": { "version": "1.0.0", "dev": true, @@ -7644,13 +7467,50 @@ }, "node_modules/balanced-match": { "version": "1.0.2", + "dev": true, "license": "MIT" }, "node_modules/bare-events": { - "version": "2.2.2", - "license": "Apache-2.0", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.4.2.tgz", + "integrity": "sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==", + "optional": true + }, + "node_modules/bare-fs": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.1.tgz", + "integrity": "sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA==", + "optional": true, + "dependencies": { + "bare-events": "^2.0.0", + "bare-path": "^2.0.0", + "bare-stream": "^2.0.0" + } + }, + "node_modules/bare-os": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.0.tgz", + "integrity": "sha512-v8DTT08AS/G0F9xrhyLtepoo9EJBJ85FRSMbu1pQUlAf6A8T0tEEQGMVObWeqpjhSPXsE0VGlluFBJu2fdoTNg==", "optional": true }, + "node_modules/bare-path": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz", + "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==", + "optional": true, + "dependencies": { + "bare-os": "^2.1.0" + } + }, + "node_modules/bare-stream": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.1.3.tgz", + "integrity": "sha512-tiDAH9H/kP+tvNO5sczyn9ZAA7utrSMobyDchsnyyXBuUe2FSQWbxhtuHB8jwpHYYevVo2UJpcmvvjrbHboUUQ==", + "optional": true, + "dependencies": { + "streamx": "^2.18.0" + } + }, "node_modules/base64-js": { "version": "1.5.1", "funding": [ @@ -7671,7 +7531,8 @@ }, "node_modules/basic-ftp": { "version": "5.0.5", - "license": "MIT", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", + "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", "engines": { "node": ">=10.0.0" } @@ -7701,10 +7562,10 @@ } }, "node_modules/bl": { - "version": "4.1.0", + "version": "5.1.0", "license": "MIT", "dependencies": { - "buffer": "^5.5.0", + "buffer": "^6.0.3", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } @@ -7789,9 +7650,8 @@ }, "node_modules/braces": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -7804,7 +7664,7 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.23.0", + "version": "4.23.1", "dev": true, "funding": [ { @@ -7822,10 +7682,10 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", + "caniuse-lite": "^1.0.30001629", + "electron-to-chromium": "^1.4.796", "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" + "update-browserslist-db": "^1.0.16" }, "bin": { "browserslist": "cli.js" @@ -7843,7 +7703,7 @@ } }, "node_modules/buffer": { - "version": "5.7.1", + "version": "6.0.3", "funding": [ { "type": "github", @@ -7861,12 +7721,13 @@ "license": "MIT", "dependencies": { "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "ieee754": "^1.2.1" } }, "node_modules/buffer-crc32": { "version": "0.2.13", - "license": "MIT", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "engines": { "node": "*" } @@ -7876,6 +7737,19 @@ "dev": true, "license": "MIT" }, + "node_modules/bufferutil": { + "version": "4.0.8", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, "node_modules/builtin-modules": { "version": "3.3.0", "dev": true, @@ -7994,9 +7868,7 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001614", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001616.tgz", - "integrity": "sha512-RHVYKov7IcdNjVHJFNY/78RdG4oGVjbayxv8u5IO74Wv7Hlq4PnJE6mo/OjFijjVFNy5ijnCt6H3IIo4t+wfEw==", + "version": "1.0.30001633", "dev": true, "funding": [ { @@ -8011,7 +7883,8 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/case-sensitive-paths-webpack-plugin": { "version": "2.4.0", @@ -8076,7 +7949,7 @@ } }, "node_modules/chrome-trace-event": { - "version": "1.0.3", + "version": "1.0.4", "dev": true, "license": "MIT", "engines": { @@ -8084,10 +7957,13 @@ } }, "node_modules/chromium-bidi": { - "version": "0.4.20", - "license": "Apache-2.0", + "version": "0.5.24", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.24.tgz", + "integrity": "sha512-5xQNN2SVBdZv4TxeMLaI+PelrnZsHDhn8h2JtyriLr+0qHcZS8BMuo93qN6J1VmtmrgYP+rmcLHcbpnA8QJh+w==", "dependencies": { - "mitt": "3.0.1" + "mitt": "3.0.1", + "urlpattern-polyfill": "10.0.0", + "zod": "3.23.8" }, "peerDependencies": { "devtools-protocol": "*" @@ -8135,20 +8011,6 @@ "node": ">=0.10.0" } }, - "node_modules/cli-color": { - "version": "2.0.4", - "license": "ISC", - "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.64", - "es6-iterator": "^2.0.3", - "memoizee": "^0.4.15", - "timers-ext": "^0.1.7" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/cli-cursor": { "version": "4.0.0", "license": "MIT", @@ -8172,15 +8034,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cli-table": { - "version": "0.3.11", - "dependencies": { - "colors": "1.0.3" - }, - "engines": { - "node": ">= 0.2.0" - } - }, "node_modules/cli-truncate": { "version": "3.1.0", "dev": true, @@ -8326,13 +8179,6 @@ "dev": true, "license": "MIT" }, - "node_modules/colors": { - "version": "1.0.3", - "license": "MIT", - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/combined-stream": { "version": "1.0.8", "dev": true, @@ -8437,6 +8283,11 @@ "node": ">=0.8" } }, + "node_modules/consola": { + "version": "2.15.3", + "dev": true, + "license": "MIT" + }, "node_modules/constants-browserify": { "version": "1.0.0", "dev": true, @@ -8521,32 +8372,6 @@ "webpack": "^5.1.0" } }, - "node_modules/copy-webpack-plugin/node_modules/ajv": { - "version": "8.13.0", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/copy-webpack-plugin/node_modules/ajv-keywords": { - "version": "5.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, "node_modules/copy-webpack-plugin/node_modules/globby": { "version": "13.2.2", "dev": true, @@ -8565,29 +8390,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/copy-webpack-plugin/node_modules/json-schema-traverse": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/copy-webpack-plugin/node_modules/schema-utils": { - "version": "4.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/copy-webpack-plugin/node_modules/slash": { "version": "4.0.0", "dev": true, @@ -8600,7 +8402,7 @@ } }, "node_modules/core-js-compat": { - "version": "3.37.0", + "version": "3.37.1", "dev": true, "license": "MIT", "dependencies": { @@ -8612,7 +8414,7 @@ } }, "node_modules/core-js-pure": { - "version": "3.37.0", + "version": "3.37.1", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -8751,13 +8553,6 @@ "yarn": ">=1" } }, - "node_modules/cross-fetch": { - "version": "4.0.0", - "license": "MIT", - "dependencies": { - "node-fetch": "^2.6.12" - } - }, "node_modules/cross-spawn": { "version": "7.0.3", "dev": true, @@ -8814,24 +8609,10 @@ } } }, - "node_modules/css-loader/node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/css-loader/node_modules/semver": { - "version": "7.6.0", + "version": "7.6.2", "dev": true, "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -8839,11 +8620,6 @@ "node": ">=10" } }, - "node_modules/css-loader/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, "node_modules/css-select": { "version": "4.3.0", "dev": true, @@ -8981,17 +8757,6 @@ "node": ">=4.0.0" } }, - "node_modules/d": { - "version": "1.0.2", - "license": "ISC", - "dependencies": { - "es5-ext": "^0.10.64", - "type": "^2.7.2" - }, - "engines": { - "node": ">=0.12" - } - }, "node_modules/d3-array": { "version": "3.2.4", "license": "ISC", @@ -9102,7 +8867,8 @@ }, "node_modules/data-uri-to-buffer": { "version": "6.0.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", "engines": { "node": ">= 14" } @@ -9169,7 +8935,7 @@ } }, "node_modules/debug": { - "version": "4.3.4", + "version": "4.3.5", "license": "MIT", "dependencies": { "ms": "2.1.2" @@ -9320,7 +9086,8 @@ }, "node_modules/degenerator": { "version": "5.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", "dependencies": { "ast-types": "^0.13.4", "escodegen": "^2.1.0", @@ -9332,7 +9099,8 @@ }, "node_modules/degenerator/node_modules/ast-types": { "version": "0.13.4", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", "dependencies": { "tslib": "^2.0.1" }, @@ -9403,7 +9171,7 @@ "license": "MIT" }, "node_modules/devtools-protocol": { - "version": "0.0.1147663", + "version": "0.0.1282316", "license": "BSD-3-Clause" }, "node_modules/didyoumean": { @@ -9591,7 +9359,7 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.4.751", + "version": "1.4.801", "dev": true, "license": "ISC" }, @@ -9645,7 +9413,7 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.16.0", + "version": "5.17.0", "dev": true, "license": "MIT", "dependencies": { @@ -9667,6 +9435,13 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/env-paths": { + "version": "2.2.1", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/envinfo": { "version": "7.13.0", "dev": true, @@ -9828,7 +9603,7 @@ } }, "node_modules/es-module-lexer": { - "version": "1.5.2", + "version": "1.5.3", "dev": true, "license": "MIT" }, @@ -9880,57 +9655,13 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es5-ext": { - "version": "0.10.64", + "node_modules/esbuild": { + "version": "0.18.20", + "dev": true, "hasInstallScript": true, - "license": "ISC", - "dependencies": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "esniff": "^2.0.1", - "next-tick": "^1.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/es6-iterator": { - "version": "2.0.3", - "license": "MIT", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/es6-symbol": { - "version": "3.1.4", - "license": "ISC", - "dependencies": { - "d": "^1.0.2", - "ext": "^1.7.0" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/es6-weak-map": { - "version": "2.0.3", - "license": "ISC", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/esbuild": { - "version": "0.18.20", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" }, "engines": { "node": ">=12" @@ -10278,24 +10009,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-plugin-jsdoc/node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/eslint-plugin-jsdoc/node_modules/semver": { - "version": "7.6.0", + "version": "7.6.2", "dev": true, "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -10303,11 +10020,6 @@ "node": ">=10" } }, - "node_modules/eslint-plugin-jsdoc/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, "node_modules/eslint-plugin-jsx-a11y": { "version": "6.8.0", "dev": true, @@ -10366,28 +10078,28 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.34.1", + "version": "7.34.2", "dev": true, "license": "MIT", "dependencies": { - "array-includes": "^3.1.7", - "array.prototype.findlast": "^1.2.4", + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", "array.prototype.flatmap": "^1.3.2", "array.prototype.toreversed": "^1.1.2", "array.prototype.tosorted": "^1.1.3", "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.0.17", + "es-iterator-helpers": "^1.0.19", "estraverse": "^5.3.0", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", - "object.entries": "^1.1.7", - "object.fromentries": "^2.0.7", - "object.hasown": "^1.1.3", - "object.values": "^1.1.7", + "object.entries": "^1.1.8", + "object.fromentries": "^2.0.8", + "object.hasown": "^1.1.4", + "object.values": "^1.2.0", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.10" + "string.prototype.matchall": "^4.0.11" }, "engines": { "node": ">=4" @@ -10605,19 +10317,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/esniff": { - "version": "2.0.1", - "license": "ISC", - "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.62", - "event-emitter": "^0.3.5", - "type": "^2.7.2" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/espree": { "version": "9.6.1", "dev": true, @@ -10700,14 +10399,6 @@ "node": ">= 0.6" } }, - "node_modules/event-emitter": { - "version": "0.3.5", - "license": "MIT", - "dependencies": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, "node_modules/event-stream": { "version": "3.3.4", "dev": true, @@ -10851,18 +10542,44 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/ext": { - "version": "1.7.0", - "license": "ISC", - "dependencies": { - "type": "^2.7.2" - } - }, "node_modules/extend": { "version": "3.0.2", "dev": true, "license": "MIT" }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extract-zip/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "dev": true, @@ -10875,7 +10592,8 @@ }, "node_modules/fast-fifo": { "version": "1.3.2", - "license": "MIT" + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==" }, "node_modules/fast-glob": { "version": "3.3.2", @@ -10919,7 +10637,7 @@ "license": "MIT" }, "node_modules/fast-xml-parser": { - "version": "4.3.6", + "version": "4.4.0", "funding": [ { "type": "github", @@ -10975,7 +10693,8 @@ }, "node_modules/fd-slicer": { "version": "1.1.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dependencies": { "pend": "~1.2.0" } @@ -11019,9 +10738,8 @@ }, "node_modules/fill-range": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -11236,7 +10954,7 @@ } }, "node_modules/foreground-child": { - "version": "3.1.1", + "version": "3.2.0", "dev": true, "license": "ISC", "dependencies": { @@ -11342,24 +11060,27 @@ "node": ">=8" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/lru-cache": { - "version": "6.0.0", + "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { + "version": "3.3.0", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "yallist": "^4.0.0" + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" }, "engines": { - "node": ">=10" + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/fork-ts-checker-webpack-plugin/node_modules/semver": { - "version": "7.6.0", + "version": "7.6.2", "dev": true, "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -11378,11 +11099,6 @@ "node": ">=8" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, "node_modules/fork-ts-checker-webpack-plugin/node_modules/yaml": { "version": "1.10.2", "dev": true, @@ -11437,10 +11153,6 @@ "dev": true, "license": "MIT" }, - "node_modules/fs-constants": { - "version": "1.0.0", - "license": "MIT" - }, "node_modules/fs-extra": { "version": "11.2.0", "license": "MIT", @@ -11454,12 +11166,13 @@ } }, "node_modules/fs-monkey": { - "version": "1.0.5", + "version": "1.0.6", "dev": true, "license": "Unlicense" }, "node_modules/fs.realpath": { "version": "1.0.0", + "dev": true, "license": "ISC" }, "node_modules/fsevents": { @@ -11591,7 +11304,7 @@ } }, "node_modules/get-tsconfig": { - "version": "4.7.3", + "version": "4.7.5", "dev": true, "license": "MIT", "dependencies": { @@ -11603,7 +11316,8 @@ }, "node_modules/get-uri": { "version": "6.0.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz", + "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", "dependencies": { "basic-ftp": "^5.0.2", "data-uri-to-buffer": "^6.0.2", @@ -11620,21 +11334,21 @@ "license": "ISC" }, "node_modules/glob": { - "version": "10.3.12", + "version": "10.4.1", "dev": true, "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.3.6", - "minimatch": "^9.0.1", - "minipass": "^7.0.4", - "path-scurry": "^1.10.2" + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -12152,6 +11866,7 @@ }, "node_modules/https-proxy-agent": { "version": "5.0.1", + "dev": true, "license": "MIT", "dependencies": { "agent-base": "6", @@ -12404,12 +12119,12 @@ } }, "node_modules/intl-messageformat": { - "version": "10.5.11", + "version": "10.5.14", "license": "BSD-3-Clause", "dependencies": { - "@formatjs/ecma402-abstract": "1.18.2", + "@formatjs/ecma402-abstract": "2.0.0", "@formatjs/fast-memoize": "2.2.0", - "@formatjs/icu-messageformat-parser": "2.7.6", + "@formatjs/icu-messageformat-parser": "2.7.8", "tslib": "^2.4.0" } }, @@ -12423,7 +12138,8 @@ }, "node_modules/ip-address": { "version": "9.0.5", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", "dependencies": { "jsbn": "1.1.0", "sprintf-js": "^1.1.3" @@ -12434,7 +12150,8 @@ }, "node_modules/ip-address/node_modules/sprintf-js": { "version": "1.1.3", - "license": "BSD-3-Clause" + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" }, "node_modules/ipaddr.js": { "version": "1.9.1", @@ -12677,6 +12394,23 @@ "node": ">=0.10.0" } }, + "node_modules/is-gzip": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/is-interactive": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-map": { "version": "2.0.3", "dev": true, @@ -12716,9 +12450,8 @@ }, "node_modules/is-number": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -12769,10 +12502,6 @@ "dev": true, "license": "MIT" }, - "node_modules/is-promise": { - "version": "2.2.2", - "license": "MIT" - }, "node_modules/is-regex": { "version": "1.1.4", "dev": true, @@ -12866,6 +12595,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-unicode-supported": { + "version": "1.3.0", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-utf8": { "version": "0.2.1", "dev": true, @@ -12987,17 +12726,6 @@ "node": ">=8" } }, - "node_modules/istanbul-lib-report/node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/istanbul-lib-report/node_modules/make-dir": { "version": "4.0.0", "dev": true, @@ -13013,12 +12741,9 @@ } }, "node_modules/istanbul-lib-report/node_modules/semver": { - "version": "7.6.0", + "version": "7.6.2", "dev": true, "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -13037,11 +12762,6 @@ "node": ">=8" } }, - "node_modules/istanbul-lib-report/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, "node_modules/istanbul-lib-source-maps": { "version": "4.0.1", "dev": true, @@ -13088,7 +12808,7 @@ } }, "node_modules/jackspeak": { - "version": "2.3.6", + "version": "3.4.0", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -14476,17 +14196,6 @@ "node": ">=8" } }, - "node_modules/jest-snapshot/node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/jest-snapshot/node_modules/pretty-format": { "version": "29.7.0", "dev": true, @@ -14517,12 +14226,9 @@ "license": "MIT" }, "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.6.0", + "version": "7.6.2", "dev": true, "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -14541,11 +14247,6 @@ "node": ">=8" } }, - "node_modules/jest-snapshot/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, "node_modules/jest-util": { "version": "29.7.0", "dev": true, @@ -14806,7 +14507,7 @@ } }, "node_modules/jiti": { - "version": "1.21.0", + "version": "1.21.6", "dev": true, "license": "MIT", "bin": { @@ -14831,7 +14532,8 @@ }, "node_modules/jsbn": { "version": "1.1.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" }, "node_modules/jsdoc-type-pratt-parser": { "version": "4.0.0", @@ -14983,10 +14685,6 @@ "version": "1.0.0", "license": "MIT" }, - "node_modules/jszip/node_modules/pako": { - "version": "1.0.11", - "license": "(MIT AND Zlib)" - }, "node_modules/jszip/node_modules/readable-stream": { "version": "2.3.8", "license": "MIT", @@ -15040,7 +14738,7 @@ } }, "node_modules/language-subtag-registry": { - "version": "0.3.22", + "version": "0.3.23", "dev": true, "license": "CC0-1.0" }, @@ -15247,6 +14945,22 @@ "node": ">=16" } }, + "node_modules/lint-staged/node_modules/debug": { + "version": "4.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/lint-staged/node_modules/execa": { "version": "7.2.0", "dev": true, @@ -15288,6 +15002,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lint-staged/node_modules/micromatch": { + "version": "4.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/lint-staged/node_modules/mimic-fn": { "version": "4.0.0", "dev": true, @@ -15461,6 +15187,30 @@ "dev": true, "license": "MIT" }, + "node_modules/log-symbols": { + "version": "5.1.0", + "license": "MIT", + "dependencies": { + "chalk": "^5.0.0", + "is-unicode-supported": "^1.1.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "5.3.0", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/log-update": { "version": "5.0.1", "dev": true, @@ -15575,13 +15325,6 @@ "yallist": "^3.0.2" } }, - "node_modules/lru-queue": { - "version": "0.1.0", - "license": "MIT", - "dependencies": { - "es5-ext": "~0.10.2" - } - }, "node_modules/lz-string": { "version": "1.5.0", "dev": true, @@ -15685,20 +15428,6 @@ "node": ">= 4.0.0" } }, - "node_modules/memoizee": { - "version": "0.4.15", - "license": "ISC", - "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.53", - "es6-weak-map": "^2.0.3", - "event-emitter": "^0.3.5", - "is-promise": "^2.2.2", - "lru-queue": "^0.1.0", - "next-tick": "^1.1.0", - "timers-ext": "^0.1.7" - } - }, "node_modules/memoizerific": { "version": "1.11.3", "dev": true, @@ -15741,11 +15470,11 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", + "version": "4.0.7", "dev": true, "license": "MIT", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -15829,7 +15558,8 @@ } }, "node_modules/minipass": { - "version": "7.0.4", + "version": "7.1.2", + "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -15837,11 +15567,8 @@ }, "node_modules/mitt": { "version": "3.0.1", - "license": "MIT" - }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "license": "MIT" + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==" }, "node_modules/ms": { "version": "2.1.2", @@ -15945,15 +15672,12 @@ }, "node_modules/netmask": { "version": "2.0.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", "engines": { "node": ">= 0.4.0" } }, - "node_modules/next-tick": { - "version": "1.1.0", - "license": "ISC" - }, "node_modules/nice-try": { "version": "1.0.5", "dev": true, @@ -16021,6 +15745,7 @@ }, "node_modules/node-fetch": { "version": "2.7.0", + "dev": true, "license": "MIT", "dependencies": { "whatwg-url": "^5.0.0" @@ -16039,14 +15764,17 @@ }, "node_modules/node-fetch/node_modules/tr46": { "version": "0.0.3", + "dev": true, "license": "MIT" }, "node_modules/node-fetch/node_modules/webidl-conversions": { "version": "3.0.1", + "dev": true, "license": "BSD-2-Clause" }, "node_modules/node-fetch/node_modules/whatwg-url": { "version": "5.0.0", + "dev": true, "license": "MIT", "dependencies": { "tr46": "~0.0.3", @@ -16061,6 +15789,17 @@ "node": ">= 6.13.0" } }, + "node_modules/node-gyp-build": { + "version": "4.8.1", + "license": "MIT", + "optional": true, + "peer": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, "node_modules/node-int64": { "version": "0.4.0", "dev": true, @@ -16235,7 +15974,7 @@ } }, "node_modules/nwsapi": { - "version": "2.2.9", + "version": "2.2.10", "dev": true, "license": "MIT" }, @@ -16458,6 +16197,79 @@ "node": ">= 0.8.0" } }, + "node_modules/ora": { + "version": "7.0.1", + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^4.0.0", + "cli-spinners": "^2.9.0", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^1.3.0", + "log-symbols": "^5.1.0", + "stdin-discarder": "^0.1.0", + "string-width": "^6.1.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/ansi-regex": { + "version": "6.0.1", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "5.3.0", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/emoji-regex": { + "version": "10.3.0", + "license": "MIT" + }, + "node_modules/ora/node_modules/string-width": { + "version": "6.1.0", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^10.2.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/strip-ansi": { + "version": "7.1.0", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/p-cancelable": { "version": "2.1.1", "license": "MIT", @@ -16538,7 +16350,8 @@ }, "node_modules/pac-proxy-agent": { "version": "7.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.1.tgz", + "integrity": "sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A==", "dependencies": { "@tootallnate/quickjs-emscripten": "^0.23.0", "agent-base": "^7.0.2", @@ -16555,7 +16368,8 @@ }, "node_modules/pac-proxy-agent/node_modules/agent-base": { "version": "7.1.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "dependencies": { "debug": "^4.3.4" }, @@ -16565,7 +16379,8 @@ }, "node_modules/pac-proxy-agent/node_modules/http-proxy-agent": { "version": "7.0.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" @@ -16576,7 +16391,8 @@ }, "node_modules/pac-proxy-agent/node_modules/https-proxy-agent": { "version": "7.0.4", - "license": "MIT", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", + "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", "dependencies": { "agent-base": "^7.0.2", "debug": "4" @@ -16587,7 +16403,8 @@ }, "node_modules/pac-resolver": { "version": "7.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", "dependencies": { "degenerator": "^5.0.0", "netmask": "^2.0.2" @@ -16596,6 +16413,10 @@ "node": ">= 14" } }, + "node_modules/pako": { + "version": "1.0.11", + "license": "(MIT AND Zlib)" + }, "node_modules/param-case": { "version": "3.0.4", "dev": true, @@ -16702,14 +16523,15 @@ "license": "MIT" }, "node_modules/path-scurry": { - "version": "1.10.2", + "version": "1.11.1", + "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -16717,6 +16539,7 @@ }, "node_modules/path-scurry/node_modules/lru-cache": { "version": "10.2.2", + "dev": true, "license": "ISC", "engines": { "node": "14 || >=16.14" @@ -16729,6 +16552,7 @@ }, "node_modules/path-type": { "version": "4.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -16747,10 +16571,11 @@ }, "node_modules/pend": { "version": "1.2.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" }, "node_modules/picocolors": { - "version": "1.0.0", + "version": "1.0.1", "license": "ISC" }, "node_modules/picomatch": { @@ -16917,7 +16742,7 @@ } }, "node_modules/postcss-load-config/node_modules/lilconfig": { - "version": "3.1.1", + "version": "3.1.2", "dev": true, "license": "MIT", "engines": { @@ -16928,7 +16753,7 @@ } }, "node_modules/postcss-load-config/node_modules/yaml": { - "version": "2.4.2", + "version": "2.4.5", "dev": true, "license": "ISC", "bin": { @@ -16959,24 +16784,10 @@ "webpack": "^5.0.0" } }, - "node_modules/postcss-loader/node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/postcss-loader/node_modules/semver": { - "version": "7.6.0", + "version": "7.6.2", "dev": true, "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -16984,11 +16795,6 @@ "node": ">=10" } }, - "node_modules/postcss-loader/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, "node_modules/postcss-modules-extract-imports": { "version": "3.1.0", "dev": true, @@ -17063,7 +16869,7 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.0.16", + "version": "6.1.0", "dev": true, "license": "MIT", "dependencies": { @@ -17175,7 +16981,8 @@ }, "node_modules/progress": { "version": "2.0.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "engines": { "node": ">=0.4.0" } @@ -17225,17 +17032,18 @@ } }, "node_modules/proxy-agent": { - "version": "6.3.0", - "license": "MIT", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", + "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", "dependencies": { "agent-base": "^7.0.2", "debug": "^4.3.4", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.0", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.3", "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.0.0", + "pac-proxy-agent": "^7.0.1", "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.1" + "socks-proxy-agent": "^8.0.2" }, "engines": { "node": ">= 14" @@ -17243,7 +17051,8 @@ }, "node_modules/proxy-agent/node_modules/agent-base": { "version": "7.1.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "dependencies": { "debug": "^4.3.4" }, @@ -17253,7 +17062,8 @@ }, "node_modules/proxy-agent/node_modules/http-proxy-agent": { "version": "7.0.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" @@ -17264,7 +17074,8 @@ }, "node_modules/proxy-agent/node_modules/https-proxy-agent": { "version": "7.0.4", - "license": "MIT", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", + "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", "dependencies": { "agent-base": "^7.0.2", "debug": "4" @@ -17275,14 +17086,16 @@ }, "node_modules/proxy-agent/node_modules/lru-cache": { "version": "7.18.3", - "license": "ISC", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "engines": { "node": ">=12" } }, "node_modules/proxy-from-env": { "version": "1.1.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, "node_modules/prr": { "version": "1.0.1", @@ -17327,38 +17140,75 @@ } }, "node_modules/puppeteer": { - "version": "21.0.3", + "version": "22.12.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-22.12.0.tgz", + "integrity": "sha512-kyUYI12SyJIjf9UGTnHfhNMYv4oVK321Jb9QZDBiGVNx5453SplvbdKI7UrF+S//3RtCneuUFCyHxnvQXQjpxg==", "hasInstallScript": true, - "license": "Apache-2.0", "dependencies": { - "@puppeteer/browsers": "1.6.0", - "cosmiconfig": "8.2.0", - "puppeteer-core": "21.0.3" + "@puppeteer/browsers": "2.2.3", + "cosmiconfig": "9.0.0", + "devtools-protocol": "0.0.1299070", + "puppeteer-core": "22.12.0" + }, + "bin": { + "puppeteer": "lib/esm/puppeteer/node/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/puppeteer-core": { + "version": "22.12.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.12.0.tgz", + "integrity": "sha512-9gY+JwBW/Fp3/x9+cOGK7ZcwqjvtvY2xjqRqsAA0B3ZFMzBauVTSZ26iWTmvOQX2sk78TN/rd5rnetxVxmK5CQ==", + "dependencies": { + "@puppeteer/browsers": "2.2.3", + "chromium-bidi": "0.5.24", + "debug": "4.3.5", + "devtools-protocol": "0.0.1299070", + "ws": "8.17.1" }, "engines": { - "node": ">=16.3.0" + "node": ">=18" } }, + "node_modules/puppeteer-core/node_modules/devtools-protocol": { + "version": "0.0.1299070", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1299070.tgz", + "integrity": "sha512-+qtL3eX50qsJ7c+qVyagqi7AWMoQCBGNfoyJZMwm/NSXVqLYbuitrWEEIzxfUmTNy7//Xe8yhMmQ+elj3uAqSg==" + }, "node_modules/puppeteer/node_modules/argparse": { "version": "2.0.1", "license": "Python-2.0" }, "node_modules/puppeteer/node_modules/cosmiconfig": { - "version": "8.2.0", + "version": "9.0.0", "license": "MIT", "dependencies": { - "import-fresh": "^3.2.1", + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0" + "parse-json": "^5.2.0" }, "engines": { "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, + "node_modules/puppeteer/node_modules/devtools-protocol": { + "version": "0.0.1299070", + "license": "BSD-3-Clause" + }, "node_modules/puppeteer/node_modules/js-yaml": { "version": "4.1.0", "license": "MIT", @@ -17369,40 +17219,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/puppeteer/node_modules/puppeteer-core": { - "version": "21.0.3", - "license": "Apache-2.0", - "dependencies": { - "@puppeteer/browsers": "1.6.0", - "chromium-bidi": "0.4.20", - "cross-fetch": "4.0.0", - "debug": "4.3.4", - "devtools-protocol": "0.0.1147663", - "ws": "8.13.0" - }, - "engines": { - "node": ">=16.3.0" - } - }, - "node_modules/puppeteer/node_modules/ws": { - "version": "8.13.0", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/pure-rand": { "version": "6.1.0", "dev": true, @@ -17458,7 +17274,8 @@ }, "node_modules/queue-tick": { "version": "1.0.1", - "license": "MIT" + "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", + "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==" }, "node_modules/quick-lru": { "version": "5.1.1", @@ -17510,7 +17327,7 @@ } }, "node_modules/re-resizable": { - "version": "6.9.16", + "version": "6.9.17", "license": "MIT", "peerDependencies": { "react": "^16.13.1 || ^17.0.0 || ^18.0.0", @@ -17808,7 +17625,7 @@ } }, "node_modules/recast": { - "version": "0.23.6", + "version": "0.23.9", "dev": true, "license": "MIT", "dependencies": { @@ -18174,12 +17991,12 @@ } }, "node_modules/rfdc": { - "version": "1.3.1", + "version": "1.4.1", "dev": true, "license": "MIT" }, "node_modules/rimraf": { - "version": "5.0.5", + "version": "5.0.7", "dev": true, "license": "ISC", "dependencies": { @@ -18189,7 +18006,7 @@ "rimraf": "dist/esm/bin.mjs" }, "engines": { - "node": ">=14" + "node": ">=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -18310,7 +18127,7 @@ } }, "node_modules/sax": { - "version": "1.3.0", + "version": "1.4.1", "license": "ISC" }, "node_modules/saxes": { @@ -18332,22 +18149,54 @@ } }, "node_modules/schema-utils": { - "version": "3.3.0", + "version": "4.2.0", "dev": true, "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" }, "engines": { - "node": ">= 10.13.0" + "node": ">= 12.13.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" } }, + "node_modules/schema-utils/node_modules/ajv": { + "version": "8.16.0", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/schema-utils/node_modules/ajv-keywords": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/schema-utils/node_modules/json-schema-traverse": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, "node_modules/select-hose": { "version": "2.0.0", "dev": true, @@ -18675,13 +18524,6 @@ "node": ">= 10.0.0" } }, - "node_modules/sitemapper/node_modules/is-gzip": { - "version": "2.0.0", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/slash": { "version": "3.0.0", "dev": true, @@ -18718,7 +18560,8 @@ }, "node_modules/smart-buffer": { "version": "4.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" @@ -18753,7 +18596,8 @@ }, "node_modules/socks": { "version": "2.8.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", "dependencies": { "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" @@ -18765,7 +18609,8 @@ }, "node_modules/socks-proxy-agent": { "version": "8.0.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.3.tgz", + "integrity": "sha512-VNegTZKhuGq5vSD6XNKlbqWhyt/40CgoEw8XxD6dhnm8Jq9IEa3nIa4HwnM8XOqU0CdB0BwWVXusqiFXfHB3+A==", "dependencies": { "agent-base": "^7.1.1", "debug": "^4.3.4", @@ -18777,7 +18622,8 @@ }, "node_modules/socks-proxy-agent/node_modules/agent-base": { "version": "7.1.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "dependencies": { "debug": "^4.3.4" }, @@ -18860,7 +18706,7 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.17", + "version": "3.0.18", "dev": true, "license": "CC0-1.0" }, @@ -18998,8 +18844,7 @@ }, "node_modules/stdin-discarder": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz", - "integrity": "sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==", + "license": "MIT", "dependencies": { "bl": "^5.0.0" }, @@ -19010,43 +18855,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/stdin-discarder/node_modules/bl": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", - "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", - "dependencies": { - "buffer": "^6.0.3", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/stdin-discarder/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/stop-iteration-iterator": { - "version": "1.0.0", - "dev": true, - "license": "MIT", + "node_modules/stop-iteration-iterator": { + "version": "1.0.0", + "dev": true, + "license": "MIT", "dependencies": { "internal-slot": "^1.0.4" }, @@ -19068,11 +18880,13 @@ } }, "node_modules/streamx": { - "version": "2.16.1", - "license": "MIT", + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.18.0.tgz", + "integrity": "sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==", "dependencies": { - "fast-fifo": "^1.1.0", - "queue-tick": "^1.0.1" + "fast-fifo": "^1.3.2", + "queue-tick": "^1.0.1", + "text-decoder": "^1.1.0" }, "optionalDependencies": { "bare-events": "^2.2.0" @@ -19347,20 +19161,20 @@ } }, "node_modules/styled-components": { - "version": "6.1.8", + "version": "6.1.11", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@emotion/is-prop-valid": "1.2.1", - "@emotion/unitless": "0.8.0", - "@types/stylis": "4.2.0", + "@emotion/is-prop-valid": "1.2.2", + "@emotion/unitless": "0.8.1", + "@types/stylis": "4.2.5", "css-to-react-native": "3.2.0", - "csstype": "3.1.2", - "postcss": "8.4.31", + "csstype": "3.1.3", + "postcss": "8.4.38", "shallowequal": "1.1.0", - "stylis": "4.3.1", - "tslib": "2.5.0" + "stylis": "4.3.2", + "tslib": "2.6.2" }, "engines": { "node": ">= 16" @@ -19374,48 +19188,14 @@ "react-dom": ">= 16.8.0" } }, - "node_modules/styled-components/node_modules/csstype": { - "version": "3.1.2", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/styled-components/node_modules/postcss": { - "version": "8.4.31", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "peer": true, - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, "node_modules/styled-components/node_modules/tslib": { - "version": "2.5.0", + "version": "2.6.2", "dev": true, "license": "0BSD", "peer": true }, "node_modules/stylis": { - "version": "4.3.1", + "version": "4.3.2", "dev": true, "license": "MIT", "peer": true @@ -19475,7 +19255,7 @@ "license": "MIT" }, "node_modules/svgo": { - "version": "3.2.0", + "version": "3.3.2", "dev": true, "license": "MIT", "dependencies": { @@ -19584,7 +19364,7 @@ "license": "BSD-3-Clause" }, "node_modules/tailwindcss": { - "version": "3.4.3", + "version": "3.4.4", "dev": true, "license": "MIT", "dependencies": { @@ -19628,31 +19408,26 @@ } }, "node_modules/tar-fs": { - "version": "2.1.1", - "license": "MIT", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.5.tgz", + "integrity": "sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==", "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", - "tar-stream": "^2.1.4" + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^2.1.1", + "bare-path": "^2.1.0" } }, - "node_modules/tar-fs/node_modules/chownr": { - "version": "1.1.4", - "license": "ISC" - }, "node_modules/tar-stream": { - "version": "2.2.0", - "license": "MIT", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" } }, "node_modules/telejson": { @@ -19664,7 +19439,7 @@ } }, "node_modules/terser": { - "version": "5.31.0", + "version": "5.31.1", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -19734,6 +19509,23 @@ "node": ">= 10.13.0" } }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/terser-webpack-plugin/node_modules/supports-color": { "version": "8.1.1", "dev": true, @@ -19813,6 +19605,14 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/text-decoder": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.1.0.tgz", + "integrity": "sha512-TmLJNj6UgX8xcUZo4UDStGQtDiTzF7BzWlzn9g7UWrjkpHr5uJTK1ld16wZ3LXb2vb6jH8qU89dW5whuMdXYdw==", + "dependencies": { + "b4a": "^1.6.4" + } + }, "node_modules/text-table": { "version": "0.2.0", "dev": true, @@ -19846,31 +19646,23 @@ "dev": true, "license": "MIT" }, - "node_modules/timers-ext": { - "version": "0.1.7", - "license": "ISC", - "dependencies": { - "es5-ext": "~0.10.46", - "next-tick": "1" - } - }, "node_modules/tiny-invariant": { "version": "1.3.3", "dev": true, "license": "MIT" }, "node_modules/tldts": { - "version": "6.1.18", + "version": "6.1.25", "license": "MIT", "dependencies": { - "tldts-core": "^6.1.18" + "tldts-core": "^6.1.25" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "6.1.18", + "version": "6.1.25", "license": "MIT" }, "node_modules/tmpl": { @@ -19888,9 +19680,8 @@ }, "node_modules/to-regex-range": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -19899,7 +19690,7 @@ } }, "node_modules/tocbot": { - "version": "4.27.16", + "version": "4.28.2", "dev": true, "license": "MIT" }, @@ -20017,24 +19808,10 @@ "node": ">=8" } }, - "node_modules/ts-loader/node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/ts-loader/node_modules/semver": { - "version": "7.6.0", + "version": "7.6.2", "dev": true, "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -20053,11 +19830,6 @@ "node": ">=8" } }, - "node_modules/ts-loader/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, "node_modules/tsc-watch": { "version": "6.2.0", "dev": true, @@ -20109,7 +19881,7 @@ } }, "node_modules/tslib": { - "version": "2.6.2", + "version": "2.6.3", "license": "0BSD" }, "node_modules/tsutils": { @@ -20131,10 +19903,6 @@ "dev": true, "license": "0BSD" }, - "node_modules/type": { - "version": "2.7.2", - "license": "ISC" - }, "node_modules/type-check": { "version": "0.4.0", "dev": true, @@ -20259,7 +20027,7 @@ } }, "node_modules/uglify-js": { - "version": "3.17.4", + "version": "3.18.0", "dev": true, "license": "BSD-2-Clause", "optional": true, @@ -20286,12 +20054,36 @@ }, "node_modules/unbzip2-stream": { "version": "1.4.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", "dependencies": { "buffer": "^5.2.1", "through": "^2.3.8" } }, + "node_modules/unbzip2-stream/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/undici-types": { "version": "5.26.5", "license": "MIT" @@ -20409,12 +20201,12 @@ } }, "node_modules/unplugin/node_modules/webpack-virtual-modules": { - "version": "0.6.1", + "version": "0.6.2", "dev": true, "license": "MIT" }, "node_modules/update-browserslist-db": { - "version": "1.0.13", + "version": "1.0.16", "dev": true, "funding": [ { @@ -20432,8 +20224,8 @@ ], "license": "MIT", "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.1.2", + "picocolors": "^1.0.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -20478,6 +20270,11 @@ "dev": true, "license": "MIT" }, + "node_modules/urlpattern-polyfill": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", + "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==" + }, "node_modules/use-callback-ref": { "version": "1.3.2", "dev": true, @@ -20559,6 +20356,19 @@ } } }, + "node_modules/utf-8-validate": { + "version": "6.0.4", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, "node_modules/util": { "version": "0.12.5", "dev": true, @@ -21048,12 +20858,14 @@ }, "node_modules/wappalyzer": { "version": "6.10.66", + "resolved": "https://registry.npmjs.org/wappalyzer/-/wappalyzer-6.10.66.tgz", + "integrity": "sha512-rPnZY1dxIJvPrL0h7AKLrwQQ5vuSCD/ALwIXdwHurlfgex1sxFEQKwG/YjLEnR4iR+HpQsLl47EKlMu87/kbow==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "funding": [ { "url": "https://github.com/sponsors/aliasio" } ], - "license": "GPL-3.0", "dependencies": { "puppeteer": "~19.7.0" }, @@ -21064,307 +20876,69 @@ "node": ">=16" } }, - "node_modules/wappalyzer/node_modules/argparse": { - "version": "2.0.1", - "license": "Python-2.0" - }, - "node_modules/wappalyzer/node_modules/brace-expansion": { - "version": "2.0.1", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/wappalyzer/node_modules/chromium-bidi": { - "version": "0.4.5", - "license": "Apache-2.0", - "dependencies": { - "mitt": "3.0.0" - }, - "peerDependencies": { - "devtools-protocol": "*" - } - }, - "node_modules/wappalyzer/node_modules/cosmiconfig": { - "version": "8.1.0", + "node_modules/watchpack": { + "version": "2.4.1", + "dev": true, "license": "MIT", "dependencies": { - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0" + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" }, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" + "node": ">=10.13.0" } }, - "node_modules/wappalyzer/node_modules/cross-fetch": { - "version": "3.1.5", + "node_modules/wbuf": { + "version": "1.7.3", + "dev": true, "license": "MIT", "dependencies": { - "node-fetch": "2.6.7" + "minimalistic-assert": "^1.0.0" } }, - "node_modules/wappalyzer/node_modules/devtools-protocol": { - "version": "0.0.1094867", - "license": "BSD-3-Clause" - }, - "node_modules/wappalyzer/node_modules/extract-zip": { - "version": "2.0.1", + "node_modules/webidl-conversions": { + "version": "7.0.0", + "dev": true, "license": "BSD-2-Clause", - "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" - }, "engines": { - "node": ">= 10.17.0" - }, - "optionalDependencies": { - "@types/yauzl": "^2.9.1" + "node": ">=12" } }, - "node_modules/wappalyzer/node_modules/get-stream": { - "version": "5.2.0", + "node_modules/webpack": { + "version": "5.92.0", + "dev": true, "license": "MIT", "dependencies": { - "pump": "^3.0.0" + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", + "acorn": "^8.7.1", + "acorn-import-attributes": "^1.9.5", + "browserslist": "^4.21.10", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.0", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/wappalyzer/node_modules/glob": { - "version": "9.3.5", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "minimatch": "^8.0.2", - "minipass": "^4.2.4", - "path-scurry": "^1.6.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/wappalyzer/node_modules/js-yaml": { - "version": "4.1.0", - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/wappalyzer/node_modules/minimatch": { - "version": "8.0.4", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/wappalyzer/node_modules/minipass": { - "version": "4.2.8", - "license": "ISC", - "engines": { - "node": ">=8" - } - }, - "node_modules/wappalyzer/node_modules/mitt": { - "version": "3.0.0", - "license": "MIT" - }, - "node_modules/wappalyzer/node_modules/node-fetch": { - "version": "2.6.7", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/wappalyzer/node_modules/puppeteer": { - "version": "19.7.5", - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "cosmiconfig": "8.1.0", - "https-proxy-agent": "5.0.1", - "progress": "2.0.3", - "proxy-from-env": "1.1.0", - "puppeteer-core": "19.7.5" - } - }, - "node_modules/wappalyzer/node_modules/puppeteer-core": { - "version": "19.7.5", - "license": "Apache-2.0", - "dependencies": { - "chromium-bidi": "0.4.5", - "cross-fetch": "3.1.5", - "debug": "4.3.4", - "devtools-protocol": "0.0.1094867", - "extract-zip": "2.0.1", - "https-proxy-agent": "5.0.1", - "proxy-from-env": "1.1.0", - "rimraf": "4.4.0", - "tar-fs": "2.1.1", - "unbzip2-stream": "1.4.3", - "ws": "8.12.1" - }, - "engines": { - "node": ">=14.14.0" - }, - "peerDependencies": { - "typescript": ">= 4.7.4" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/wappalyzer/node_modules/rimraf": { - "version": "4.4.0", - "license": "ISC", - "dependencies": { - "glob": "^9.2.0" - }, - "bin": { - "rimraf": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/wappalyzer/node_modules/tr46": { - "version": "0.0.3", - "license": "MIT" - }, - "node_modules/wappalyzer/node_modules/webidl-conversions": { - "version": "3.0.1", - "license": "BSD-2-Clause" - }, - "node_modules/wappalyzer/node_modules/whatwg-url": { - "version": "5.0.0", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/wappalyzer/node_modules/ws": { - "version": "8.12.1", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/watchpack": { - "version": "2.4.1", - "dev": true, - "license": "MIT", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/wbuf": { - "version": "1.7.3", - "dev": true, - "license": "MIT", - "dependencies": { - "minimalistic-assert": "^1.0.0" - } - }, - "node_modules/webidl-conversions": { - "version": "7.0.0", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - } - }, - "node_modules/webpack": { - "version": "5.91.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.5", - "@webassemblyjs/ast": "^1.12.1", - "@webassemblyjs/wasm-edit": "^1.12.1", - "@webassemblyjs/wasm-parser": "^1.12.1", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", - "browserslist": "^4.21.10", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.16.0", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.10", - "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" + "node": ">=10.13.0" }, "funding": { "type": "opencollective", @@ -21455,55 +21029,6 @@ } } }, - "node_modules/webpack-dev-middleware/node_modules/ajv": { - "version": "8.13.0", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": { - "version": "5.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/webpack-dev-middleware/node_modules/schema-utils": { - "version": "4.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/webpack-dev-server": { "version": "4.15.2", "dev": true, @@ -21562,32 +21087,6 @@ } } }, - "node_modules/webpack-dev-server/node_modules/ajv": { - "version": "8.13.0", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/webpack-dev-server/node_modules/ajv-keywords": { - "version": "5.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, "node_modules/webpack-dev-server/node_modules/glob": { "version": "7.2.3", "dev": true, @@ -21615,11 +21114,6 @@ "node": ">= 10" } }, - "node_modules/webpack-dev-server/node_modules/json-schema-traverse": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, "node_modules/webpack-dev-server/node_modules/rimraf": { "version": "3.0.2", "dev": true, @@ -21634,26 +21128,8 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/webpack-dev-server/node_modules/schema-utils": { - "version": "4.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/webpack-dev-server/node_modules/webpack-dev-middleware": { - "version": "5.3.4", + "node_modules/webpack-dev-server/node_modules/webpack-dev-middleware": { + "version": "5.3.4", "dev": true, "license": "MIT", "dependencies": { @@ -21697,6 +21173,14 @@ "node": ">=10.0.0" } }, + "node_modules/webpack-node-externals": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/webpack-sources": { "version": "3.2.3", "dev": true, @@ -21726,14 +21210,31 @@ "node": ">=0.4.0" } }, - "node_modules/webpack/node_modules/acorn-import-assertions": { - "version": "1.9.0", + "node_modules/webpack/node_modules/acorn-import-attributes": { + "version": "1.9.5", "dev": true, "license": "MIT", "peerDependencies": { "acorn": "^8" } }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/webpackbar": { "version": "5.0.2", "dev": true, @@ -21780,11 +21281,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/webpackbar/node_modules/consola": { - "version": "2.15.3", - "dev": true, - "license": "MIT" - }, "node_modules/webpackbar/node_modules/has-flag": { "version": "4.0.0", "dev": true, @@ -22105,9 +21601,9 @@ "license": "ISC" }, "node_modules/ws": { - "version": "8.17.0", - "dev": true, - "license": "MIT", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "engines": { "node": ">=10.0.0" }, @@ -22177,7 +21673,6 @@ }, "node_modules/yargs": { "version": "17.7.2", - "dev": true, "license": "MIT", "dependencies": { "cliui": "^8.0.1", @@ -22201,12 +21696,10 @@ }, "node_modules/yargs/node_modules/emoji-regex": { "version": "8.0.0", - "dev": true, "license": "MIT" }, "node_modules/yargs/node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -22214,7 +21707,6 @@ }, "node_modules/yargs/node_modules/string-width": { "version": "4.2.3", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -22227,7 +21719,8 @@ }, "node_modules/yauzl": { "version": "2.10.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" @@ -22243,49 +21736,69 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "packages/analysis-backend": { - "extraneous": true + "node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } }, "packages/analysis-utils": { - "extraneous": true + "name": "@google-psat/analysis-utils", + "version": "0.9.0-2", + "license": "Apache-2.0", + "dependencies": { + "@google-psat/common": "*", + "puppeteer": "^22.10.0", + "simple-cookie": "^1.0.15", + "tldts": "^6.0.14", + "wappalyzer": "^6.10.66" + }, + "devDependencies": { + "tsc-watch": "^6.0.4", + "typescript": "^5.0.4" + } }, "packages/cli": { - "name": "@ps-analysis-tool/cli", - "version": "0.8.0", + "name": "@google-psat/cli", + "version": "0.9.0-2", "license": "Apache-2.0", "dependencies": { - "@ps-analysis-tool/common": "*", - "cli-color": "^2.0.3", - "cli-table": "^0.3.11", + "@google-psat/analysis-utils": "*", + "@google-psat/cli-dashboard": "*", + "@google-psat/common": "*", + "@google-psat/i18n": "*", + "@google-psat/library-detection": "*", "commander": "^10.0.1", "fs-extra": "^11.1.1", - "ora": "^7.0.1", "promptly": "^3.2.0", - "puppeteer": "^21.0.3", - "simple-cookie": "^1.0.15", "sitemapper": "^3.1.8", "spinnies": "^0.5.1", - "tldts": "^6.0.14", - "wappalyzer": "^6.10.66", "xml2js": "^0.6.2" }, + "bin": { + "psat": "dist/main.js" + }, "devDependencies": { - "@types/cli-color": "^2.0.2", "@types/fs-extra": "^11.0.1", "@types/node": "^20.2.3", "@types/promptly": "^3.0.2", "@types/xml2js": "^0.4.14", "tsc-watch": "^6.0.4", - "typescript": "^5.0.4" + "typescript": "^5.0.4", + "webpack": "^5.86.0" } }, "packages/cli-dashboard": { - "name": "@ps-analysis-tool/cli-dashboard", - "version": "0.8.0", + "name": "@google-psat/cli-dashboard", + "version": "0.9.0-2", "license": "Apache-2.0", "dependencies": { - "@ps-analysis-tool/common": "*", - "@ps-analysis-tool/design-system": "*", + "@google-psat/common": "*", + "@google-psat/design-system": "*", + "@google-psat/i18n": "*", + "@google-psat/library-detection": "*", "classnames": "^2.3.2", "file-saver": "^2.0.5", "jszip": "^3.10.1", @@ -22308,361 +21821,133 @@ "webpackbar": "^5.0.2" } }, - "packages/cli-dashboard/node_modules/ansi-regex": { - "version": "6.0.1", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "packages/cli-dashboard/node_modules/chalk": { - "version": "5.3.0", + "packages/cli/node_modules/commander": { + "version": "10.0.1", "license": "MIT", "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=14" } }, - "packages/cli-dashboard/node_modules/emoji-regex": { - "version": "10.3.0", - "license": "MIT" - }, - "packages/cli-dashboard/node_modules/is-interactive": { - "version": "2.0.0", + "packages/cli/node_modules/xml2js": { + "version": "0.6.2", "license": "MIT", - "engines": { - "node": ">=12" + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "packages/cli-dashboard/node_modules/is-unicode-supported": { - "version": "1.3.0", - "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4.0.0" } }, - "packages/cli-dashboard/node_modules/log-symbols": { - "version": "5.1.0", - "license": "MIT", + "packages/common": { + "name": "@google-psat/common", + "version": "0.9.0-2", + "license": "Apache-2.0", "dependencies": { - "chalk": "^5.0.0", - "is-unicode-supported": "^1.1.0" - }, - "engines": { - "node": ">=12" + "@google-psat/i18n": "*", + "tldts": "^6.0.14" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "devDependencies": { + "devtools-protocol": "^0.0.1282316" } }, - "packages/cli-dashboard/node_modules/ora": { - "version": "7.0.1", - "license": "MIT", + "packages/design-system": { + "name": "@google-psat/design-system", + "version": "0.9.0-2", + "license": "Apache-2.0", "dependencies": { - "chalk": "^5.3.0", - "cli-cursor": "^4.0.0", - "cli-spinners": "^2.9.0", - "is-interactive": "^2.0.0", - "is-unicode-supported": "^1.3.0", - "log-symbols": "^5.1.0", - "stdin-discarder": "^0.1.0", - "string-width": "^6.1.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=16" + "@google-psat/common": "*", + "@google-psat/i18n": "*", + "p-queue": "^7.3.4", + "use-context-selector": "^1.4.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "devDependencies": {} }, - "packages/cli-dashboard/node_modules/string-width": { - "version": "6.1.0", - "license": "MIT", + "packages/eslint-import-resolver": { + "name": "@google-psat/eslint-import-resolver", + "version": "0.9.0", + "license": "Apache-2.0", "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^10.2.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "eslint-import-resolver-node": "^0.3.7" } }, - "packages/cli-dashboard/node_modules/strip-ansi": { - "version": "7.1.0", - "license": "MIT", + "packages/extension": { + "name": "@google-psat/extension", + "version": "0.9.0", + "license": "Apache-2.0", "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" + "@floating-ui/core": "^1.5.0", + "@floating-ui/dom": "^1.5.3", + "@google-psat/common": "*", + "@google-psat/design-system": "*", + "@google-psat/i18n": "*", + "@google-psat/library-detection": "*", + "classnames": "^2.3.2", + "fast-xml-parser": "^4.3.2", + "file-saver": "^2.0.5", + "p-queue": "^7.3.4", + "re-resizable": "^6.9.9", + "react": "^18.2.0", + "react-copy-to-clipboard": "^5.1.0", + "react-dom": "^18.2.0", + "react-error-boundary": "^4.0.11", + "shallow-equal": "^3.1.0", + "simple-cookie": "^1.0.15", + "tldts": "^6.0.14", + "use-context-selector": "^1.4.1", + "use-debounce": "^9.0.4", + "validate.js": "^0.13.1", + "victory": "^36.6.11" }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "devDependencies": { + "@types/react-copy-to-clipboard": "^5.0.4", + "devtools-protocol": "^0.0.1282316", + "html-inline-script-webpack-plugin": "^3.2.1" } }, - "packages/cli/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "packages/i18n": { + "name": "@google-psat/i18n", + "version": "0.9.0-2", + "license": "Apache-2.0", + "dependencies": { + "intl-messageformat": "^10.5.11" } }, - "packages/cli/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "packages/library-detection": { + "name": "@google-psat/library-detection", + "version": "0.9.0-2", + "license": "Apache-2.0", + "dependencies": { + "@google-psat/common": "*", + "@google-psat/design-system": "*", + "@google-psat/i18n": "*", + "classnames": "^2.5.1", + "escape-string-regexp": "^4.0.0", + "react": "^18.2.0", + "use-context-selector": "^1.4.1" } }, - "packages/cli/node_modules/commander": { - "version": "10.0.1", + "packages/library-detection/node_modules/escape-string-regexp": { + "version": "4.0.0", "license": "MIT", "engines": { - "node": ">=14" - } - }, - "packages/cli/node_modules/emoji-regex": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", - "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==" - }, - "packages/cli/node_modules/is-interactive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", - "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "packages/cli/node_modules/is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", - "engines": { - "node": ">=12" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "packages/cli/node_modules/log-symbols": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", - "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", + "packages/report": { + "name": "@google-psat/report", + "version": "0.9.0-2", + "license": "Apache-2.0", "dependencies": { - "chalk": "^5.0.0", - "is-unicode-supported": "^1.1.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "packages/cli/node_modules/ora": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-7.0.1.tgz", - "integrity": "sha512-0TUxTiFJWv+JnjWm4o9yvuskpEJLXTcng8MJuKd+SzAzp2o+OP3HWqNhB4OdJRt1Vsd9/mR0oyaEYlOnL7XIRw==", - "dependencies": { - "chalk": "^5.3.0", - "cli-cursor": "^4.0.0", - "cli-spinners": "^2.9.0", - "is-interactive": "^2.0.0", - "is-unicode-supported": "^1.3.0", - "log-symbols": "^5.1.0", - "stdin-discarder": "^0.1.0", - "string-width": "^6.1.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "packages/cli/node_modules/string-width": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-6.1.0.tgz", - "integrity": "sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ==", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^10.2.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "packages/cli/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "packages/cli/node_modules/xml2js": { - "version": "0.6.2", - "license": "MIT", - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "packages/common": { - "name": "@ps-analysis-tool/common", - "version": "0.8.0", - "license": "Apache-2.0", - "dependencies": { - "tldts": "^6.0.14" - }, - "devDependencies": { - "devtools-protocol": "^0.0.1282316" - } - }, - "packages/common/node_modules/devtools-protocol": { - "version": "0.0.1282316", - "dev": true, - "license": "BSD-3-Clause" - }, - "packages/design-system": { - "name": "@ps-analysis-tool/design-system", - "version": "0.8.0", - "license": "Apache-2.0", - "dependencies": { - "@ps-analysis-tool/common": "*", - "p-queue": "^7.3.4", - "use-context-selector": "^1.4.1" - }, - "devDependencies": {} - }, - "packages/eslint-import-resolver": { - "name": "@ps-analysis-tool/eslint-import-resolver", - "version": "0.8.0", - "license": "Apache-2.0", - "dependencies": { - "eslint-import-resolver-node": "^0.3.7" - } - }, - "packages/extension": { - "name": "@ps-analysis-tool/extension", - "version": "0.8.0", - "license": "Apache-2.0", - "dependencies": { - "@floating-ui/core": "^1.5.0", - "@floating-ui/dom": "^1.5.3", - "@ps-analysis-tool/common": "*", - "@ps-analysis-tool/design-system": "*", - "@ps-analysis-tool/library-detection": "*", - "classnames": "^2.3.2", - "fast-xml-parser": "^4.3.2", - "file-saver": "^2.0.5", - "p-queue": "^7.3.4", - "re-resizable": "^6.9.9", - "react": "^18.2.0", - "react-copy-to-clipboard": "^5.1.0", - "react-dom": "^18.2.0", - "react-error-boundary": "^4.0.11", - "shallow-equal": "^3.1.0", - "simple-cookie": "^1.0.15", - "tldts": "^6.0.14", - "use-context-selector": "^1.4.1", - "use-debounce": "^9.0.4", - "validate.js": "^0.13.1", - "victory": "^36.6.11" - }, - "devDependencies": { - "@types/react-copy-to-clipboard": "^5.0.4", - "devtools-protocol": "^0.0.1282316", - "html-inline-script-webpack-plugin": "^3.2.1" - } - }, - "packages/extension/node_modules/devtools-protocol": { - "version": "0.0.1282316", - "dev": true, - "license": "BSD-3-Clause" - }, - "packages/i18n": { - "name": "@ps-analysis-tool/i18n", - "version": "0.8.0", - "license": "Apache-2.0", - "dependencies": { - "intl-messageformat": "^10.5.11" - } - }, - "packages/library-detection": { - "name": "@ps-analysis-tool/library-detection", - "version": "0.8.0", - "license": "Apache-2.0", - "dependencies": { - "@ps-analysis-tool/common": "*", - "@ps-analysis-tool/design-system": "*", - "classnames": "^2.5.1", - "escape-string-regexp": "^4.0.0", - "react": "^18.2.0", - "use-context-selector": "^1.4.1" - } - }, - "packages/library-detection/node_modules/escape-string-regexp": { - "version": "4.0.0", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "packages/report": { - "name": "@ps-analysis-tool/report", - "version": "0.8.0", - "license": "Apache-2.0", - "dependencies": { - "@ps-analysis-tool/common": "*", - "@ps-analysis-tool/design-system": "*", - "@ps-analysis-tool/library-detection": "*", - "react": "^18.2.0", - "react-dom": "^18.2.0" + "@google-psat/common": "*", + "@google-psat/design-system": "*", + "@google-psat/i18n": "*", + "@google-psat/library-detection": "*", + "react": "^18.2.0", + "react-dom": "^18.2.0" }, "devDependencies": { "@babel/plugin-transform-react-jsx": "^7.22.15", @@ -22677,14048 +21962,5 @@ "webpackbar": "^5.0.2" } } - }, - "dependencies": { - "@adobe/css-tools": { - "version": "4.3.3", - "dev": true - }, - "@alloc/quick-lru": { - "version": "5.2.0", - "dev": true - }, - "@ampproject/remapping": { - "version": "2.3.0", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "@babel/code-frame": { - "version": "7.24.2", - "requires": { - "@babel/highlight": "^7.24.2", - "picocolors": "^1.0.0" - } - }, - "@babel/compat-data": { - "version": "7.24.4", - "dev": true - }, - "@babel/core": { - "version": "7.24.5", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.2", - "@babel/generator": "^7.24.5", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.24.5", - "@babel/helpers": "^7.24.5", - "@babel/parser": "^7.24.5", - "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.5", - "@babel/types": "^7.24.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - } - }, - "@babel/generator": { - "version": "7.24.5", - "dev": true, - "requires": { - "@babel/types": "^7.24.5", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.22.15", - "dev": true, - "requires": { - "@babel/types": "^7.22.15" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.23.6", - "dev": true, - "requires": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-validator-option": "^7.23.5", - "browserslist": "^4.22.2", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.24.5", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-member-expression-to-functions": "^7.24.5", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.24.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.24.5", - "semver": "^6.3.1" - } - }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.22.15", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "regexpu-core": "^5.3.1", - "semver": "^6.3.1" - } - }, - "@babel/helper-define-polyfill-provider": { - "version": "0.6.2", - "dev": true, - "requires": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" - } - }, - "@babel/helper-environment-visitor": { - "version": "7.22.20", - "dev": true - }, - "@babel/helper-function-name": { - "version": "7.23.0", - "dev": true, - "requires": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.22.5", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.24.5", - "dev": true, - "requires": { - "@babel/types": "^7.24.5" - } - }, - "@babel/helper-module-imports": { - "version": "7.24.3", - "dev": true, - "requires": { - "@babel/types": "^7.24.0" - } - }, - "@babel/helper-module-transforms": { - "version": "7.24.5", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.24.3", - "@babel/helper-simple-access": "^7.24.5", - "@babel/helper-split-export-declaration": "^7.24.5", - "@babel/helper-validator-identifier": "^7.24.5" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.22.5", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.24.5", - "dev": true - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.22.20", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-wrap-function": "^7.22.20" - } - }, - "@babel/helper-replace-supers": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-member-expression-to-functions": "^7.23.0", - "@babel/helper-optimise-call-expression": "^7.22.5" - } - }, - "@babel/helper-simple-access": { - "version": "7.24.5", - "dev": true, - "requires": { - "@babel/types": "^7.24.5" - } - }, - "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.22.5", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.24.5", - "dev": true, - "requires": { - "@babel/types": "^7.24.5" - } - }, - "@babel/helper-string-parser": { - "version": "7.24.1", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.24.5" - }, - "@babel/helper-validator-option": { - "version": "7.23.5", - "dev": true - }, - "@babel/helper-wrap-function": { - "version": "7.24.5", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.23.0", - "@babel/template": "^7.24.0", - "@babel/types": "^7.24.5" - } - }, - "@babel/helpers": { - "version": "7.24.5", - "dev": true, - "requires": { - "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.5", - "@babel/types": "^7.24.5" - } - }, - "@babel/highlight": { - "version": "7.24.5", - "requires": { - "@babel/helper-validator-identifier": "^7.24.5", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - } - }, - "@babel/parser": { - "version": "7.24.5", - "dev": true - }, - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.24.5", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.24.5" - } - }, - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.24.1" - } - }, - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "dev": true, - "requires": {} - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-flow": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-syntax-import-assertions": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-syntax-import-attributes": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-syntax-unicode-sets-regex": { - "version": "7.18.6", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-async-generator-functions": { - "version": "7.24.3", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-remap-async-to-generator": "^7.22.20", - "@babel/plugin-syntax-async-generators": "^7.8.4" - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.24.1", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-remap-async-to-generator": "^7.22.20" - } - }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.24.5", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.5" - } - }, - "@babel/plugin-transform-class-properties": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.24.1", - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-class-static-block": { - "version": "7.24.4", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.24.4", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - } - }, - "@babel/plugin-transform-classes": { - "version": "7.24.5", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.24.5", - "@babel/helper-replace-supers": "^7.24.1", - "@babel/helper-split-export-declaration": "^7.24.5", - "globals": "^11.1.0" - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/template": "^7.24.0" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.24.5", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.5" - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-dynamic-import": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-export-namespace-from": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - } - }, - "@babel/plugin-transform-flow-strip-types": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-flow": "^7.24.1" - } - }, - "@babel/plugin-transform-for-of": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-json-strings": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-json-strings": "^7.8.3" - } - }, - "@babel/plugin-transform-literals": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-logical-assignment-operators": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - } - }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-simple-access": "^7.22.5" - } - }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-validator-identifier": "^7.22.20" - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.22.5", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - } - }, - "@babel/plugin-transform-numeric-separator": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - } - }, - "@babel/plugin-transform-object-rest-spread": { - "version": "7.24.5", - "dev": true, - "requires": { - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-plugin-utils": "^7.24.5", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.24.5" - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-replace-supers": "^7.24.1" - } - }, - "@babel/plugin-transform-optional-catch-binding": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - } - }, - "@babel/plugin-transform-optional-chaining": { - "version": "7.24.5", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.24.5", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.5" - } - }, - "@babel/plugin-transform-private-methods": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.24.1", - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-private-property-in-object": { - "version": "7.24.5", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.24.5", - "@babel/helper-plugin-utils": "^7.24.5", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - } - }, - "@babel/plugin-transform-property-literals": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-react-constant-elements": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-react-display-name": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-react-jsx": { - "version": "7.23.4", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-jsx": "^7.23.3", - "@babel/types": "^7.23.4" - } - }, - "@babel/plugin-transform-react-jsx-development": { - "version": "7.22.5", - "dev": true, - "requires": { - "@babel/plugin-transform-react-jsx": "^7.22.5" - } - }, - "@babel/plugin-transform-react-pure-annotations": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "regenerator-transform": "^0.15.2" - } - }, - "@babel/plugin-transform-reserved-words": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-spread": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.24.5", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.5" - } - }, - "@babel/plugin-transform-typescript": { - "version": "7.24.5", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.24.5", - "@babel/helper-plugin-utils": "^7.24.5", - "@babel/plugin-syntax-typescript": "^7.24.1" - } - }, - "@babel/plugin-transform-unicode-escapes": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-unicode-property-regex": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-unicode-sets-regex": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/preset-env": { - "version": "7.24.5", - "dev": true, - "requires": { - "@babel/compat-data": "^7.24.4", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-plugin-utils": "^7.24.5", - "@babel/helper-validator-option": "^7.23.5", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.24.5", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.1", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.1", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.1", - "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.24.1", - "@babel/plugin-syntax-import-attributes": "^7.24.1", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.24.1", - "@babel/plugin-transform-async-generator-functions": "^7.24.3", - "@babel/plugin-transform-async-to-generator": "^7.24.1", - "@babel/plugin-transform-block-scoped-functions": "^7.24.1", - "@babel/plugin-transform-block-scoping": "^7.24.5", - "@babel/plugin-transform-class-properties": "^7.24.1", - "@babel/plugin-transform-class-static-block": "^7.24.4", - "@babel/plugin-transform-classes": "^7.24.5", - "@babel/plugin-transform-computed-properties": "^7.24.1", - "@babel/plugin-transform-destructuring": "^7.24.5", - "@babel/plugin-transform-dotall-regex": "^7.24.1", - "@babel/plugin-transform-duplicate-keys": "^7.24.1", - "@babel/plugin-transform-dynamic-import": "^7.24.1", - "@babel/plugin-transform-exponentiation-operator": "^7.24.1", - "@babel/plugin-transform-export-namespace-from": "^7.24.1", - "@babel/plugin-transform-for-of": "^7.24.1", - "@babel/plugin-transform-function-name": "^7.24.1", - "@babel/plugin-transform-json-strings": "^7.24.1", - "@babel/plugin-transform-literals": "^7.24.1", - "@babel/plugin-transform-logical-assignment-operators": "^7.24.1", - "@babel/plugin-transform-member-expression-literals": "^7.24.1", - "@babel/plugin-transform-modules-amd": "^7.24.1", - "@babel/plugin-transform-modules-commonjs": "^7.24.1", - "@babel/plugin-transform-modules-systemjs": "^7.24.1", - "@babel/plugin-transform-modules-umd": "^7.24.1", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", - "@babel/plugin-transform-new-target": "^7.24.1", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.1", - "@babel/plugin-transform-numeric-separator": "^7.24.1", - "@babel/plugin-transform-object-rest-spread": "^7.24.5", - "@babel/plugin-transform-object-super": "^7.24.1", - "@babel/plugin-transform-optional-catch-binding": "^7.24.1", - "@babel/plugin-transform-optional-chaining": "^7.24.5", - "@babel/plugin-transform-parameters": "^7.24.5", - "@babel/plugin-transform-private-methods": "^7.24.1", - "@babel/plugin-transform-private-property-in-object": "^7.24.5", - "@babel/plugin-transform-property-literals": "^7.24.1", - "@babel/plugin-transform-regenerator": "^7.24.1", - "@babel/plugin-transform-reserved-words": "^7.24.1", - "@babel/plugin-transform-shorthand-properties": "^7.24.1", - "@babel/plugin-transform-spread": "^7.24.1", - "@babel/plugin-transform-sticky-regex": "^7.24.1", - "@babel/plugin-transform-template-literals": "^7.24.1", - "@babel/plugin-transform-typeof-symbol": "^7.24.5", - "@babel/plugin-transform-unicode-escapes": "^7.24.1", - "@babel/plugin-transform-unicode-property-regex": "^7.24.1", - "@babel/plugin-transform-unicode-regex": "^7.24.1", - "@babel/plugin-transform-unicode-sets-regex": "^7.24.1", - "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.4", - "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.31.0", - "semver": "^6.3.1" - } - }, - "@babel/preset-flow": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-validator-option": "^7.23.5", - "@babel/plugin-transform-flow-strip-types": "^7.24.1" - } - }, - "@babel/preset-modules": { - "version": "0.1.6-no-external-plugins", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - } - }, - "@babel/preset-react": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-validator-option": "^7.23.5", - "@babel/plugin-transform-react-display-name": "^7.24.1", - "@babel/plugin-transform-react-jsx": "^7.23.4", - "@babel/plugin-transform-react-jsx-development": "^7.22.5", - "@babel/plugin-transform-react-pure-annotations": "^7.24.1" - } - }, - "@babel/preset-typescript": { - "version": "7.24.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-validator-option": "^7.23.5", - "@babel/plugin-syntax-jsx": "^7.24.1", - "@babel/plugin-transform-modules-commonjs": "^7.24.1", - "@babel/plugin-transform-typescript": "^7.24.1" - } - }, - "@babel/regjsgen": { - "version": "0.8.0", - "dev": true - }, - "@babel/runtime": { - "version": "7.24.5", - "requires": { - "regenerator-runtime": "^0.14.0" - } - }, - "@babel/template": { - "version": "7.24.0", - "dev": true, - "requires": { - "@babel/code-frame": "^7.23.5", - "@babel/parser": "^7.24.0", - "@babel/types": "^7.24.0" - } - }, - "@babel/traverse": { - "version": "7.24.5", - "dev": true, - "requires": { - "@babel/code-frame": "^7.24.2", - "@babel/generator": "^7.24.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.24.5", - "@babel/parser": "^7.24.5", - "@babel/types": "^7.24.5", - "debug": "^4.3.1", - "globals": "^11.1.0" - } - }, - "@babel/types": { - "version": "7.24.5", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.24.1", - "@babel/helper-validator-identifier": "^7.24.5", - "to-fast-properties": "^2.0.0" - } - }, - "@base2/pretty-print-object": { - "version": "1.0.1", - "dev": true - }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "dev": true - }, - "@discoveryjs/json-ext": { - "version": "0.5.7", - "dev": true - }, - "@emotion/is-prop-valid": { - "version": "1.2.1", - "dev": true, - "peer": true, - "requires": { - "@emotion/memoize": "^0.8.1" - } - }, - "@emotion/memoize": { - "version": "0.8.1", - "dev": true, - "peer": true - }, - "@emotion/unitless": { - "version": "0.8.0", - "dev": true, - "peer": true - }, - "@emotion/use-insertion-effect-with-fallbacks": { - "version": "1.0.1", - "dev": true, - "requires": {} - }, - "@es-joy/jsdoccomment": { - "version": "0.41.0", - "dev": true, - "requires": { - "comment-parser": "1.4.1", - "esquery": "^1.5.0", - "jsdoc-type-pratt-parser": "~4.0.0" - } - }, - "@esbuild/darwin-arm64": { - "version": "0.18.20", - "dev": true, - "optional": true - }, - "@eslint-community/eslint-utils": { - "version": "4.4.0", - "dev": true, - "requires": { - "eslint-visitor-keys": "^3.3.0" - } - }, - "@eslint-community/regexpp": { - "version": "4.10.0", - "dev": true - }, - "@eslint/eslintrc": { - "version": "2.1.4", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "argparse": { - "version": "2.0.1", - "dev": true - }, - "globals": { - "version": "13.24.0", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "js-yaml": { - "version": "4.1.0", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "type-fest": { - "version": "0.20.2", - "dev": true - } - } - }, - "@eslint/js": { - "version": "8.57.0", - "dev": true - }, - "@floating-ui/core": { - "version": "1.6.1", - "requires": { - "@floating-ui/utils": "^0.2.0" - } - }, - "@floating-ui/dom": { - "version": "1.6.4", - "requires": { - "@floating-ui/core": "^1.0.0", - "@floating-ui/utils": "^0.2.0" - } - }, - "@floating-ui/react-dom": { - "version": "2.0.9", - "dev": true, - "requires": { - "@floating-ui/dom": "^1.0.0" - } - }, - "@floating-ui/utils": { - "version": "0.2.2" - }, - "@formatjs/ecma402-abstract": { - "version": "1.18.2", - "requires": { - "@formatjs/intl-localematcher": "0.5.4", - "tslib": "^2.4.0" - } - }, - "@formatjs/fast-memoize": { - "version": "2.2.0", - "requires": { - "tslib": "^2.4.0" - } - }, - "@formatjs/icu-messageformat-parser": { - "version": "2.7.6", - "requires": { - "@formatjs/ecma402-abstract": "1.18.2", - "@formatjs/icu-skeleton-parser": "1.8.0", - "tslib": "^2.4.0" - } - }, - "@formatjs/icu-skeleton-parser": { - "version": "1.8.0", - "requires": { - "@formatjs/ecma402-abstract": "1.18.2", - "tslib": "^2.4.0" - } - }, - "@formatjs/intl-localematcher": { - "version": "0.5.4", - "requires": { - "tslib": "^2.4.0" - } - }, - "@humanwhocodes/config-array": { - "version": "0.11.14", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - } - }, - "@humanwhocodes/module-importer": { - "version": "1.0.1", - "dev": true - }, - "@humanwhocodes/object-schema": { - "version": "2.0.3", - "dev": true - }, - "@isaacs/cliui": { - "version": "8.0.2", - "dev": true, - "requires": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "6.0.1", - "dev": true - }, - "strip-ansi": { - "version": "7.1.0", - "dev": true, - "requires": { - "ansi-regex": "^6.0.1" - } - } - } - }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "dev": true - }, - "find-up": { - "version": "4.1.0", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - } - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "dev": true - }, - "@jest/console": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jest/core": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "pretty-format": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "dev": true - } - } - }, - "react-is": { - "version": "18.3.1", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jest/environment": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "dependencies": { - "jest-mock": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - } - } - } - }, - "@jest/expect": { - "version": "29.7.0", - "dev": true, - "requires": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - } - }, - "@jest/expect-utils": { - "version": "29.7.0", - "dev": true, - "requires": { - "jest-get-type": "^29.6.3" - } - }, - "@jest/fake-timers": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "dependencies": { - "jest-mock": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - } - } - } - }, - "@jest/globals": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "dependencies": { - "jest-mock": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - } - } - } - }, - "@jest/reporters": { - "version": "29.7.0", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "glob": { - "version": "7.2.3", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "6.0.2", - "dev": true, - "requires": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - } - }, - "lru-cache": { - "version": "6.0.0", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.6.0", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "dev": true - } - } - }, - "@jest/schemas": { - "version": "29.6.3", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.27.8" - } - }, - "@jest/source-map": { - "version": "29.6.3", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - } - }, - "@jest/test-result": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/test-sequencer": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - } - }, - "@jest/transform": { - "version": "29.7.0", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jest/types": { - "version": "29.6.3", - "dev": true, - "requires": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jridgewell/gen-mapping": { - "version": "0.3.5", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.2", - "dev": true - }, - "@jridgewell/set-array": { - "version": "1.2.1", - "dev": true - }, - "@jridgewell/source-map": { - "version": "0.3.6", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.25", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "@juggle/resize-observer": { - "version": "3.4.0", - "dev": true - }, - "@leichtgewicht/ip-codec": { - "version": "2.0.5", - "dev": true - }, - "@mdx-js/react": { - "version": "2.3.0", - "dev": true, - "requires": { - "@types/mdx": "^2.0.0", - "@types/react": ">=16" - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@pkgjs/parseargs": { - "version": "0.11.0", - "dev": true, - "optional": true - }, - "@pmmmwh/react-refresh-webpack-plugin": { - "version": "0.5.13", - "dev": true, - "requires": { - "ansi-html-community": "^0.0.8", - "core-js-pure": "^3.23.3", - "error-stack-parser": "^2.0.6", - "html-entities": "^2.1.0", - "loader-utils": "^2.0.4", - "schema-utils": "^3.0.0", - "source-map": "^0.7.3" - } - }, - "@ps-analysis-tool/cli": { - "version": "file:packages/cli", - "requires": { - "@ps-analysis-tool/common": "*", - "@types/cli-color": "^2.0.2", - "@types/fs-extra": "^11.0.1", - "@types/node": "^20.2.3", - "@types/promptly": "^3.0.2", - "@types/xml2js": "^0.4.14", - "cli-color": "^2.0.3", - "cli-table": "^0.3.11", - "commander": "^10.0.1", - "fs-extra": "^11.1.1", - "ora": "^7.0.1", - "promptly": "^3.2.0", - "puppeteer": "^21.0.3", - "simple-cookie": "^1.0.15", - "sitemapper": "^3.1.8", - "spinnies": "^0.5.1", - "tldts": "^6.0.14", - "tsc-watch": "^6.0.4", - "typescript": "^5.0.4", - "wappalyzer": "^6.10.66", - "xml2js": "^0.6.2" - }, - "dependencies": { - "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" - }, - "chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==" - }, - "commander": { - "version": "10.0.1" - }, - "emoji-regex": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", - "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==" - }, - "is-interactive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", - "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==" - }, - "is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==" - }, - "log-symbols": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", - "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", - "requires": { - "chalk": "^5.0.0", - "is-unicode-supported": "^1.1.0" - } - }, - "ora": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-7.0.1.tgz", - "integrity": "sha512-0TUxTiFJWv+JnjWm4o9yvuskpEJLXTcng8MJuKd+SzAzp2o+OP3HWqNhB4OdJRt1Vsd9/mR0oyaEYlOnL7XIRw==", - "requires": { - "chalk": "^5.3.0", - "cli-cursor": "^4.0.0", - "cli-spinners": "^2.9.0", - "is-interactive": "^2.0.0", - "is-unicode-supported": "^1.3.0", - "log-symbols": "^5.1.0", - "stdin-discarder": "^0.1.0", - "string-width": "^6.1.0", - "strip-ansi": "^7.1.0" - } - }, - "string-width": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-6.1.0.tgz", - "integrity": "sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ==", - "requires": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^10.2.1", - "strip-ansi": "^7.0.1" - } - }, - "strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "requires": { - "ansi-regex": "^6.0.1" - } - }, - "xml2js": { - "version": "0.6.2", - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - } - } - } - }, - "@ps-analysis-tool/cli-dashboard": { - "version": "file:packages/cli-dashboard", - "requires": { - "@babel/plugin-transform-react-jsx": "^7.22.15", - "@babel/preset-env": "^7.22.15", - "@babel/preset-react": "^7.22.15", - "@babel/preset-typescript": "^7.22.15", - "@ps-analysis-tool/common": "*", - "@ps-analysis-tool/design-system": "*", - "@types/file-saver": "^2.0.5", - "babel-loader": "^9.1.3", - "classnames": "^2.3.2", - "file-saver": "^2.0.5", - "jszip": "^3.10.1", - "ora": "^7.0.1", - "re-resizable": "^6.9.9", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "use-context-selector": "^1.4.1", - "webpack": "^5.88.2", - "webpack-cli": "^5.1.4", - "webpack-dev-server": "^4.15.1", - "webpackbar": "^5.0.2" - }, - "dependencies": { - "ansi-regex": { - "version": "6.0.1" - }, - "chalk": { - "version": "5.3.0" - }, - "emoji-regex": { - "version": "10.3.0" - }, - "is-interactive": { - "version": "2.0.0" - }, - "is-unicode-supported": { - "version": "1.3.0" - }, - "log-symbols": { - "version": "5.1.0", - "requires": { - "chalk": "^5.0.0", - "is-unicode-supported": "^1.1.0" - } - }, - "ora": { - "version": "7.0.1", - "requires": { - "chalk": "^5.3.0", - "cli-cursor": "^4.0.0", - "cli-spinners": "^2.9.0", - "is-interactive": "^2.0.0", - "is-unicode-supported": "^1.3.0", - "log-symbols": "^5.1.0", - "stdin-discarder": "^0.1.0", - "string-width": "^6.1.0", - "strip-ansi": "^7.1.0" - } - }, - "string-width": { - "version": "6.1.0", - "requires": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^10.2.1", - "strip-ansi": "^7.0.1" - } - }, - "strip-ansi": { - "version": "7.1.0", - "requires": { - "ansi-regex": "^6.0.1" - } - } - } - }, - "@ps-analysis-tool/common": { - "version": "file:packages/common", - "requires": { - "devtools-protocol": "^0.0.1282316", - "tldts": "^6.0.14" - }, - "dependencies": { - "devtools-protocol": { - "version": "0.0.1282316", - "dev": true - } - } - }, - "@ps-analysis-tool/design-system": { - "version": "file:packages/design-system", - "requires": { - "@ps-analysis-tool/common": "*", - "p-queue": "^7.3.4", - "use-context-selector": "^1.4.1" - } - }, - "@ps-analysis-tool/eslint-import-resolver": { - "version": "file:packages/eslint-import-resolver", - "requires": { - "eslint-import-resolver-node": "^0.3.7" - } - }, - "@ps-analysis-tool/extension": { - "version": "file:packages/extension", - "requires": { - "@floating-ui/core": "^1.5.0", - "@floating-ui/dom": "^1.5.3", - "@ps-analysis-tool/common": "*", - "@ps-analysis-tool/design-system": "*", - "@ps-analysis-tool/library-detection": "*", - "@types/react-copy-to-clipboard": "^5.0.4", - "classnames": "^2.3.2", - "devtools-protocol": "^0.0.1282316", - "fast-xml-parser": "^4.3.2", - "file-saver": "^2.0.5", - "html-inline-script-webpack-plugin": "^3.2.1", - "p-queue": "^7.3.4", - "re-resizable": "^6.9.9", - "react": "^18.2.0", - "react-copy-to-clipboard": "^5.1.0", - "react-dom": "^18.2.0", - "react-error-boundary": "^4.0.11", - "shallow-equal": "^3.1.0", - "simple-cookie": "^1.0.15", - "tldts": "^6.0.14", - "use-context-selector": "^1.4.1", - "use-debounce": "^9.0.4", - "validate.js": "^0.13.1", - "victory": "^36.6.11" - }, - "dependencies": { - "devtools-protocol": { - "version": "0.0.1282316", - "dev": true - } - } - }, - "@ps-analysis-tool/i18n": { - "version": "file:packages/i18n", - "requires": { - "intl-messageformat": "^10.5.11" - } - }, - "@ps-analysis-tool/library-detection": { - "version": "file:packages/library-detection", - "requires": { - "@ps-analysis-tool/common": "*", - "@ps-analysis-tool/design-system": "*", - "classnames": "^2.5.1", - "escape-string-regexp": "^4.0.0", - "react": "^18.2.0", - "use-context-selector": "^1.4.1" - }, - "dependencies": { - "escape-string-regexp": { - "version": "4.0.0" - } - } - }, - "@ps-analysis-tool/report": { - "version": "file:packages/report", - "requires": { - "@babel/plugin-transform-react-jsx": "^7.22.15", - "@babel/preset-env": "^7.22.15", - "@babel/preset-react": "^7.22.15", - "@babel/preset-typescript": "^7.22.15", - "@ps-analysis-tool/common": "*", - "@ps-analysis-tool/design-system": "*", - "@ps-analysis-tool/library-detection": "*", - "@types/file-saver": "^2.0.5", - "babel-loader": "^9.1.3", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "webpack": "^5.88.2", - "webpack-cli": "^5.1.4", - "webpack-dev-server": "^4.15.1", - "webpackbar": "^5.0.2" - } - }, - "@puppeteer/browsers": { - "version": "1.6.0", - "requires": { - "debug": "4.3.4", - "extract-zip": "2.0.1", - "progress": "2.0.3", - "proxy-agent": "6.3.0", - "tar-fs": "3.0.4", - "unbzip2-stream": "1.4.3", - "yargs": "17.7.1" - }, - "dependencies": { - "emoji-regex": { - "version": "8.0.0" - }, - "extract-zip": { - "version": "2.0.1", - "requires": { - "@types/yauzl": "^2.9.1", - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - } - }, - "get-stream": { - "version": "5.2.0", - "requires": { - "pump": "^3.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "3.0.0" - }, - "string-width": { - "version": "4.2.3", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "tar-fs": { - "version": "3.0.4", - "requires": { - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^3.1.5" - } - }, - "tar-stream": { - "version": "3.1.7", - "requires": { - "b4a": "^1.6.4", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" - } - }, - "yargs": { - "version": "17.7.1", - "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - } - } - } - }, - "@radix-ui/number": { - "version": "1.0.1", - "dev": true, - "requires": { - "@babel/runtime": "^7.13.10" - } - }, - "@radix-ui/primitive": { - "version": "1.0.1", - "dev": true, - "requires": { - "@babel/runtime": "^7.13.10" - } - }, - "@radix-ui/react-arrow": { - "version": "1.0.3", - "dev": true, - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.3" - } - }, - "@radix-ui/react-collection": { - "version": "1.0.3", - "dev": true, - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-slot": "1.0.2" - } - }, - "@radix-ui/react-compose-refs": { - "version": "1.0.1", - "dev": true, - "requires": { - "@babel/runtime": "^7.13.10" - } - }, - "@radix-ui/react-context": { - "version": "1.0.1", - "dev": true, - "requires": { - "@babel/runtime": "^7.13.10" - } - }, - "@radix-ui/react-direction": { - "version": "1.0.1", - "dev": true, - "requires": { - "@babel/runtime": "^7.13.10" - } - }, - "@radix-ui/react-dismissable-layer": { - "version": "1.0.4", - "dev": true, - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-escape-keydown": "1.0.3" - } - }, - "@radix-ui/react-focus-guards": { - "version": "1.0.1", - "dev": true, - "requires": { - "@babel/runtime": "^7.13.10" - } - }, - "@radix-ui/react-focus-scope": { - "version": "1.0.3", - "dev": true, - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1" - } - }, - "@radix-ui/react-id": { - "version": "1.0.1", - "dev": true, - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-layout-effect": "1.0.1" - } - }, - "@radix-ui/react-popper": { - "version": "1.1.2", - "dev": true, - "requires": { - "@babel/runtime": "^7.13.10", - "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.0.3", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-layout-effect": "1.0.1", - "@radix-ui/react-use-rect": "1.0.1", - "@radix-ui/react-use-size": "1.0.1", - "@radix-ui/rect": "1.0.1" - } - }, - "@radix-ui/react-portal": { - "version": "1.0.3", - "dev": true, - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.3" - } - }, - "@radix-ui/react-primitive": { - "version": "1.0.3", - "dev": true, - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-slot": "1.0.2" - } - }, - "@radix-ui/react-roving-focus": { - "version": "1.0.4", - "dev": true, - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-collection": "1.0.3", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-direction": "1.0.1", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-controllable-state": "1.0.1" - } - }, - "@radix-ui/react-select": { - "version": "1.2.2", - "dev": true, - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/number": "1.0.1", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-collection": "1.0.3", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-direction": "1.0.1", - "@radix-ui/react-dismissable-layer": "1.0.4", - "@radix-ui/react-focus-guards": "1.0.1", - "@radix-ui/react-focus-scope": "1.0.3", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-popper": "1.1.2", - "@radix-ui/react-portal": "1.0.3", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-slot": "1.0.2", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-controllable-state": "1.0.1", - "@radix-ui/react-use-layout-effect": "1.0.1", - "@radix-ui/react-use-previous": "1.0.1", - "@radix-ui/react-visually-hidden": "1.0.3", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.5.5" - } - }, - "@radix-ui/react-separator": { - "version": "1.0.3", - "dev": true, - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.3" - } - }, - "@radix-ui/react-slot": { - "version": "1.0.2", - "dev": true, - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1" - } - }, - "@radix-ui/react-toggle": { - "version": "1.0.3", - "dev": true, - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-controllable-state": "1.0.1" - } - }, - "@radix-ui/react-toggle-group": { - "version": "1.0.4", - "dev": true, - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-direction": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-roving-focus": "1.0.4", - "@radix-ui/react-toggle": "1.0.3", - "@radix-ui/react-use-controllable-state": "1.0.1" - } - }, - "@radix-ui/react-toolbar": { - "version": "1.0.4", - "dev": true, - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-direction": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-roving-focus": "1.0.4", - "@radix-ui/react-separator": "1.0.3", - "@radix-ui/react-toggle-group": "1.0.4" - } - }, - "@radix-ui/react-use-callback-ref": { - "version": "1.0.1", - "dev": true, - "requires": { - "@babel/runtime": "^7.13.10" - } - }, - "@radix-ui/react-use-controllable-state": { - "version": "1.0.1", - "dev": true, - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-callback-ref": "1.0.1" - } - }, - "@radix-ui/react-use-escape-keydown": { - "version": "1.0.3", - "dev": true, - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-callback-ref": "1.0.1" - } - }, - "@radix-ui/react-use-layout-effect": { - "version": "1.0.1", - "dev": true, - "requires": { - "@babel/runtime": "^7.13.10" - } - }, - "@radix-ui/react-use-previous": { - "version": "1.0.1", - "dev": true, - "requires": { - "@babel/runtime": "^7.13.10" - } - }, - "@radix-ui/react-use-rect": { - "version": "1.0.1", - "dev": true, - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/rect": "1.0.1" - } - }, - "@radix-ui/react-use-size": { - "version": "1.0.1", - "dev": true, - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-layout-effect": "1.0.1" - } - }, - "@radix-ui/react-visually-hidden": { - "version": "1.0.3", - "dev": true, - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.3" - } - }, - "@radix-ui/rect": { - "version": "1.0.1", - "dev": true, - "requires": { - "@babel/runtime": "^7.13.10" - } - }, - "@sinclair/typebox": { - "version": "0.27.8", - "dev": true - }, - "@sindresorhus/is": { - "version": "4.6.0" - }, - "@sinonjs/commons": { - "version": "3.0.1", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/fake-timers": { - "version": "10.3.0", - "dev": true, - "requires": { - "@sinonjs/commons": "^3.0.0" - } - }, - "@sinonjs/formatio": { - "version": "3.2.2", - "dev": true, - "requires": { - "@sinonjs/commons": "^1", - "@sinonjs/samsam": "^3.1.0" - }, - "dependencies": { - "@sinonjs/commons": { - "version": "1.8.6", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - } - } - }, - "@sinonjs/samsam": { - "version": "3.3.3", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.3.0", - "array-from": "^2.1.1", - "lodash": "^4.17.15" - }, - "dependencies": { - "@sinonjs/commons": { - "version": "1.8.6", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - } - } - }, - "@sinonjs/text-encoding": { - "version": "0.7.2", - "dev": true - }, - "@storybook/addon-actions": { - "version": "7.6.18", - "dev": true, - "requires": { - "@storybook/core-events": "7.6.18", - "@storybook/global": "^5.0.0", - "@types/uuid": "^9.0.1", - "dequal": "^2.0.2", - "polished": "^4.2.2", - "uuid": "^9.0.0" - } - }, - "@storybook/addon-backgrounds": { - "version": "7.6.18", - "dev": true, - "requires": { - "@storybook/global": "^5.0.0", - "memoizerific": "^1.11.3", - "ts-dedent": "^2.0.0" - } - }, - "@storybook/addon-controls": { - "version": "7.6.18", - "dev": true, - "requires": { - "@storybook/blocks": "7.6.18", - "lodash": "^4.17.21", - "ts-dedent": "^2.0.0" - } - }, - "@storybook/addon-docs": { - "version": "7.6.18", - "dev": true, - "requires": { - "@jest/transform": "^29.3.1", - "@mdx-js/react": "^2.1.5", - "@storybook/blocks": "7.6.18", - "@storybook/client-logger": "7.6.18", - "@storybook/components": "7.6.18", - "@storybook/csf-plugin": "7.6.18", - "@storybook/csf-tools": "7.6.18", - "@storybook/global": "^5.0.0", - "@storybook/mdx2-csf": "^1.0.0", - "@storybook/node-logger": "7.6.18", - "@storybook/postinstall": "7.6.18", - "@storybook/preview-api": "7.6.18", - "@storybook/react-dom-shim": "7.6.18", - "@storybook/theming": "7.6.18", - "@storybook/types": "7.6.18", - "fs-extra": "^11.1.0", - "remark-external-links": "^8.0.0", - "remark-slug": "^6.0.0", - "ts-dedent": "^2.0.0" - } - }, - "@storybook/addon-essentials": { - "version": "7.6.18", - "dev": true, - "requires": { - "@storybook/addon-actions": "7.6.18", - "@storybook/addon-backgrounds": "7.6.18", - "@storybook/addon-controls": "7.6.18", - "@storybook/addon-docs": "7.6.18", - "@storybook/addon-highlight": "7.6.18", - "@storybook/addon-measure": "7.6.18", - "@storybook/addon-outline": "7.6.18", - "@storybook/addon-toolbars": "7.6.18", - "@storybook/addon-viewport": "7.6.18", - "@storybook/core-common": "7.6.18", - "@storybook/manager-api": "7.6.18", - "@storybook/node-logger": "7.6.18", - "@storybook/preview-api": "7.6.18", - "ts-dedent": "^2.0.0" - } - }, - "@storybook/addon-highlight": { - "version": "7.6.18", - "dev": true, - "requires": { - "@storybook/global": "^5.0.0" - } - }, - "@storybook/addon-interactions": { - "version": "7.6.18", - "dev": true, - "requires": { - "@storybook/global": "^5.0.0", - "@storybook/types": "7.6.18", - "jest-mock": "^27.0.6", - "polished": "^4.2.2", - "ts-dedent": "^2.2.0" - } - }, - "@storybook/addon-links": { - "version": "7.6.18", - "dev": true, - "requires": { - "@storybook/csf": "^0.1.2", - "@storybook/global": "^5.0.0", - "ts-dedent": "^2.0.0" - } - }, - "@storybook/addon-measure": { - "version": "7.6.18", - "dev": true, - "requires": { - "@storybook/global": "^5.0.0", - "tiny-invariant": "^1.3.1" - } - }, - "@storybook/addon-outline": { - "version": "7.6.18", - "dev": true, - "requires": { - "@storybook/global": "^5.0.0", - "ts-dedent": "^2.0.0" - } - }, - "@storybook/addon-styling": { - "version": "1.3.7", - "dev": true, - "requires": { - "@babel/template": "^7.20.7", - "@babel/types": "^7.21.5", - "@storybook/api": "^7.0.12", - "@storybook/components": "^7.0.12", - "@storybook/core-common": "^7.0.12", - "@storybook/core-events": "^7.0.12", - "@storybook/manager-api": "^7.0.12", - "@storybook/node-logger": "^7.0.12", - "@storybook/preview-api": "^7.0.12", - "@storybook/theming": "^7.0.12", - "@storybook/types": "^7.0.12", - "css-loader": "^6.7.3", - "less-loader": "^11.1.0", - "postcss-loader": "^7.2.4", - "prettier": "^2.8.0", - "resolve-url-loader": "^5.0.0", - "sass-loader": "^13.2.2", - "style-loader": "^3.3.2" - } - }, - "@storybook/addon-toolbars": { - "version": "7.6.18", - "dev": true - }, - "@storybook/addon-viewport": { - "version": "7.6.18", - "dev": true, - "requires": { - "memoizerific": "^1.11.3" - } - }, - "@storybook/api": { - "version": "7.6.17", - "dev": true, - "requires": { - "@storybook/client-logger": "7.6.17", - "@storybook/manager-api": "7.6.17" - }, - "dependencies": { - "@storybook/channels": { - "version": "7.6.17", - "dev": true, - "requires": { - "@storybook/client-logger": "7.6.17", - "@storybook/core-events": "7.6.17", - "@storybook/global": "^5.0.0", - "qs": "^6.10.0", - "telejson": "^7.2.0", - "tiny-invariant": "^1.3.1" - } - }, - "@storybook/client-logger": { - "version": "7.6.17", - "dev": true, - "requires": { - "@storybook/global": "^5.0.0" - } - }, - "@storybook/core-events": { - "version": "7.6.17", - "dev": true, - "requires": { - "ts-dedent": "^2.0.0" - } - }, - "@storybook/manager-api": { - "version": "7.6.17", - "dev": true, - "requires": { - "@storybook/channels": "7.6.17", - "@storybook/client-logger": "7.6.17", - "@storybook/core-events": "7.6.17", - "@storybook/csf": "^0.1.2", - "@storybook/global": "^5.0.0", - "@storybook/router": "7.6.17", - "@storybook/theming": "7.6.17", - "@storybook/types": "7.6.17", - "dequal": "^2.0.2", - "lodash": "^4.17.21", - "memoizerific": "^1.11.3", - "store2": "^2.14.2", - "telejson": "^7.2.0", - "ts-dedent": "^2.0.0" - } - }, - "@storybook/router": { - "version": "7.6.17", - "dev": true, - "requires": { - "@storybook/client-logger": "7.6.17", - "memoizerific": "^1.11.3", - "qs": "^6.10.0" - } - }, - "@storybook/theming": { - "version": "7.6.17", - "dev": true, - "requires": { - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@storybook/client-logger": "7.6.17", - "@storybook/global": "^5.0.0", - "memoizerific": "^1.11.3" - } - }, - "@storybook/types": { - "version": "7.6.17", - "dev": true, - "requires": { - "@storybook/channels": "7.6.17", - "@types/babel__core": "^7.0.0", - "@types/express": "^4.7.0", - "file-system-cache": "2.3.0" - } - } - } - }, - "@storybook/blocks": { - "version": "7.6.18", - "dev": true, - "requires": { - "@storybook/channels": "7.6.18", - "@storybook/client-logger": "7.6.18", - "@storybook/components": "7.6.18", - "@storybook/core-events": "7.6.18", - "@storybook/csf": "^0.1.2", - "@storybook/docs-tools": "7.6.18", - "@storybook/global": "^5.0.0", - "@storybook/manager-api": "7.6.18", - "@storybook/preview-api": "7.6.18", - "@storybook/theming": "7.6.18", - "@storybook/types": "7.6.18", - "@types/lodash": "^4.14.167", - "color-convert": "^2.0.1", - "dequal": "^2.0.2", - "lodash": "^4.17.21", - "markdown-to-jsx": "^7.1.8", - "memoizerific": "^1.11.3", - "polished": "^4.2.2", - "react-colorful": "^5.1.2", - "telejson": "^7.2.0", - "tocbot": "^4.20.1", - "ts-dedent": "^2.0.0", - "util-deprecate": "^1.0.2" - } - }, - "@storybook/builder-webpack5": { - "version": "7.6.18", - "dev": true, - "requires": { - "@babel/core": "^7.23.2", - "@storybook/channels": "7.6.18", - "@storybook/client-logger": "7.6.18", - "@storybook/core-common": "7.6.18", - "@storybook/core-events": "7.6.18", - "@storybook/core-webpack": "7.6.18", - "@storybook/node-logger": "7.6.18", - "@storybook/preview": "7.6.18", - "@storybook/preview-api": "7.6.18", - "@swc/core": "^1.3.82", - "@types/node": "^18.0.0", - "@types/semver": "^7.3.4", - "babel-loader": "^9.0.0", - "browser-assert": "^1.2.1", - "case-sensitive-paths-webpack-plugin": "^2.4.0", - "cjs-module-lexer": "^1.2.3", - "constants-browserify": "^1.0.0", - "css-loader": "^6.7.1", - "es-module-lexer": "^1.4.1", - "express": "^4.17.3", - "fork-ts-checker-webpack-plugin": "^8.0.0", - "fs-extra": "^11.1.0", - "html-webpack-plugin": "^5.5.0", - "magic-string": "^0.30.5", - "path-browserify": "^1.0.1", - "process": "^0.11.10", - "semver": "^7.3.7", - "style-loader": "^3.3.1", - "swc-loader": "^0.2.3", - "terser-webpack-plugin": "^5.3.1", - "ts-dedent": "^2.0.0", - "url": "^0.11.0", - "util": "^0.12.4", - "util-deprecate": "^1.0.2", - "webpack": "5", - "webpack-dev-middleware": "^6.1.1", - "webpack-hot-middleware": "^2.25.1", - "webpack-virtual-modules": "^0.5.0" - }, - "dependencies": { - "@types/node": { - "version": "18.19.31", - "dev": true, - "requires": { - "undici-types": "~5.26.4" - } - }, - "lru-cache": { - "version": "6.0.0", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.6.0", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "dev": true - } - } - }, - "@storybook/channels": { - "version": "7.6.18", - "dev": true, - "requires": { - "@storybook/client-logger": "7.6.18", - "@storybook/core-events": "7.6.18", - "@storybook/global": "^5.0.0", - "qs": "^6.10.0", - "telejson": "^7.2.0", - "tiny-invariant": "^1.3.1" - } - }, - "@storybook/client-logger": { - "version": "7.6.18", - "dev": true, - "requires": { - "@storybook/global": "^5.0.0" - } - }, - "@storybook/components": { - "version": "7.6.18", - "dev": true, - "requires": { - "@radix-ui/react-select": "^1.2.2", - "@radix-ui/react-toolbar": "^1.0.4", - "@storybook/client-logger": "7.6.18", - "@storybook/csf": "^0.1.2", - "@storybook/global": "^5.0.0", - "@storybook/theming": "7.6.18", - "@storybook/types": "7.6.18", - "memoizerific": "^1.11.3", - "use-resize-observer": "^9.1.0", - "util-deprecate": "^1.0.2" - } - }, - "@storybook/core-client": { - "version": "7.6.18", - "dev": true, - "requires": { - "@storybook/client-logger": "7.6.18", - "@storybook/preview-api": "7.6.18" - } - }, - "@storybook/core-common": { - "version": "7.6.18", - "dev": true, - "requires": { - "@storybook/core-events": "7.6.18", - "@storybook/node-logger": "7.6.18", - "@storybook/types": "7.6.18", - "@types/find-cache-dir": "^3.2.1", - "@types/node": "^18.0.0", - "@types/node-fetch": "^2.6.4", - "@types/pretty-hrtime": "^1.0.0", - "chalk": "^4.1.0", - "esbuild": "^0.18.0", - "esbuild-register": "^3.5.0", - "file-system-cache": "2.3.0", - "find-cache-dir": "^3.0.0", - "find-up": "^5.0.0", - "fs-extra": "^11.1.0", - "glob": "^10.0.0", - "handlebars": "^4.7.7", - "lazy-universal-dotenv": "^4.0.0", - "node-fetch": "^2.0.0", - "picomatch": "^2.3.0", - "pkg-dir": "^5.0.0", - "pretty-hrtime": "^1.0.3", - "resolve-from": "^5.0.0", - "ts-dedent": "^2.0.0" - }, - "dependencies": { - "@types/node": { - "version": "18.19.31", - "dev": true, - "requires": { - "undici-types": "~5.26.4" - } - }, - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@storybook/core-events": { - "version": "7.6.18", - "dev": true, - "requires": { - "ts-dedent": "^2.0.0" - } - }, - "@storybook/core-webpack": { - "version": "7.6.18", - "dev": true, - "requires": { - "@storybook/core-common": "7.6.18", - "@storybook/node-logger": "7.6.18", - "@storybook/types": "7.6.18", - "@types/node": "^18.0.0", - "ts-dedent": "^2.0.0" - }, - "dependencies": { - "@types/node": { - "version": "18.19.31", - "dev": true, - "requires": { - "undici-types": "~5.26.4" - } - } - } - }, - "@storybook/csf": { - "version": "0.1.5", - "dev": true, - "requires": { - "type-fest": "^2.19.0" - } - }, - "@storybook/csf-plugin": { - "version": "7.6.18", - "dev": true, - "requires": { - "@storybook/csf-tools": "7.6.18", - "unplugin": "^1.3.1" - } - }, - "@storybook/csf-tools": { - "version": "7.6.18", - "dev": true, - "requires": { - "@babel/generator": "^7.23.0", - "@babel/parser": "^7.23.0", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0", - "@storybook/csf": "^0.1.2", - "@storybook/types": "7.6.18", - "fs-extra": "^11.1.0", - "recast": "^0.23.1", - "ts-dedent": "^2.0.0" - } - }, - "@storybook/docs-tools": { - "version": "7.6.18", - "dev": true, - "requires": { - "@storybook/core-common": "7.6.18", - "@storybook/preview-api": "7.6.18", - "@storybook/types": "7.6.18", - "@types/doctrine": "^0.0.3", - "assert": "^2.1.0", - "doctrine": "^3.0.0", - "lodash": "^4.17.21" - } - }, - "@storybook/global": { - "version": "5.0.0", - "dev": true - }, - "@storybook/instrumenter": { - "version": "7.6.18", - "dev": true, - "requires": { - "@storybook/channels": "7.6.18", - "@storybook/client-logger": "7.6.18", - "@storybook/core-events": "7.6.18", - "@storybook/global": "^5.0.0", - "@storybook/preview-api": "7.6.18", - "@vitest/utils": "^0.34.6", - "util": "^0.12.4" - } - }, - "@storybook/manager-api": { - "version": "7.6.18", - "dev": true, - "requires": { - "@storybook/channels": "7.6.18", - "@storybook/client-logger": "7.6.18", - "@storybook/core-events": "7.6.18", - "@storybook/csf": "^0.1.2", - "@storybook/global": "^5.0.0", - "@storybook/router": "7.6.18", - "@storybook/theming": "7.6.18", - "@storybook/types": "7.6.18", - "dequal": "^2.0.2", - "lodash": "^4.17.21", - "memoizerific": "^1.11.3", - "store2": "^2.14.2", - "telejson": "^7.2.0", - "ts-dedent": "^2.0.0" - } - }, - "@storybook/mdx2-csf": { - "version": "1.1.0", - "dev": true - }, - "@storybook/node-logger": { - "version": "7.6.18", - "dev": true - }, - "@storybook/postinstall": { - "version": "7.6.18", - "dev": true - }, - "@storybook/preset-react-webpack": { - "version": "7.6.18", - "dev": true, - "requires": { - "@babel/preset-flow": "^7.22.15", - "@babel/preset-react": "^7.22.15", - "@pmmmwh/react-refresh-webpack-plugin": "^0.5.11", - "@storybook/core-webpack": "7.6.18", - "@storybook/docs-tools": "7.6.18", - "@storybook/node-logger": "7.6.18", - "@storybook/react": "7.6.18", - "@storybook/react-docgen-typescript-plugin": "1.0.6--canary.9.0c3f3b7.0", - "@types/node": "^18.0.0", - "@types/semver": "^7.3.4", - "babel-plugin-add-react-displayname": "^0.0.5", - "fs-extra": "^11.1.0", - "magic-string": "^0.30.5", - "react-docgen": "^7.0.0", - "react-refresh": "^0.14.0", - "semver": "^7.3.7", - "webpack": "5" - }, - "dependencies": { - "@types/node": { - "version": "18.19.31", - "dev": true, - "requires": { - "undici-types": "~5.26.4" - } - }, - "lru-cache": { - "version": "6.0.0", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.6.0", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "dev": true - } - } - }, - "@storybook/preview": { - "version": "7.6.18", - "dev": true - }, - "@storybook/preview-api": { - "version": "7.6.18", - "dev": true, - "requires": { - "@storybook/channels": "7.6.18", - "@storybook/client-logger": "7.6.18", - "@storybook/core-events": "7.6.18", - "@storybook/csf": "^0.1.2", - "@storybook/global": "^5.0.0", - "@storybook/types": "7.6.18", - "@types/qs": "^6.9.5", - "dequal": "^2.0.2", - "lodash": "^4.17.21", - "memoizerific": "^1.11.3", - "qs": "^6.10.0", - "synchronous-promise": "^2.0.15", - "ts-dedent": "^2.0.0", - "util-deprecate": "^1.0.2" - } - }, - "@storybook/react": { - "version": "7.6.18", - "dev": true, - "requires": { - "@storybook/client-logger": "7.6.18", - "@storybook/core-client": "7.6.18", - "@storybook/docs-tools": "7.6.18", - "@storybook/global": "^5.0.0", - "@storybook/preview-api": "7.6.18", - "@storybook/react-dom-shim": "7.6.18", - "@storybook/types": "7.6.18", - "@types/escodegen": "^0.0.6", - "@types/estree": "^0.0.51", - "@types/node": "^18.0.0", - "acorn": "^7.4.1", - "acorn-jsx": "^5.3.1", - "acorn-walk": "^7.2.0", - "escodegen": "^2.1.0", - "html-tags": "^3.1.0", - "lodash": "^4.17.21", - "prop-types": "^15.7.2", - "react-element-to-jsx-string": "^15.0.0", - "ts-dedent": "^2.0.0", - "type-fest": "~2.19", - "util-deprecate": "^1.0.2" - }, - "dependencies": { - "@types/node": { - "version": "18.19.31", - "dev": true, - "requires": { - "undici-types": "~5.26.4" - } - } - } - }, - "@storybook/react-docgen-typescript-plugin": { - "version": "1.0.6--canary.9.0c3f3b7.0", - "dev": true, - "requires": { - "debug": "^4.1.1", - "endent": "^2.0.1", - "find-cache-dir": "^3.3.1", - "flat-cache": "^3.0.4", - "micromatch": "^4.0.2", - "react-docgen-typescript": "^2.2.2", - "tslib": "^2.0.0" - } - }, - "@storybook/react-dom-shim": { - "version": "7.6.18", - "dev": true, - "requires": {} - }, - "@storybook/react-webpack5": { - "version": "7.6.18", - "dev": true, - "requires": { - "@storybook/builder-webpack5": "7.6.18", - "@storybook/preset-react-webpack": "7.6.18", - "@storybook/react": "7.6.18", - "@types/node": "^18.0.0" - }, - "dependencies": { - "@types/node": { - "version": "18.19.31", - "dev": true, - "requires": { - "undici-types": "~5.26.4" - } - } - } - }, - "@storybook/router": { - "version": "7.6.18", - "dev": true, - "requires": { - "@storybook/client-logger": "7.6.18", - "memoizerific": "^1.11.3", - "qs": "^6.10.0" - } - }, - "@storybook/testing-library": { - "version": "0.0.14-next.2", - "dev": true, - "requires": { - "@storybook/client-logger": "^7.0.0-beta.0 || ^7.0.0-rc.0 || ^7.0.0", - "@storybook/instrumenter": "^7.0.0-beta.0 || ^7.0.0-rc.0 || ^7.0.0", - "@testing-library/dom": "^8.3.0", - "@testing-library/user-event": "^13.2.1", - "ts-dedent": "^2.2.0" - } - }, - "@storybook/theming": { - "version": "7.6.18", - "dev": true, - "requires": { - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@storybook/client-logger": "7.6.18", - "@storybook/global": "^5.0.0", - "memoizerific": "^1.11.3" - } - }, - "@storybook/types": { - "version": "7.6.18", - "dev": true, - "requires": { - "@storybook/channels": "7.6.18", - "@types/babel__core": "^7.0.0", - "@types/express": "^4.7.0", - "file-system-cache": "2.3.0" - } - }, - "@svgr/babel-plugin-add-jsx-attribute": { - "version": "8.0.0", - "dev": true, - "requires": {} - }, - "@svgr/babel-plugin-remove-jsx-attribute": { - "version": "8.0.0", - "dev": true, - "requires": {} - }, - "@svgr/babel-plugin-remove-jsx-empty-expression": { - "version": "8.0.0", - "dev": true, - "requires": {} - }, - "@svgr/babel-plugin-replace-jsx-attribute-value": { - "version": "8.0.0", - "dev": true, - "requires": {} - }, - "@svgr/babel-plugin-svg-dynamic-title": { - "version": "8.0.0", - "dev": true, - "requires": {} - }, - "@svgr/babel-plugin-svg-em-dimensions": { - "version": "8.0.0", - "dev": true, - "requires": {} - }, - "@svgr/babel-plugin-transform-react-native-svg": { - "version": "8.1.0", - "dev": true, - "requires": {} - }, - "@svgr/babel-plugin-transform-svg-component": { - "version": "8.0.0", - "dev": true, - "requires": {} - }, - "@svgr/babel-preset": { - "version": "8.1.0", - "dev": true, - "requires": { - "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", - "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", - "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", - "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", - "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", - "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", - "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", - "@svgr/babel-plugin-transform-svg-component": "8.0.0" - } - }, - "@svgr/core": { - "version": "8.1.0", - "dev": true, - "requires": { - "@babel/core": "^7.21.3", - "@svgr/babel-preset": "8.1.0", - "camelcase": "^6.2.0", - "cosmiconfig": "^8.1.3", - "snake-case": "^3.0.4" - } - }, - "@svgr/hast-util-to-babel-ast": { - "version": "8.0.0", - "dev": true, - "requires": { - "@babel/types": "^7.21.3", - "entities": "^4.4.0" - } - }, - "@svgr/plugin-jsx": { - "version": "8.1.0", - "dev": true, - "requires": { - "@babel/core": "^7.21.3", - "@svgr/babel-preset": "8.1.0", - "@svgr/hast-util-to-babel-ast": "8.0.0", - "svg-parser": "^2.0.4" - } - }, - "@svgr/plugin-svgo": { - "version": "8.1.0", - "dev": true, - "requires": { - "cosmiconfig": "^8.1.3", - "deepmerge": "^4.3.1", - "svgo": "^3.0.2" - } - }, - "@svgr/webpack": { - "version": "8.1.0", - "dev": true, - "requires": { - "@babel/core": "^7.21.3", - "@babel/plugin-transform-react-constant-elements": "^7.21.3", - "@babel/preset-env": "^7.20.2", - "@babel/preset-react": "^7.18.6", - "@babel/preset-typescript": "^7.21.0", - "@svgr/core": "8.1.0", - "@svgr/plugin-jsx": "8.1.0", - "@svgr/plugin-svgo": "8.1.0" - } - }, - "@swc/core": { - "version": "1.4.17", - "dev": true, - "requires": { - "@swc/core-darwin-arm64": "1.4.17", - "@swc/core-darwin-x64": "1.4.17", - "@swc/core-linux-arm-gnueabihf": "1.4.17", - "@swc/core-linux-arm64-gnu": "1.4.17", - "@swc/core-linux-arm64-musl": "1.4.17", - "@swc/core-linux-x64-gnu": "1.4.17", - "@swc/core-linux-x64-musl": "1.4.17", - "@swc/core-win32-arm64-msvc": "1.4.17", - "@swc/core-win32-ia32-msvc": "1.4.17", - "@swc/core-win32-x64-msvc": "1.4.17", - "@swc/counter": "^0.1.2", - "@swc/types": "^0.1.5" - } - }, - "@swc/core-darwin-arm64": { - "version": "1.4.17", - "dev": true, - "optional": true - }, - "@swc/counter": { - "version": "0.1.3", - "dev": true - }, - "@swc/types": { - "version": "0.1.6", - "dev": true, - "requires": { - "@swc/counter": "^0.1.3" - } - }, - "@szmarczak/http-timer": { - "version": "4.0.6", - "requires": { - "defer-to-connect": "^2.0.0" - } - }, - "@testing-library/dom": { - "version": "8.20.1", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "5.1.3", - "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.5.0", - "pretty-format": "^27.0.2" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@testing-library/jest-dom": { - "version": "5.17.0", - "dev": true, - "requires": { - "@adobe/css-tools": "^4.0.1", - "@babel/runtime": "^7.9.2", - "@types/testing-library__jest-dom": "^5.9.1", - "aria-query": "^5.0.0", - "chalk": "^3.0.0", - "css.escape": "^1.5.1", - "dom-accessibility-api": "^0.5.6", - "lodash": "^4.17.15", - "redent": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@testing-library/react": { - "version": "14.3.1", - "dev": true, - "requires": { - "@babel/runtime": "^7.12.5", - "@testing-library/dom": "^9.0.0", - "@types/react-dom": "^18.0.0" - }, - "dependencies": { - "@testing-library/dom": { - "version": "9.3.4", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "5.1.3", - "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.5.0", - "pretty-format": "^27.0.2" - } - }, - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@testing-library/user-event": { - "version": "13.5.0", - "dev": true, - "requires": { - "@babel/runtime": "^7.12.5" - } - }, - "@tootallnate/once": { - "version": "2.0.0", - "dev": true - }, - "@tootallnate/quickjs-emscripten": { - "version": "0.23.0" - }, - "@trysound/sax": { - "version": "0.2.0", - "dev": true - }, - "@types/aria-query": { - "version": "5.0.4", - "dev": true - }, - "@types/babel__core": { - "version": "7.20.5", - "dev": true, - "requires": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@types/babel__generator": { - "version": "7.6.8", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.4.4", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.20.5", - "dev": true, - "requires": { - "@babel/types": "^7.20.7" - } - }, - "@types/body-parser": { - "version": "1.19.5", - "dev": true, - "requires": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "@types/bonjour": { - "version": "3.5.13", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/cacheable-request": { - "version": "6.0.3", - "requires": { - "@types/http-cache-semantics": "*", - "@types/keyv": "^3.1.4", - "@types/node": "*", - "@types/responselike": "^1.0.0" - } - }, - "@types/chrome": { - "version": "0.0.237", - "dev": true, - "requires": { - "@types/filesystem": "*", - "@types/har-format": "*" - } - }, - "@types/cli-color": { - "version": "2.0.6", - "dev": true - }, - "@types/connect": { - "version": "3.4.38", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/connect-history-api-fallback": { - "version": "1.5.4", - "dev": true, - "requires": { - "@types/express-serve-static-core": "*", - "@types/node": "*" - } - }, - "@types/d3-array": { - "version": "3.2.1" - }, - "@types/d3-color": { - "version": "3.1.3" - }, - "@types/d3-ease": { - "version": "3.0.2" - }, - "@types/d3-interpolate": { - "version": "3.0.4", - "requires": { - "@types/d3-color": "*" - } - }, - "@types/d3-path": { - "version": "3.1.0" - }, - "@types/d3-scale": { - "version": "4.0.8", - "requires": { - "@types/d3-time": "*" - } - }, - "@types/d3-shape": { - "version": "3.1.6", - "requires": { - "@types/d3-path": "*" - } - }, - "@types/d3-time": { - "version": "3.0.3" - }, - "@types/d3-timer": { - "version": "3.0.2" - }, - "@types/doctrine": { - "version": "0.0.3", - "dev": true - }, - "@types/escodegen": { - "version": "0.0.6", - "dev": true - }, - "@types/eslint": { - "version": "8.56.10", - "dev": true, - "requires": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "@types/eslint-scope": { - "version": "3.7.7", - "dev": true, - "requires": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "@types/estree": { - "version": "0.0.51", - "dev": true - }, - "@types/express": { - "version": "4.17.21", - "dev": true, - "requires": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "@types/express-serve-static-core": { - "version": "4.19.0", - "dev": true, - "requires": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "@types/file-saver": { - "version": "2.0.7", - "dev": true - }, - "@types/filesystem": { - "version": "0.0.36", - "dev": true, - "requires": { - "@types/filewriter": "*" - } - }, - "@types/filewriter": { - "version": "0.0.33", - "dev": true - }, - "@types/find-cache-dir": { - "version": "3.2.1", - "dev": true - }, - "@types/fs-extra": { - "version": "11.0.4", - "dev": true, - "requires": { - "@types/jsonfile": "*", - "@types/node": "*" - } - }, - "@types/graceful-fs": { - "version": "4.1.9", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/har-format": { - "version": "1.2.15", - "dev": true - }, - "@types/html-minifier-terser": { - "version": "6.1.0", - "dev": true - }, - "@types/http-cache-semantics": { - "version": "4.0.4" - }, - "@types/http-errors": { - "version": "2.0.4", - "dev": true - }, - "@types/http-proxy": { - "version": "1.17.14", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.6", - "dev": true - }, - "@types/istanbul-lib-report": { - "version": "3.0.3", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*" - } - }, - "@types/istanbul-reports": { - "version": "3.0.4", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/jest": { - "version": "29.5.12", - "dev": true, - "requires": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "dev": true - }, - "pretty-format": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - } - }, - "react-is": { - "version": "18.3.1", - "dev": true - } - } - }, - "@types/jsdom": { - "version": "20.0.1", - "dev": true, - "requires": { - "@types/node": "*", - "@types/tough-cookie": "*", - "parse5": "^7.0.0" - } - }, - "@types/json-schema": { - "version": "7.0.15", - "dev": true - }, - "@types/json5": { - "version": "0.0.29", - "dev": true - }, - "@types/jsonfile": { - "version": "6.1.4", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/keyv": { - "version": "3.1.4", - "requires": { - "@types/node": "*" - } - }, - "@types/lodash": { - "version": "4.17.0", - "dev": true - }, - "@types/mdx": { - "version": "2.0.13", - "dev": true - }, - "@types/mime": { - "version": "1.3.5", - "dev": true - }, - "@types/node": { - "version": "20.12.7", - "requires": { - "undici-types": "~5.26.4" - } - }, - "@types/node-fetch": { - "version": "2.6.11", - "dev": true, - "requires": { - "@types/node": "*", - "form-data": "^4.0.0" - } - }, - "@types/node-forge": { - "version": "1.3.11", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/parse-json": { - "version": "4.0.2", - "dev": true - }, - "@types/pretty-hrtime": { - "version": "1.0.3", - "dev": true - }, - "@types/promptly": { - "version": "3.0.5", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/prop-types": { - "version": "15.7.12", - "dev": true - }, - "@types/qs": { - "version": "6.9.15", - "dev": true - }, - "@types/range-parser": { - "version": "1.2.7", - "dev": true - }, - "@types/react": { - "version": "18.3.1", - "dev": true, - "requires": { - "@types/prop-types": "*", - "csstype": "^3.0.2" - } - }, - "@types/react-copy-to-clipboard": { - "version": "5.0.7", - "dev": true, - "requires": { - "@types/react": "*" - } - }, - "@types/react-dom": { - "version": "18.3.0", - "dev": true, - "requires": { - "@types/react": "*" - } - }, - "@types/resolve": { - "version": "1.20.6", - "dev": true - }, - "@types/responselike": { - "version": "1.0.3", - "requires": { - "@types/node": "*" - } - }, - "@types/retry": { - "version": "0.12.0", - "dev": true - }, - "@types/semver": { - "version": "7.5.8", - "dev": true - }, - "@types/send": { - "version": "0.17.4", - "dev": true, - "requires": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "@types/serve-index": { - "version": "1.9.4", - "dev": true, - "requires": { - "@types/express": "*" - } - }, - "@types/serve-static": { - "version": "1.15.7", - "dev": true, - "requires": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "*" - } - }, - "@types/sinon": { - "version": "17.0.3", - "dev": true, - "requires": { - "@types/sinonjs__fake-timers": "*" - } - }, - "@types/sinon-chrome": { - "version": "2.2.15", - "dev": true, - "requires": { - "@types/chrome": "*", - "@types/sinon": "*" - } - }, - "@types/sinonjs__fake-timers": { - "version": "8.1.5", - "dev": true - }, - "@types/sockjs": { - "version": "0.3.36", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/stack-utils": { - "version": "2.0.3", - "dev": true - }, - "@types/stylis": { - "version": "4.2.0", - "dev": true, - "peer": true - }, - "@types/testing-library__jest-dom": { - "version": "5.14.9", - "dev": true, - "requires": { - "@types/jest": "*" - } - }, - "@types/tough-cookie": { - "version": "4.0.5", - "dev": true - }, - "@types/unist": { - "version": "2.0.10", - "dev": true - }, - "@types/uuid": { - "version": "9.0.8", - "dev": true - }, - "@types/ws": { - "version": "8.5.10", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/xml2js": { - "version": "0.4.14", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/yargs": { - "version": "17.0.32", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "21.0.3", - "dev": true - }, - "@types/yauzl": { - "version": "2.10.3", - "optional": true, - "requires": { - "@types/node": "*" - } - }, - "@typescript-eslint/eslint-plugin": { - "version": "5.62.0", - "dev": true, - "requires": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/type-utils": "5.62.0", - "@typescript-eslint/utils": "5.62.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.6.0", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "dev": true - } - } - }, - "@typescript-eslint/parser": { - "version": "5.62.0", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "debug": "^4.3.4" - } - }, - "@typescript-eslint/scope-manager": { - "version": "5.62.0", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" - } - }, - "@typescript-eslint/type-utils": { - "version": "5.62.0", - "dev": true, - "requires": { - "@typescript-eslint/typescript-estree": "5.62.0", - "@typescript-eslint/utils": "5.62.0", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/types": { - "version": "5.62.0", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.6.0", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "dev": true - } - } - }, - "@typescript-eslint/utils": { - "version": "5.62.0", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.6.0", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "dev": true - } - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" - } - }, - "@ungap/structured-clone": { - "version": "1.2.0", - "dev": true - }, - "@vitest/utils": { - "version": "0.34.7", - "dev": true, - "requires": { - "diff-sequences": "^29.4.3", - "loupe": "^2.3.6", - "pretty-format": "^29.5.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "dev": true - }, - "pretty-format": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - } - }, - "react-is": { - "version": "18.3.1", - "dev": true - } - } - }, - "@webassemblyjs/ast": { - "version": "1.12.1", - "dev": true, - "requires": { - "@webassemblyjs/helper-numbers": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6" - } - }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.6", - "dev": true - }, - "@webassemblyjs/helper-api-error": { - "version": "1.11.6", - "dev": true - }, - "@webassemblyjs/helper-buffer": { - "version": "1.12.1", - "dev": true - }, - "@webassemblyjs/helper-numbers": { - "version": "1.11.6", - "dev": true, - "requires": { - "@webassemblyjs/floating-point-hex-parser": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.6", - "dev": true - }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.12.1", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.12.1" - } - }, - "@webassemblyjs/ieee754": { - "version": "1.11.6", - "dev": true, - "requires": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "@webassemblyjs/leb128": { - "version": "1.11.6", - "dev": true, - "requires": { - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/utf8": { - "version": "1.11.6", - "dev": true - }, - "@webassemblyjs/wasm-edit": { - "version": "1.12.1", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-opt": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1", - "@webassemblyjs/wast-printer": "1.12.1" - } - }, - "@webassemblyjs/wasm-gen": { - "version": "1.12.1", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "@webassemblyjs/wasm-opt": { - "version": "1.12.1", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1" - } - }, - "@webassemblyjs/wasm-parser": { - "version": "1.12.1", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-api-error": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "@webassemblyjs/wast-printer": { - "version": "1.12.1", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.12.1", - "@xtuc/long": "4.2.2" - } - }, - "@webpack-cli/configtest": { - "version": "2.1.1", - "dev": true, - "requires": {} - }, - "@webpack-cli/info": { - "version": "2.0.2", - "dev": true, - "requires": {} - }, - "@webpack-cli/serve": { - "version": "2.0.5", - "dev": true, - "requires": {} - }, - "@xtuc/ieee754": { - "version": "1.2.0", - "dev": true - }, - "@xtuc/long": { - "version": "4.2.2", - "dev": true - }, - "abab": { - "version": "2.0.6", - "dev": true - }, - "accepts": { - "version": "1.3.8", - "dev": true, - "requires": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - } - }, - "acorn": { - "version": "7.4.1", - "dev": true - }, - "acorn-globals": { - "version": "7.0.1", - "dev": true, - "requires": { - "acorn": "^8.1.0", - "acorn-walk": "^8.0.2" - }, - "dependencies": { - "acorn": { - "version": "8.11.3", - "dev": true - }, - "acorn-walk": { - "version": "8.3.2", - "dev": true - } - } - }, - "acorn-jsx": { - "version": "5.3.2", - "dev": true, - "requires": {} - }, - "acorn-walk": { - "version": "7.2.0", - "dev": true - }, - "adjust-sourcemap-loader": { - "version": "4.0.0", - "dev": true, - "requires": { - "loader-utils": "^2.0.0", - "regex-parser": "^2.2.11" - } - }, - "agent-base": { - "version": "6.0.2", - "requires": { - "debug": "4" - } - }, - "ajv": { - "version": "6.12.6", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-formats": { - "version": "2.1.1", - "dev": true, - "requires": { - "ajv": "^8.0.0" - }, - "dependencies": { - "ajv": { - "version": "8.13.0", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.3", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "dev": true - } - } - }, - "ajv-keywords": { - "version": "3.5.2", - "dev": true, - "requires": {} - }, - "ansi-escapes": { - "version": "4.3.2", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - }, - "dependencies": { - "type-fest": { - "version": "0.21.3", - "dev": true - } - } - }, - "ansi-html-community": { - "version": "0.0.8", - "dev": true - }, - "ansi-regex": { - "version": "5.0.1" - }, - "ansi-styles": { - "version": "3.2.1", - "requires": { - "color-convert": "^1.9.0" - }, - "dependencies": { - "color-convert": { - "version": "1.9.3", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3" - } - } - }, - "any-promise": { - "version": "1.3.0", - "dev": true - }, - "anymatch": { - "version": "3.1.3", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "app-root-dir": { - "version": "1.0.2", - "dev": true - }, - "are-docs-informative": { - "version": "0.0.2", - "dev": true - }, - "arg": { - "version": "5.0.2", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "aria-hidden": { - "version": "1.2.4", - "dev": true, - "requires": { - "tslib": "^2.0.0" - } - }, - "aria-query": { - "version": "5.1.3", - "dev": true, - "requires": { - "deep-equal": "^2.0.5" - } - }, - "array-buffer-byte-length": { - "version": "1.0.1", - "dev": true, - "requires": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" - } - }, - "array-flatten": { - "version": "1.1.1", - "dev": true - }, - "array-from": { - "version": "2.1.1", - "dev": true - }, - "array-includes": { - "version": "3.1.8", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "is-string": "^1.0.7" - } - }, - "array-union": { - "version": "2.1.0", - "dev": true - }, - "array.prototype.findlast": { - "version": "1.2.5", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" - } - }, - "array.prototype.findlastindex": { - "version": "1.2.5", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" - } - }, - "array.prototype.flat": { - "version": "1.3.2", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - } - }, - "array.prototype.flatmap": { - "version": "1.3.2", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - } - }, - "array.prototype.toreversed": { - "version": "1.1.2", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - } - }, - "array.prototype.tosorted": { - "version": "1.1.3", - "dev": true, - "requires": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.1.0", - "es-shim-unscopables": "^1.0.2" - } - }, - "arraybuffer.prototype.slice": { - "version": "1.0.3", - "dev": true, - "requires": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" - } - }, - "assert": { - "version": "2.1.0", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "is-nan": "^1.3.2", - "object-is": "^1.1.5", - "object.assign": "^4.1.4", - "util": "^0.12.5" - } - }, - "ast-types": { - "version": "0.16.1", - "dev": true, - "requires": { - "tslib": "^2.0.1" - } - }, - "ast-types-flow": { - "version": "0.0.8", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "dev": true - }, - "autoprefixer": { - "version": "10.4.19", - "dev": true, - "requires": { - "browserslist": "^4.23.0", - "caniuse-lite": "^1.0.30001599", - "fraction.js": "^4.3.7", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.2.0" - } - }, - "available-typed-arrays": { - "version": "1.0.7", - "dev": true, - "requires": { - "possible-typed-array-names": "^1.0.0" - } - }, - "axe-core": { - "version": "4.7.0", - "dev": true - }, - "axobject-query": { - "version": "3.2.1", - "dev": true, - "requires": { - "dequal": "^2.0.3" - } - }, - "b4a": { - "version": "1.6.6" - }, - "babel-jest": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "babel-loader": { - "version": "9.1.3", - "dev": true, - "requires": { - "find-cache-dir": "^4.0.0", - "schema-utils": "^4.0.0" - }, - "dependencies": { - "ajv": { - "version": "8.13.0", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.3", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" - } - }, - "ajv-keywords": { - "version": "5.1.0", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.3" - } - }, - "find-cache-dir": { - "version": "4.0.0", - "dev": true, - "requires": { - "common-path-prefix": "^3.0.0", - "pkg-dir": "^7.0.0" - } - }, - "find-up": { - "version": "6.3.0", - "dev": true, - "requires": { - "locate-path": "^7.1.0", - "path-exists": "^5.0.0" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "dev": true - }, - "locate-path": { - "version": "7.2.0", - "dev": true, - "requires": { - "p-locate": "^6.0.0" - } - }, - "p-limit": { - "version": "4.0.0", - "dev": true, - "requires": { - "yocto-queue": "^1.0.0" - } - }, - "p-locate": { - "version": "6.0.0", - "dev": true, - "requires": { - "p-limit": "^4.0.0" - } - }, - "path-exists": { - "version": "5.0.0", - "dev": true - }, - "pkg-dir": { - "version": "7.0.0", - "dev": true, - "requires": { - "find-up": "^6.3.0" - } - }, - "schema-utils": { - "version": "4.2.0", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - } - }, - "yocto-queue": { - "version": "1.0.0", - "dev": true - } - } - }, - "babel-plugin-add-react-displayname": { - "version": "0.0.5", - "dev": true - }, - "babel-plugin-istanbul": { - "version": "6.1.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - } - }, - "babel-plugin-jest-hoist": { - "version": "29.6.3", - "dev": true, - "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-plugin-polyfill-corejs2": { - "version": "0.4.11", - "dev": true, - "requires": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.6.2", - "semver": "^6.3.1" - } - }, - "babel-plugin-polyfill-corejs3": { - "version": "0.10.4", - "dev": true, - "requires": { - "@babel/helper-define-polyfill-provider": "^0.6.1", - "core-js-compat": "^3.36.1" - } - }, - "babel-plugin-polyfill-regenerator": { - "version": "0.6.2", - "dev": true, - "requires": { - "@babel/helper-define-polyfill-provider": "^0.6.2" - } - }, - "babel-plugin-styled-components": { - "version": "2.1.4", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", - "@babel/plugin-syntax-jsx": "^7.22.5", - "lodash": "^4.17.21", - "picomatch": "^2.3.1" - } - }, - "babel-preset-current-node-syntax": { - "version": "1.0.1", - "dev": true, - "requires": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - } - }, - "babel-preset-jest": { - "version": "29.6.3", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - } - }, - "balanced-match": { - "version": "1.0.2" - }, - "bare-events": { - "version": "2.2.2", - "optional": true - }, - "base64-js": { - "version": "1.5.1" - }, - "basic-ftp": { - "version": "5.0.5" - }, - "batch": { - "version": "0.6.1", - "dev": true - }, - "big.js": { - "version": "5.2.2", - "dev": true - }, - "binary-extensions": { - "version": "2.3.0", - "dev": true - }, - "bl": { - "version": "4.1.0", - "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "bluebird": { - "version": "3.7.2", - "dev": true - }, - "body-parser": { - "version": "1.20.2", - "dev": true, - "requires": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "dev": true - }, - "qs": { - "version": "6.11.0", - "dev": true, - "requires": { - "side-channel": "^1.0.4" - } - } - } - }, - "bonjour-service": { - "version": "1.2.1", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.3", - "multicast-dns": "^7.2.5" - } - }, - "boolbase": { - "version": "1.0.0", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "requires": { - "fill-range": "^7.1.1" - } - }, - "browser-assert": { - "version": "1.2.1", - "dev": true - }, - "browserslist": { - "version": "4.23.0", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" - } - }, - "bser": { - "version": "2.1.1", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, - "buffer": { - "version": "5.7.1", - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "buffer-crc32": { - "version": "0.2.13" - }, - "buffer-from": { - "version": "1.1.2", - "dev": true - }, - "builtin-modules": { - "version": "3.3.0", - "dev": true - }, - "bytes": { - "version": "3.1.2", - "dev": true - }, - "cacheable-lookup": { - "version": "5.0.4" - }, - "cacheable-request": { - "version": "7.0.4", - "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" - }, - "dependencies": { - "get-stream": { - "version": "5.2.0", - "requires": { - "pump": "^3.0.0" - } - } - } - }, - "call-bind": { - "version": "1.0.7", - "dev": true, - "requires": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - } - }, - "callsites": { - "version": "3.1.0" - }, - "camel-case": { - "version": "4.1.2", - "dev": true, - "requires": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, - "camelcase": { - "version": "6.3.0", - "dev": true - }, - "camelcase-css": { - "version": "2.0.1", - "dev": true - }, - "camelize": { - "version": "1.0.1", - "dev": true, - "peer": true - }, - "caniuse-lite": { - "version": "1.0.30001614", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001616.tgz", - "integrity": "sha512-RHVYKov7IcdNjVHJFNY/78RdG4oGVjbayxv8u5IO74Wv7Hlq4PnJE6mo/OjFijjVFNy5ijnCt6H3IIo4t+wfEw==", - "dev": true - }, - "case-sensitive-paths-webpack-plugin": { - "version": "2.4.0", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "char-regex": { - "version": "1.0.2", - "dev": true - }, - "chokidar": { - "version": "3.6.0", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, - "chrome-trace-event": { - "version": "1.0.3", - "dev": true - }, - "chromium-bidi": { - "version": "0.4.20", - "requires": { - "mitt": "3.0.1" - } - }, - "ci-info": { - "version": "3.9.0", - "dev": true - }, - "cjs-module-lexer": { - "version": "1.3.1", - "dev": true - }, - "classnames": { - "version": "2.5.1" - }, - "clean-css": { - "version": "5.3.3", - "dev": true, - "requires": { - "source-map": "~0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "dev": true - } - } - }, - "cli-color": { - "version": "2.0.4", - "requires": { - "d": "^1.0.1", - "es5-ext": "^0.10.64", - "es6-iterator": "^2.0.3", - "memoizee": "^0.4.15", - "timers-ext": "^0.1.7" - } - }, - "cli-cursor": { - "version": "4.0.0", - "requires": { - "restore-cursor": "^4.0.0" - } - }, - "cli-spinners": { - "version": "2.9.2" - }, - "cli-table": { - "version": "0.3.11", - "requires": { - "colors": "1.0.3" - } - }, - "cli-truncate": { - "version": "3.1.0", - "dev": true, - "requires": { - "slice-ansi": "^5.0.0", - "string-width": "^5.0.0" - } - }, - "cliui": { - "version": "8.0.1", - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "requires": { - "color-convert": "^2.0.1" - } - }, - "emoji-regex": { - "version": "8.0.0" - }, - "is-fullwidth-code-point": { - "version": "3.0.0" - }, - "string-width": { - "version": "4.2.3", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "wrap-ansi": { - "version": "7.0.0", - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - } - } - }, - "clone-deep": { - "version": "4.0.1", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "dependencies": { - "is-plain-object": { - "version": "2.0.4", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - } - } - }, - "clone-response": { - "version": "1.0.3", - "requires": { - "mimic-response": "^1.0.0" - } - }, - "co": { - "version": "4.6.0", - "dev": true - }, - "collect-v8-coverage": { - "version": "1.0.2", - "dev": true - }, - "color-convert": { - "version": "2.0.1", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4" - }, - "colorette": { - "version": "2.0.20", - "dev": true - }, - "colors": { - "version": "1.0.3" - }, - "combined-stream": { - "version": "1.0.8", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "8.3.0", - "dev": true - }, - "comment-parser": { - "version": "1.4.1", - "dev": true - }, - "common-path-prefix": { - "version": "3.0.0", - "dev": true - }, - "commondir": { - "version": "1.0.1", - "dev": true - }, - "compressible": { - "version": "2.0.18", - "dev": true, - "requires": { - "mime-db": ">= 1.43.0 < 2" - } - }, - "compression": { - "version": "1.7.4", - "dev": true, - "requires": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "dependencies": { - "bytes": { - "version": "3.0.0", - "dev": true - }, - "debug": { - "version": "2.6.9", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "dev": true - }, - "safe-buffer": { - "version": "5.1.2", - "dev": true - } - } - }, - "concat-map": { - "version": "0.0.1", - "dev": true - }, - "connect-history-api-fallback": { - "version": "2.0.0", - "dev": true - }, - "constants-browserify": { - "version": "1.0.0", - "dev": true - }, - "content-disposition": { - "version": "0.5.4", - "dev": true, - "requires": { - "safe-buffer": "5.2.1" - } - }, - "content-type": { - "version": "1.0.5", - "dev": true - }, - "convert-source-map": { - "version": "2.0.0", - "dev": true - }, - "cookie": { - "version": "0.6.0", - "dev": true - }, - "cookie-signature": { - "version": "1.0.6", - "dev": true - }, - "copy-anything": { - "version": "2.0.6", - "dev": true, - "peer": true, - "requires": { - "is-what": "^3.14.1" - } - }, - "copy-to-clipboard": { - "version": "3.3.3", - "requires": { - "toggle-selection": "^1.0.6" - } - }, - "copy-webpack-plugin": { - "version": "11.0.0", - "dev": true, - "requires": { - "fast-glob": "^3.2.11", - "glob-parent": "^6.0.1", - "globby": "^13.1.1", - "normalize-path": "^3.0.0", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0" - }, - "dependencies": { - "ajv": { - "version": "8.13.0", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.3", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" - } - }, - "ajv-keywords": { - "version": "5.1.0", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.3" - } - }, - "globby": { - "version": "13.2.2", - "dev": true, - "requires": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.3.0", - "ignore": "^5.2.4", - "merge2": "^1.4.1", - "slash": "^4.0.0" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "dev": true - }, - "schema-utils": { - "version": "4.2.0", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - } - }, - "slash": { - "version": "4.0.0", - "dev": true - } - } - }, - "core-js-compat": { - "version": "3.37.0", - "dev": true, - "requires": { - "browserslist": "^4.23.0" - } - }, - "core-js-pure": { - "version": "3.37.0", - "dev": true - }, - "core-util-is": { - "version": "1.0.3" - }, - "cosmiconfig": { - "version": "8.3.6", - "dev": true, - "requires": { - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0", - "path-type": "^4.0.0" - }, - "dependencies": { - "argparse": { - "version": "2.0.1", - "dev": true - }, - "js-yaml": { - "version": "4.1.0", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - } - } - }, - "create-jest": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "cross-env": { - "version": "7.0.3", - "dev": true, - "requires": { - "cross-spawn": "^7.0.1" - } - }, - "cross-fetch": { - "version": "4.0.0", - "requires": { - "node-fetch": "^2.6.12" - } - }, - "cross-spawn": { - "version": "7.0.3", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "css-color-keywords": { - "version": "1.0.0", - "dev": true, - "peer": true - }, - "css-loader": { - "version": "6.11.0", - "dev": true, - "requires": { - "icss-utils": "^5.1.0", - "postcss": "^8.4.33", - "postcss-modules-extract-imports": "^3.1.0", - "postcss-modules-local-by-default": "^4.0.5", - "postcss-modules-scope": "^3.2.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.2.0", - "semver": "^7.5.4" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.6.0", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "dev": true - } - } - }, - "css-select": { - "version": "4.3.0", - "dev": true, - "requires": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - } - }, - "css-to-react-native": { - "version": "3.2.0", - "dev": true, - "peer": true, - "requires": { - "camelize": "^1.0.0", - "css-color-keywords": "^1.0.0", - "postcss-value-parser": "^4.0.2" - } - }, - "css-tree": { - "version": "2.3.1", - "dev": true, - "requires": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" - } - }, - "css-what": { - "version": "6.1.0", - "dev": true - }, - "css.escape": { - "version": "1.5.1", - "dev": true - }, - "cssesc": { - "version": "3.0.0", - "dev": true - }, - "csso": { - "version": "5.0.5", - "dev": true, - "requires": { - "css-tree": "~2.2.0" - }, - "dependencies": { - "css-tree": { - "version": "2.2.1", - "dev": true, - "requires": { - "mdn-data": "2.0.28", - "source-map-js": "^1.0.1" - } - }, - "mdn-data": { - "version": "2.0.28", - "dev": true - } - } - }, - "cssom": { - "version": "0.5.0", - "dev": true - }, - "cssstyle": { - "version": "2.3.0", - "dev": true, - "requires": { - "cssom": "~0.3.6" - }, - "dependencies": { - "cssom": { - "version": "0.3.8", - "dev": true - } - } - }, - "csstype": { - "version": "3.1.3", - "dev": true - }, - "csvtojson": { - "version": "2.0.10", - "dev": true, - "requires": { - "bluebird": "^3.5.1", - "lodash": "^4.17.3", - "strip-bom": "^2.0.0" - } - }, - "d": { - "version": "1.0.2", - "requires": { - "es5-ext": "^0.10.64", - "type": "^2.7.2" - } - }, - "d3-array": { - "version": "3.2.4", - "requires": { - "internmap": "1 - 2" - } - }, - "d3-color": { - "version": "3.1.0" - }, - "d3-ease": { - "version": "3.0.1" - }, - "d3-format": { - "version": "3.1.0" - }, - "d3-interpolate": { - "version": "3.0.1", - "requires": { - "d3-color": "1 - 3" - } - }, - "d3-path": { - "version": "3.1.0" - }, - "d3-scale": { - "version": "4.0.2", - "requires": { - "d3-array": "2.10.0 - 3", - "d3-format": "1 - 3", - "d3-interpolate": "1.2.0 - 3", - "d3-time": "2.1.1 - 3", - "d3-time-format": "2 - 4" - } - }, - "d3-shape": { - "version": "3.2.0", - "requires": { - "d3-path": "^3.1.0" - } - }, - "d3-time": { - "version": "3.1.0", - "requires": { - "d3-array": "2 - 3" - } - }, - "d3-time-format": { - "version": "4.1.0", - "requires": { - "d3-time": "1 - 3" - } - }, - "d3-timer": { - "version": "3.0.1" - }, - "d3-voronoi": { - "version": "1.1.4" - }, - "damerau-levenshtein": { - "version": "1.0.8", - "dev": true - }, - "data-uri-to-buffer": { - "version": "6.0.2" - }, - "data-urls": { - "version": "3.0.2", - "dev": true, - "requires": { - "abab": "^2.0.6", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^11.0.0" - } - }, - "data-view-buffer": { - "version": "1.0.1", - "dev": true, - "requires": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - } - }, - "data-view-byte-length": { - "version": "1.0.1", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - } - }, - "data-view-byte-offset": { - "version": "1.0.0", - "dev": true, - "requires": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - } - }, - "debug": { - "version": "4.3.4", - "requires": { - "ms": "2.1.2" - } - }, - "decimal.js": { - "version": "10.4.3", - "dev": true - }, - "decompress-response": { - "version": "6.0.0", - "requires": { - "mimic-response": "^3.1.0" - }, - "dependencies": { - "mimic-response": { - "version": "3.1.0" - } - } - }, - "dedent": { - "version": "0.7.0", - "dev": true - }, - "deep-equal": { - "version": "2.2.3", - "dev": true, - "requires": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.5", - "es-get-iterator": "^1.1.3", - "get-intrinsic": "^1.2.2", - "is-arguments": "^1.1.1", - "is-array-buffer": "^3.0.2", - "is-date-object": "^1.0.5", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "isarray": "^2.0.5", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "side-channel": "^1.0.4", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.13" - } - }, - "deep-is": { - "version": "0.1.4", - "dev": true - }, - "deepmerge": { - "version": "4.3.1", - "dev": true - }, - "default-gateway": { - "version": "6.0.3", - "dev": true, - "requires": { - "execa": "^5.0.0" - } - }, - "defer-to-connect": { - "version": "2.0.1" - }, - "define-data-property": { - "version": "1.1.4", - "dev": true, - "requires": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - } - }, - "define-lazy-prop": { - "version": "2.0.0", - "dev": true - }, - "define-properties": { - "version": "1.2.1", - "dev": true, - "requires": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - } - }, - "degenerator": { - "version": "5.0.1", - "requires": { - "ast-types": "^0.13.4", - "escodegen": "^2.1.0", - "esprima": "^4.0.1" - }, - "dependencies": { - "ast-types": { - "version": "0.13.4", - "requires": { - "tslib": "^2.0.1" - } - } - } - }, - "delaunator": { - "version": "4.0.1" - }, - "delaunay-find": { - "version": "0.0.6", - "requires": { - "delaunator": "^4.0.0" - } - }, - "delayed-stream": { - "version": "1.0.0", - "dev": true - }, - "depd": { - "version": "2.0.0", - "dev": true - }, - "dequal": { - "version": "2.0.3", - "dev": true - }, - "destroy": { - "version": "1.2.0", - "dev": true - }, - "detect-newline": { - "version": "3.1.0", - "dev": true - }, - "detect-node": { - "version": "2.1.0", - "dev": true - }, - "detect-node-es": { - "version": "1.1.0", - "dev": true - }, - "devtools-protocol": { - "version": "0.0.1147663" - }, - "didyoumean": { - "version": "1.2.2", - "dev": true - }, - "diff": { - "version": "3.5.0", - "dev": true - }, - "diff-sequences": { - "version": "29.6.3", - "dev": true - }, - "dir-glob": { - "version": "3.0.1", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "dlv": { - "version": "1.1.3", - "dev": true - }, - "dns-packet": { - "version": "5.6.1", - "dev": true, - "requires": { - "@leichtgewicht/ip-codec": "^2.0.1" - } - }, - "doctrine": { - "version": "3.0.0", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "dom-accessibility-api": { - "version": "0.5.16", - "dev": true - }, - "dom-converter": { - "version": "0.2.0", - "dev": true, - "requires": { - "utila": "~0.4" - } - }, - "dom-serializer": { - "version": "1.4.1", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "dependencies": { - "entities": { - "version": "2.2.0", - "dev": true - } - } - }, - "domelementtype": { - "version": "2.3.0", - "dev": true - }, - "domexception": { - "version": "4.0.0", - "dev": true, - "requires": { - "webidl-conversions": "^7.0.0" - } - }, - "domhandler": { - "version": "4.3.1", - "dev": true, - "requires": { - "domelementtype": "^2.2.0" - } - }, - "domutils": { - "version": "2.8.0", - "dev": true, - "requires": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - } - }, - "dot-case": { - "version": "3.0.4", - "dev": true, - "requires": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "dotenv": { - "version": "16.4.5", - "dev": true - }, - "dotenv-expand": { - "version": "10.0.0", - "dev": true - }, - "duplexer": { - "version": "0.1.2", - "dev": true - }, - "eastasianwidth": { - "version": "0.2.0" - }, - "ee-first": { - "version": "1.1.1", - "dev": true - }, - "electron-to-chromium": { - "version": "1.4.751", - "dev": true - }, - "emittery": { - "version": "0.13.1", - "dev": true - }, - "emoji-regex": { - "version": "9.2.2", - "dev": true - }, - "emojis-list": { - "version": "3.0.0", - "dev": true - }, - "encodeurl": { - "version": "1.0.2", - "dev": true - }, - "end-of-stream": { - "version": "1.4.4", - "requires": { - "once": "^1.4.0" - } - }, - "endent": { - "version": "2.1.0", - "dev": true, - "requires": { - "dedent": "^0.7.0", - "fast-json-parse": "^1.0.3", - "objectorarray": "^1.0.5" - } - }, - "enhanced-resolve": { - "version": "5.16.0", - "dev": true, - "requires": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - } - }, - "entities": { - "version": "4.5.0", - "dev": true - }, - "envinfo": { - "version": "7.13.0", - "dev": true - }, - "errno": { - "version": "0.1.8", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "prr": "~1.0.1" - } - }, - "error-ex": { - "version": "1.3.2", - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "error-stack-parser": { - "version": "2.1.4", - "dev": true, - "requires": { - "stackframe": "^1.3.4" - } - }, - "es-abstract": { - "version": "1.23.3", - "dev": true, - "requires": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "data-view-buffer": "^1.0.1", - "data-view-byte-length": "^1.0.1", - "data-view-byte-offset": "^1.0.0", - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.0.3", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", - "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "hasown": "^2.0.2", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.1", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.3", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.13", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", - "object-keys": "^1.1.1", - "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.2", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.9", - "string.prototype.trimend": "^1.0.8", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.6", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.15" - } - }, - "es-define-property": { - "version": "1.0.0", - "dev": true, - "requires": { - "get-intrinsic": "^1.2.4" - } - }, - "es-errors": { - "version": "1.3.0", - "dev": true - }, - "es-get-iterator": { - "version": "1.1.3", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "is-arguments": "^1.1.1", - "is-map": "^2.0.2", - "is-set": "^2.0.2", - "is-string": "^1.0.7", - "isarray": "^2.0.5", - "stop-iteration-iterator": "^1.0.0" - } - }, - "es-iterator-helpers": { - "version": "1.0.19", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", - "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.3", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.3", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "iterator.prototype": "^1.1.2", - "safe-array-concat": "^1.1.2" - } - }, - "es-module-lexer": { - "version": "1.5.2", - "dev": true - }, - "es-object-atoms": { - "version": "1.0.0", - "dev": true, - "requires": { - "es-errors": "^1.3.0" - } - }, - "es-set-tostringtag": { - "version": "2.0.3", - "dev": true, - "requires": { - "get-intrinsic": "^1.2.4", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" - } - }, - "es-shim-unscopables": { - "version": "1.0.2", - "dev": true, - "requires": { - "hasown": "^2.0.0" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "es5-ext": { - "version": "0.10.64", - "requires": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "esniff": "^2.0.1", - "next-tick": "^1.1.0" - } - }, - "es6-iterator": { - "version": "2.0.3", - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "es6-symbol": { - "version": "3.1.4", - "requires": { - "d": "^1.0.2", - "ext": "^1.7.0" - } - }, - "es6-weak-map": { - "version": "2.0.3", - "requires": { - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.1" - } - }, - "esbuild": { - "version": "0.18.20", - "dev": true, - "requires": { - "@esbuild/android-arm": "0.18.20", - "@esbuild/android-arm64": "0.18.20", - "@esbuild/android-x64": "0.18.20", - "@esbuild/darwin-arm64": "0.18.20", - "@esbuild/darwin-x64": "0.18.20", - "@esbuild/freebsd-arm64": "0.18.20", - "@esbuild/freebsd-x64": "0.18.20", - "@esbuild/linux-arm": "0.18.20", - "@esbuild/linux-arm64": "0.18.20", - "@esbuild/linux-ia32": "0.18.20", - "@esbuild/linux-loong64": "0.18.20", - "@esbuild/linux-mips64el": "0.18.20", - "@esbuild/linux-ppc64": "0.18.20", - "@esbuild/linux-riscv64": "0.18.20", - "@esbuild/linux-s390x": "0.18.20", - "@esbuild/linux-x64": "0.18.20", - "@esbuild/netbsd-x64": "0.18.20", - "@esbuild/openbsd-x64": "0.18.20", - "@esbuild/sunos-x64": "0.18.20", - "@esbuild/win32-arm64": "0.18.20", - "@esbuild/win32-ia32": "0.18.20", - "@esbuild/win32-x64": "0.18.20" - } - }, - "esbuild-register": { - "version": "3.5.0", - "dev": true, - "requires": { - "debug": "^4.3.4" - } - }, - "escalade": { - "version": "3.1.2" - }, - "escape-html": { - "version": "1.0.3", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5" - }, - "escodegen": { - "version": "2.1.0", - "requires": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "source-map": "~0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "optional": true - } - } - }, - "eslint": { - "version": "8.57.0", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "argparse": { - "version": "2.0.1", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "escape-string-regexp": { - "version": "4.0.0", - "dev": true - }, - "eslint-scope": { - "version": "7.2.2", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "globals": { - "version": "13.24.0", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "js-yaml": { - "version": "4.1.0", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "type-fest": { - "version": "0.20.2", - "dev": true - } - } - }, - "eslint-config-prettier": { - "version": "8.10.0", - "dev": true, - "requires": {} - }, - "eslint-import-resolver-node": { - "version": "0.3.9", - "requires": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "eslint-import-resolver-typescript": { - "version": "3.6.1", - "dev": true, - "requires": { - "debug": "^4.3.4", - "enhanced-resolve": "^5.12.0", - "eslint-module-utils": "^2.7.4", - "fast-glob": "^3.3.1", - "get-tsconfig": "^4.5.0", - "is-core-module": "^2.11.0", - "is-glob": "^4.0.3" - } - }, - "eslint-module-utils": { - "version": "2.8.1", - "dev": true, - "requires": { - "debug": "^3.2.7" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "eslint-plugin-eslint-comments": { - "version": "3.2.0", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5", - "ignore": "^5.0.5" - } - }, - "eslint-plugin-header": { - "version": "3.1.1", - "dev": true, - "requires": {} - }, - "eslint-plugin-import": { - "version": "2.29.1", - "dev": true, - "requires": { - "array-includes": "^3.1.7", - "array.prototype.findlastindex": "^1.2.3", - "array.prototype.flat": "^1.3.2", - "array.prototype.flatmap": "^1.3.2", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.8.0", - "hasown": "^2.0.0", - "is-core-module": "^2.13.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.7", - "object.groupby": "^1.0.1", - "object.values": "^1.1.7", - "semver": "^6.3.1", - "tsconfig-paths": "^3.15.0" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "doctrine": { - "version": "2.1.0", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - } - } - }, - "eslint-plugin-jest": { - "version": "27.9.0", - "dev": true, - "requires": { - "@typescript-eslint/utils": "^5.10.0" - } - }, - "eslint-plugin-jsdoc": { - "version": "46.10.1", - "dev": true, - "requires": { - "@es-joy/jsdoccomment": "~0.41.0", - "are-docs-informative": "^0.0.2", - "comment-parser": "1.4.1", - "debug": "^4.3.4", - "escape-string-regexp": "^4.0.0", - "esquery": "^1.5.0", - "is-builtin-module": "^3.2.1", - "semver": "^7.5.4", - "spdx-expression-parse": "^4.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "4.0.0", - "dev": true - }, - "lru-cache": { - "version": "6.0.0", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.6.0", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "dev": true - } - } - }, - "eslint-plugin-jsx-a11y": { - "version": "6.8.0", - "dev": true, - "requires": { - "@babel/runtime": "^7.23.2", - "aria-query": "^5.3.0", - "array-includes": "^3.1.7", - "array.prototype.flatmap": "^1.3.2", - "ast-types-flow": "^0.0.8", - "axe-core": "=4.7.0", - "axobject-query": "^3.2.1", - "damerau-levenshtein": "^1.0.8", - "emoji-regex": "^9.2.2", - "es-iterator-helpers": "^1.0.15", - "hasown": "^2.0.0", - "jsx-ast-utils": "^3.3.5", - "language-tags": "^1.0.9", - "minimatch": "^3.1.2", - "object.entries": "^1.1.7", - "object.fromentries": "^2.0.7" - }, - "dependencies": { - "aria-query": { - "version": "5.3.0", - "dev": true, - "requires": { - "dequal": "^2.0.3" - } - } - } - }, - "eslint-plugin-prettier": { - "version": "4.2.1", - "dev": true, - "requires": { - "prettier-linter-helpers": "^1.0.0" - } - }, - "eslint-plugin-react": { - "version": "7.34.1", - "dev": true, - "requires": { - "array-includes": "^3.1.7", - "array.prototype.findlast": "^1.2.4", - "array.prototype.flatmap": "^1.3.2", - "array.prototype.toreversed": "^1.1.2", - "array.prototype.tosorted": "^1.1.3", - "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.0.17", - "estraverse": "^5.3.0", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.7", - "object.fromentries": "^2.0.7", - "object.hasown": "^1.1.3", - "object.values": "^1.1.7", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.5", - "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.10" - }, - "dependencies": { - "doctrine": { - "version": "2.1.0", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "resolve": { - "version": "2.0.0-next.5", - "dev": true, - "requires": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - } - } - }, - "eslint-plugin-react-hooks": { - "version": "4.6.2", - "dev": true, - "requires": {} - }, - "eslint-plugin-storybook": { - "version": "0.6.15", - "dev": true, - "requires": { - "@storybook/csf": "^0.0.1", - "@typescript-eslint/utils": "^5.45.0", - "requireindex": "^1.1.0", - "ts-dedent": "^2.2.0" - }, - "dependencies": { - "@storybook/csf": { - "version": "0.0.1", - "dev": true, - "requires": { - "lodash": "^4.17.15" - } - } - } - }, - "eslint-scope": { - "version": "5.1.1", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "dependencies": { - "estraverse": { - "version": "4.3.0", - "dev": true - } - } - }, - "eslint-visitor-keys": { - "version": "3.4.3", - "dev": true - }, - "esniff": { - "version": "2.0.1", - "requires": { - "d": "^1.0.1", - "es5-ext": "^0.10.62", - "event-emitter": "^0.3.5", - "type": "^2.7.2" - } - }, - "espree": { - "version": "9.6.1", - "dev": true, - "requires": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "dependencies": { - "acorn": { - "version": "8.11.3", - "dev": true - } - } - }, - "esprima": { - "version": "4.0.1" - }, - "esquery": { - "version": "1.5.0", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - } - }, - "esrecurse": { - "version": "4.3.0", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0" - }, - "esutils": { - "version": "2.0.3" - }, - "etag": { - "version": "1.8.1", - "dev": true - }, - "event-emitter": { - "version": "0.3.5", - "requires": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, - "event-stream": { - "version": "3.3.4", - "dev": true, - "requires": { - "duplexer": "~0.1.1", - "from": "~0", - "map-stream": "~0.1.0", - "pause-stream": "0.0.11", - "split": "0.3", - "stream-combiner": "~0.0.4", - "through": "~2.3.1" - } - }, - "eventemitter3": { - "version": "5.0.1" - }, - "events": { - "version": "3.3.0", - "dev": true - }, - "execa": { - "version": "5.1.1", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "dependencies": { - "signal-exit": { - "version": "3.0.7", - "dev": true - } - } - }, - "exit": { - "version": "0.1.2", - "dev": true - }, - "expect": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - } - }, - "express": { - "version": "4.19.2", - "dev": true, - "requires": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.2", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.6.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "dev": true - }, - "qs": { - "version": "6.11.0", - "dev": true, - "requires": { - "side-channel": "^1.0.4" - } - } - } - }, - "ext": { - "version": "1.7.0", - "requires": { - "type": "^2.7.2" - } - }, - "extend": { - "version": "3.0.2", - "dev": true - }, - "fast-deep-equal": { - "version": "3.1.3", - "dev": true - }, - "fast-diff": { - "version": "1.3.0", - "dev": true - }, - "fast-fifo": { - "version": "1.3.2" - }, - "fast-glob": { - "version": "3.3.2", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, - "fast-json-parse": { - "version": "1.0.3", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "dev": true - }, - "fast-xml-parser": { - "version": "4.3.6", - "requires": { - "strnum": "^1.0.5" - } - }, - "fastest-levenshtein": { - "version": "1.0.16", - "dev": true - }, - "fastq": { - "version": "1.17.1", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "faye-websocket": { - "version": "0.11.4", - "dev": true, - "requires": { - "websocket-driver": ">=0.5.1" - } - }, - "fb-watchman": { - "version": "2.0.2", - "dev": true, - "requires": { - "bser": "2.1.1" - } - }, - "fd-slicer": { - "version": "1.1.0", - "requires": { - "pend": "~1.2.0" - } - }, - "file-entry-cache": { - "version": "6.0.1", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "file-saver": { - "version": "2.0.5" - }, - "file-system-cache": { - "version": "2.3.0", - "dev": true, - "requires": { - "fs-extra": "11.1.1", - "ramda": "0.29.0" - }, - "dependencies": { - "fs-extra": { - "version": "11.1.1", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - } - } - }, - "fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "finalhandler": { - "version": "1.2.0", - "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "dev": true - } - } - }, - "find-cache-dir": { - "version": "3.3.2", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "pkg-dir": { - "version": "4.2.0", - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - } - } - }, - "find-up": { - "version": "5.0.0", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "flat": { - "version": "5.0.2", - "dev": true - }, - "flat-cache": { - "version": "3.2.0", - "dev": true, - "requires": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "dependencies": { - "glob": { - "version": "7.2.3", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "rimraf": { - "version": "3.0.2", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "flatted": { - "version": "3.3.1", - "dev": true - }, - "follow-redirects": { - "version": "1.15.6", - "dev": true - }, - "for-each": { - "version": "0.3.3", - "dev": true, - "requires": { - "is-callable": "^1.1.3" - } - }, - "foreground-child": { - "version": "3.1.1", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - } - }, - "fork-ts-checker-webpack-plugin": { - "version": "8.0.0", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.7", - "chalk": "^4.1.2", - "chokidar": "^3.5.3", - "cosmiconfig": "^7.0.1", - "deepmerge": "^4.2.2", - "fs-extra": "^10.0.0", - "memfs": "^3.4.1", - "minimatch": "^3.0.4", - "node-abort-controller": "^3.0.1", - "schema-utils": "^3.1.1", - "semver": "^7.3.5", - "tapable": "^2.2.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "cosmiconfig": { - "version": "7.1.0", - "dev": true, - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - } - }, - "fs-extra": { - "version": "10.1.0", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "lru-cache": { - "version": "6.0.0", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.6.0", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "dev": true - }, - "yaml": { - "version": "1.10.2", - "dev": true - } - } - }, - "form-data": { - "version": "4.0.0", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "forwarded": { - "version": "0.2.0", - "dev": true - }, - "fraction.js": { - "version": "4.3.7", - "dev": true - }, - "fresh": { - "version": "0.5.2", - "dev": true - }, - "from": { - "version": "0.1.7", - "dev": true - }, - "fs-constants": { - "version": "1.0.0" - }, - "fs-extra": { - "version": "11.2.0", - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "fs-monkey": { - "version": "1.0.5", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0" - }, - "fsevents": { - "version": "2.3.3", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.2" - }, - "function.prototype.name": { - "version": "1.1.6", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" - } - }, - "functions-have-names": { - "version": "1.2.3", - "dev": true - }, - "gensync": { - "version": "1.0.0-beta.2", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5" - }, - "get-func-name": { - "version": "2.0.2", - "dev": true - }, - "get-intrinsic": { - "version": "1.2.4", - "dev": true, - "requires": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - } - }, - "get-nonce": { - "version": "1.0.1", - "dev": true - }, - "get-package-type": { - "version": "0.1.0", - "dev": true - }, - "get-stream": { - "version": "6.0.1", - "dev": true - }, - "get-symbol-description": { - "version": "1.0.2", - "dev": true, - "requires": { - "call-bind": "^1.0.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" - } - }, - "get-tsconfig": { - "version": "4.7.3", - "dev": true, - "requires": { - "resolve-pkg-maps": "^1.0.0" - } - }, - "get-uri": { - "version": "6.0.3", - "requires": { - "basic-ftp": "^5.0.2", - "data-uri-to-buffer": "^6.0.2", - "debug": "^4.3.4", - "fs-extra": "^11.2.0" - } - }, - "github-slugger": { - "version": "1.5.0", - "dev": true - }, - "glob": { - "version": "10.3.12", - "dev": true, - "requires": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.6", - "minimatch": "^9.0.1", - "minipass": "^7.0.4", - "path-scurry": "^1.10.2" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "minimatch": { - "version": "9.0.4", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - } - } - }, - "glob-parent": { - "version": "6.0.2", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "glob-to-regexp": { - "version": "0.4.1", - "dev": true - }, - "globals": { - "version": "11.12.0", - "dev": true - }, - "globalthis": { - "version": "1.0.4", - "dev": true, - "requires": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" - } - }, - "globby": { - "version": "11.1.0", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "gopd": { - "version": "1.0.1", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.3" - } - }, - "got": { - "version": "11.8.6", - "requires": { - "@sindresorhus/is": "^4.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.2", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" - } - }, - "graceful-fs": { - "version": "4.2.11" - }, - "graphemer": { - "version": "1.4.0", - "dev": true - }, - "handle-thing": { - "version": "2.0.1", - "dev": true - }, - "handlebars": { - "version": "4.7.8", - "dev": true, - "requires": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4", - "wordwrap": "^1.0.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "dev": true - } - } - }, - "has-bigints": { - "version": "1.0.2", - "dev": true - }, - "has-flag": { - "version": "3.0.0" - }, - "has-property-descriptors": { - "version": "1.0.2", - "dev": true, - "requires": { - "es-define-property": "^1.0.0" - } - }, - "has-proto": { - "version": "1.0.3", - "dev": true - }, - "has-symbols": { - "version": "1.0.3", - "dev": true - }, - "has-tostringtag": { - "version": "1.0.2", - "dev": true, - "requires": { - "has-symbols": "^1.0.3" - } - }, - "hasown": { - "version": "2.0.2", - "requires": { - "function-bind": "^1.1.2" - } - }, - "he": { - "version": "1.2.0", - "dev": true - }, - "hosted-git-info": { - "version": "2.8.9", - "dev": true - }, - "hpack.js": { - "version": "2.1.6", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "dev": true - }, - "readable-stream": { - "version": "2.3.8", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "html-encoding-sniffer": { - "version": "3.0.0", - "dev": true, - "requires": { - "whatwg-encoding": "^2.0.0" - } - }, - "html-entities": { - "version": "2.5.2", - "dev": true - }, - "html-escaper": { - "version": "2.0.2", - "dev": true - }, - "html-inline-script-webpack-plugin": { - "version": "3.2.1", - "dev": true, - "requires": {} - }, - "html-minifier-terser": { - "version": "6.1.0", - "dev": true, - "requires": { - "camel-case": "^4.1.2", - "clean-css": "^5.2.2", - "commander": "^8.3.0", - "he": "^1.2.0", - "param-case": "^3.0.4", - "relateurl": "^0.2.7", - "terser": "^5.10.0" - } - }, - "html-tags": { - "version": "3.3.1", - "dev": true - }, - "html-webpack-plugin": { - "version": "5.6.0", - "dev": true, - "requires": { - "@types/html-minifier-terser": "^6.0.0", - "html-minifier-terser": "^6.0.2", - "lodash": "^4.17.21", - "pretty-error": "^4.0.0", - "tapable": "^2.0.0" - } - }, - "htmlparser2": { - "version": "6.1.0", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - }, - "dependencies": { - "entities": { - "version": "2.2.0", - "dev": true - } - } - }, - "http-cache-semantics": { - "version": "4.1.1" - }, - "http-deceiver": { - "version": "1.2.7", - "dev": true - }, - "http-errors": { - "version": "2.0.0", - "dev": true, - "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - } - }, - "http-parser-js": { - "version": "0.5.8", - "dev": true - }, - "http-proxy": { - "version": "1.18.1", - "dev": true, - "requires": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "dependencies": { - "eventemitter3": { - "version": "4.0.7", - "dev": true - } - } - }, - "http-proxy-agent": { - "version": "5.0.0", - "dev": true, - "requires": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - } - }, - "http-proxy-middleware": { - "version": "2.0.6", - "dev": true, - "requires": { - "@types/http-proxy": "^1.17.8", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" - } - }, - "http2-wrapper": { - "version": "1.0.3", - "requires": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" - } - }, - "https-proxy-agent": { - "version": "5.0.1", - "requires": { - "agent-base": "6", - "debug": "4" - } - }, - "human-signals": { - "version": "2.1.0", - "dev": true - }, - "husky": { - "version": "8.0.3", - "dev": true - }, - "iconv-lite": { - "version": "0.4.24", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "icss-utils": { - "version": "5.1.0", - "dev": true, - "requires": {} - }, - "ieee754": { - "version": "1.2.1" - }, - "ignore": { - "version": "5.3.1", - "dev": true - }, - "image-size": { - "version": "0.5.5", - "dev": true, - "optional": true, - "peer": true - }, - "immediate": { - "version": "3.0.6" - }, - "import-fresh": { - "version": "3.3.0", - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "4.0.0" - } - } - }, - "import-local": { - "version": "3.1.0", - "dev": true, - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "pkg-dir": { - "version": "4.2.0", - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - } - } - }, - "imurmurhash": { - "version": "0.1.4", - "dev": true - }, - "indent-string": { - "version": "4.0.0", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4" - }, - "internal-slot": { - "version": "1.0.7", - "dev": true, - "requires": { - "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" - } - }, - "internmap": { - "version": "2.0.3" - }, - "interpret": { - "version": "3.1.1", - "dev": true - }, - "intl-messageformat": { - "version": "10.5.11", - "requires": { - "@formatjs/ecma402-abstract": "1.18.2", - "@formatjs/fast-memoize": "2.2.0", - "@formatjs/icu-messageformat-parser": "2.7.6", - "tslib": "^2.4.0" - } - }, - "invariant": { - "version": "2.2.4", - "dev": true, - "requires": { - "loose-envify": "^1.0.0" - } - }, - "ip-address": { - "version": "9.0.5", - "requires": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, - "dependencies": { - "sprintf-js": { - "version": "1.1.3" - } - } - }, - "ipaddr.js": { - "version": "1.9.1", - "dev": true - }, - "is-absolute-url": { - "version": "3.0.3", - "dev": true - }, - "is-arguments": { - "version": "1.1.1", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-array-buffer": { - "version": "3.0.4", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" - } - }, - "is-arrayish": { - "version": "0.2.1" - }, - "is-async-function": { - "version": "2.0.0", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-bigint": { - "version": "1.0.4", - "dev": true, - "requires": { - "has-bigints": "^1.0.1" - } - }, - "is-binary-path": { - "version": "2.1.0", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-boolean-object": { - "version": "1.1.2", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-builtin-module": { - "version": "3.2.1", - "dev": true, - "requires": { - "builtin-modules": "^3.3.0" - } - }, - "is-callable": { - "version": "1.2.7", - "dev": true - }, - "is-core-module": { - "version": "2.13.1", - "requires": { - "hasown": "^2.0.0" - } - }, - "is-data-view": { - "version": "1.0.1", - "dev": true, - "requires": { - "is-typed-array": "^1.1.13" - } - }, - "is-date-object": { - "version": "1.0.5", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-docker": { - "version": "2.2.1", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "dev": true - }, - "is-finalizationregistry": { - "version": "1.0.2", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-fullwidth-code-point": { - "version": "4.0.0", - "dev": true - }, - "is-generator-fn": { - "version": "2.1.0", - "dev": true - }, - "is-generator-function": { - "version": "1.0.10", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-glob": { - "version": "4.0.3", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-map": { - "version": "2.0.3", - "dev": true - }, - "is-nan": { - "version": "1.3.2", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3" - } - }, - "is-negative-zero": { - "version": "2.0.3", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-number-object": { - "version": "1.0.7", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-path-inside": { - "version": "3.0.3", - "dev": true - }, - "is-plain-obj": { - "version": "3.0.0", - "dev": true - }, - "is-plain-object": { - "version": "5.0.0", - "dev": true - }, - "is-potential-custom-element-name": { - "version": "1.0.1", - "dev": true - }, - "is-promise": { - "version": "2.2.2" - }, - "is-regex": { - "version": "1.1.4", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-set": { - "version": "2.0.3", - "dev": true - }, - "is-shared-array-buffer": { - "version": "1.0.3", - "dev": true, - "requires": { - "call-bind": "^1.0.7" - } - }, - "is-stream": { - "version": "2.0.1", - "dev": true - }, - "is-string": { - "version": "1.0.7", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-symbol": { - "version": "1.0.4", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "is-typed-array": { - "version": "1.1.13", - "dev": true, - "requires": { - "which-typed-array": "^1.1.14" - } - }, - "is-utf8": { - "version": "0.2.1", - "dev": true - }, - "is-weakmap": { - "version": "2.0.2", - "dev": true - }, - "is-weakref": { - "version": "1.0.2", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-weakset": { - "version": "2.0.3", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4" - } - }, - "is-what": { - "version": "3.14.1", - "dev": true, - "peer": true - }, - "is-wsl": { - "version": "2.2.0", - "dev": true, - "requires": { - "is-docker": "^2.0.0" - } - }, - "isarray": { - "version": "2.0.5", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "3.2.2", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "5.2.1", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - } - }, - "istanbul-lib-report": { - "version": "3.0.1", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "lru-cache": { - "version": "6.0.0", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "make-dir": { - "version": "4.0.0", - "dev": true, - "requires": { - "semver": "^7.5.3" - } - }, - "semver": { - "version": "7.6.0", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "dev": true - } - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.1", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "dev": true - } - } - }, - "istanbul-reports": { - "version": "3.1.7", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "iterator.prototype": { - "version": "1.1.2", - "dev": true, - "requires": { - "define-properties": "^1.2.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "reflect.getprototypeof": "^1.0.4", - "set-function-name": "^2.0.1" - } - }, - "jackspeak": { - "version": "2.3.6", - "dev": true, - "requires": { - "@isaacs/cliui": "^8.0.2", - "@pkgjs/parseargs": "^0.11.0" - } - }, - "jest": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - } - }, - "jest-changed-files": { - "version": "29.7.0", - "dev": true, - "requires": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - } - }, - "jest-circus": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "dedent": { - "version": "1.5.3", - "dev": true, - "requires": {} - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "pretty-format": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "dev": true - } - } - }, - "react-is": { - "version": "18.3.1", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-cli": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-config": { - "version": "29.7.0", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "glob": { - "version": "7.2.3", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "pretty-format": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "dev": true - } - } - }, - "react-is": { - "version": "18.3.1", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-diff": { - "version": "29.7.0", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "pretty-format": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "dev": true - } - } - }, - "react-is": { - "version": "18.3.1", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-docblock": { - "version": "29.7.0", - "dev": true, - "requires": { - "detect-newline": "^3.0.0" - } - }, - "jest-each": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "pretty-format": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "dev": true - } - } - }, - "react-is": { - "version": "18.3.1", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-environment-jsdom": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/jsdom": "^20.0.0", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0", - "jsdom": "^20.0.0" - }, - "dependencies": { - "jest-mock": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - } - } - } - }, - "jest-environment-node": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "dependencies": { - "jest-mock": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - } - } - } - }, - "jest-extended": { - "version": "4.0.2", - "dev": true, - "requires": { - "jest-diff": "^29.0.0", - "jest-get-type": "^29.0.0" - } - }, - "jest-get-type": { - "version": "29.6.3", - "dev": true - }, - "jest-haste-map": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.3.2", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - } - }, - "jest-leak-detector": { - "version": "29.7.0", - "dev": true, - "requires": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "dev": true - }, - "pretty-format": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - } - }, - "react-is": { - "version": "18.3.1", - "dev": true - } - } - }, - "jest-matcher-utils": { - "version": "29.7.0", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "pretty-format": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "dev": true - } - } - }, - "react-is": { - "version": "18.3.1", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-message-util": { - "version": "29.7.0", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "pretty-format": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "dev": true - } - } - }, - "react-is": { - "version": "18.3.1", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-mock": { - "version": "27.5.1", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.9", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-pnp-resolver": { - "version": "1.2.3", - "dev": true, - "requires": {} - }, - "jest-regex-util": { - "version": "29.6.3", - "dev": true - }, - "jest-resolve": { - "version": "29.7.0", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-resolve-dependencies": { - "version": "29.7.0", - "dev": true, - "requires": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - } - }, - "jest-runner": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-runtime": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "glob": { - "version": "7.2.3", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "jest-mock": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - } - }, - "strip-bom": { - "version": "4.0.0", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-snapshot": { - "version": "29.7.0", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "lru-cache": { - "version": "6.0.0", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "pretty-format": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "dev": true - } - } - }, - "react-is": { - "version": "18.3.1", - "dev": true - }, - "semver": { - "version": "7.6.0", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "dev": true - } - } - }, - "jest-util": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-validate": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "pretty-format": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "dev": true - } - } - }, - "react-is": { - "version": "18.3.1", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-watcher": { - "version": "29.7.0", - "dev": true, - "requires": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-worker": { - "version": "29.7.0", - "dev": true, - "requires": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "supports-color": { - "version": "8.1.1", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jiti": { - "version": "1.21.0", - "dev": true - }, - "js-tokens": { - "version": "4.0.0" - }, - "js-yaml": { - "version": "3.14.1", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsbn": { - "version": "1.1.0" - }, - "jsdoc-type-pratt-parser": { - "version": "4.0.0", - "dev": true - }, - "jsdom": { - "version": "20.0.3", - "dev": true, - "requires": { - "abab": "^2.0.6", - "acorn": "^8.8.1", - "acorn-globals": "^7.0.0", - "cssom": "^0.5.0", - "cssstyle": "^2.3.0", - "data-urls": "^3.0.2", - "decimal.js": "^10.4.2", - "domexception": "^4.0.0", - "escodegen": "^2.0.0", - "form-data": "^4.0.0", - "html-encoding-sniffer": "^3.0.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.1", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.2", - "parse5": "^7.1.1", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.1.2", - "w3c-xmlserializer": "^4.0.0", - "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^2.0.0", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^11.0.0", - "ws": "^8.11.0", - "xml-name-validator": "^4.0.0" - }, - "dependencies": { - "acorn": { - "version": "8.11.3", - "dev": true - } - } - }, - "jsesc": { - "version": "2.5.2", - "dev": true - }, - "json-buffer": { - "version": "3.0.1" - }, - "json-parse-better-errors": { - "version": "1.0.2", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1" - }, - "json-schema-traverse": { - "version": "0.4.1", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1" - }, - "json5": { - "version": "2.2.3", - "dev": true - }, - "jsonfile": { - "version": "6.1.0", - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "jsx-ast-utils": { - "version": "3.3.5", - "dev": true, - "requires": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" - } - }, - "jszip": { - "version": "3.10.1", - "requires": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "setimmediate": "^1.0.5" - }, - "dependencies": { - "isarray": { - "version": "1.0.0" - }, - "pako": { - "version": "1.0.11" - }, - "readable-stream": { - "version": "2.3.8", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2" - }, - "string_decoder": { - "version": "1.1.1", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "just-extend": { - "version": "4.2.1", - "dev": true - }, - "keyv": { - "version": "4.5.4", - "requires": { - "json-buffer": "3.0.1" - } - }, - "kind-of": { - "version": "6.0.3", - "dev": true - }, - "kleur": { - "version": "3.0.3", - "dev": true - }, - "language-subtag-registry": { - "version": "0.3.22", - "dev": true - }, - "language-tags": { - "version": "1.0.9", - "dev": true, - "requires": { - "language-subtag-registry": "^0.3.20" - } - }, - "launch-editor": { - "version": "2.6.1", - "dev": true, - "requires": { - "picocolors": "^1.0.0", - "shell-quote": "^1.8.1" - } - }, - "lazy-universal-dotenv": { - "version": "4.0.0", - "dev": true, - "requires": { - "app-root-dir": "^1.0.2", - "dotenv": "^16.0.0", - "dotenv-expand": "^10.0.0" - } - }, - "less": { - "version": "4.2.0", - "dev": true, - "peer": true, - "requires": { - "copy-anything": "^2.0.1", - "errno": "^0.1.1", - "graceful-fs": "^4.1.2", - "image-size": "~0.5.0", - "make-dir": "^2.1.0", - "mime": "^1.4.1", - "needle": "^3.1.0", - "parse-node-version": "^1.0.1", - "source-map": "~0.6.0", - "tslib": "^2.3.0" - }, - "dependencies": { - "make-dir": { - "version": "2.1.0", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "pify": { - "version": "4.0.1", - "dev": true, - "optional": true, - "peer": true - }, - "semver": { - "version": "5.7.2", - "dev": true, - "optional": true, - "peer": true - }, - "source-map": { - "version": "0.6.1", - "dev": true, - "optional": true, - "peer": true - } - } - }, - "less-loader": { - "version": "11.1.4", - "dev": true, - "requires": {} - }, - "leven": { - "version": "3.1.0", - "dev": true - }, - "levn": { - "version": "0.4.1", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "lie": { - "version": "3.3.0", - "requires": { - "immediate": "~3.0.5" - } - }, - "lilconfig": { - "version": "2.1.0", - "dev": true - }, - "lines-and-columns": { - "version": "1.2.4" - }, - "lint-staged": { - "version": "13.3.0", - "dev": true, - "requires": { - "chalk": "5.3.0", - "commander": "11.0.0", - "debug": "4.3.4", - "execa": "7.2.0", - "lilconfig": "2.1.0", - "listr2": "6.6.1", - "micromatch": "4.0.5", - "pidtree": "0.6.0", - "string-argv": "0.3.2", - "yaml": "2.3.1" - }, - "dependencies": { - "chalk": { - "version": "5.3.0", - "dev": true - }, - "commander": { - "version": "11.0.0", - "dev": true - }, - "execa": { - "version": "7.2.0", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" - } - }, - "human-signals": { - "version": "4.3.1", - "dev": true - }, - "is-stream": { - "version": "3.0.0", - "dev": true - }, - "mimic-fn": { - "version": "4.0.0", - "dev": true - }, - "npm-run-path": { - "version": "5.3.0", - "dev": true, - "requires": { - "path-key": "^4.0.0" - } - }, - "onetime": { - "version": "6.0.0", - "dev": true, - "requires": { - "mimic-fn": "^4.0.0" - } - }, - "path-key": { - "version": "4.0.0", - "dev": true - }, - "signal-exit": { - "version": "3.0.7", - "dev": true - }, - "strip-final-newline": { - "version": "3.0.0", - "dev": true - } - } - }, - "listr2": { - "version": "6.6.1", - "dev": true, - "requires": { - "cli-truncate": "^3.1.0", - "colorette": "^2.0.20", - "eventemitter3": "^5.0.1", - "log-update": "^5.0.1", - "rfdc": "^1.3.0", - "wrap-ansi": "^8.1.0" - } - }, - "load-json-file": { - "version": "4.0.0", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "parse-json": { - "version": "4.0.0", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "strip-bom": { - "version": "3.0.0", - "dev": true - } - } - }, - "loader-runner": { - "version": "4.3.0", - "dev": true - }, - "loader-utils": { - "version": "2.0.4", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - } - }, - "locate-path": { - "version": "6.0.0", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "lodash": { - "version": "4.17.21" - }, - "lodash.debounce": { - "version": "4.0.8", - "dev": true - }, - "lodash.merge": { - "version": "4.6.2", - "dev": true - }, - "log-update": { - "version": "5.0.1", - "dev": true, - "requires": { - "ansi-escapes": "^5.0.0", - "cli-cursor": "^4.0.0", - "slice-ansi": "^5.0.0", - "strip-ansi": "^7.0.1", - "wrap-ansi": "^8.0.1" - }, - "dependencies": { - "ansi-escapes": { - "version": "5.0.0", - "dev": true, - "requires": { - "type-fest": "^1.0.2" - } - }, - "ansi-regex": { - "version": "6.0.1", - "dev": true - }, - "strip-ansi": { - "version": "7.1.0", - "dev": true, - "requires": { - "ansi-regex": "^6.0.1" - } - }, - "type-fest": { - "version": "1.4.0", - "dev": true - } - } - }, - "lolex": { - "version": "4.2.0", - "dev": true - }, - "loose-envify": { - "version": "1.4.0", - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "loupe": { - "version": "2.3.7", - "dev": true, - "requires": { - "get-func-name": "^2.0.1" - } - }, - "lower-case": { - "version": "2.0.2", - "dev": true, - "requires": { - "tslib": "^2.0.3" - } - }, - "lowercase-keys": { - "version": "2.0.0" - }, - "lru-cache": { - "version": "5.1.1", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "lru-queue": { - "version": "0.1.0", - "requires": { - "es5-ext": "~0.10.2" - } - }, - "lz-string": { - "version": "1.5.0", - "dev": true - }, - "magic-string": { - "version": "0.30.10", - "dev": true, - "requires": { - "@jridgewell/sourcemap-codec": "^1.4.15" - } - }, - "make-dir": { - "version": "3.1.0", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "makeerror": { - "version": "1.0.12", - "dev": true, - "requires": { - "tmpl": "1.0.5" - } - }, - "map-or-similar": { - "version": "1.5.0", - "dev": true - }, - "map-stream": { - "version": "0.1.0", - "dev": true - }, - "markdown-to-jsx": { - "version": "7.4.7", - "dev": true, - "requires": {} - }, - "mdast-util-definitions": { - "version": "4.0.0", - "dev": true, - "requires": { - "unist-util-visit": "^2.0.0" - } - }, - "mdast-util-to-string": { - "version": "1.1.0", - "dev": true - }, - "mdn-data": { - "version": "2.0.30", - "dev": true - }, - "media-typer": { - "version": "0.3.0", - "dev": true - }, - "memfs": { - "version": "3.5.3", - "dev": true, - "requires": { - "fs-monkey": "^1.0.4" - } - }, - "memoizee": { - "version": "0.4.15", - "requires": { - "d": "^1.0.1", - "es5-ext": "^0.10.53", - "es6-weak-map": "^2.0.3", - "event-emitter": "^0.3.5", - "is-promise": "^2.2.2", - "lru-queue": "^0.1.0", - "next-tick": "^1.1.0", - "timers-ext": "^0.1.7" - } - }, - "memoizerific": { - "version": "1.11.3", - "dev": true, - "requires": { - "map-or-similar": "^1.5.0" - } - }, - "memorystream": { - "version": "0.3.1", - "dev": true - }, - "merge-descriptors": { - "version": "1.0.1", - "dev": true - }, - "merge-stream": { - "version": "2.0.0", - "dev": true - }, - "merge2": { - "version": "1.4.1", - "dev": true - }, - "methods": { - "version": "1.1.2", - "dev": true - }, - "micromatch": { - "version": "4.0.5", - "dev": true, - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - }, - "mime": { - "version": "1.6.0", - "dev": true - }, - "mime-db": { - "version": "1.52.0", - "dev": true - }, - "mime-types": { - "version": "2.1.35", - "dev": true, - "requires": { - "mime-db": "1.52.0" - } - }, - "mimic-fn": { - "version": "2.1.0" - }, - "mimic-response": { - "version": "1.0.1" - }, - "min-indent": { - "version": "1.0.1", - "dev": true - }, - "minimalistic-assert": { - "version": "1.0.1", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.8", - "dev": true - }, - "minipass": { - "version": "7.0.4" - }, - "mitt": { - "version": "3.0.1" - }, - "mkdirp-classic": { - "version": "0.5.3" - }, - "ms": { - "version": "2.1.2" - }, - "multicast-dns": { - "version": "7.2.5", - "dev": true, - "requires": { - "dns-packet": "^5.2.2", - "thunky": "^1.0.2" - } - }, - "mute-stream": { - "version": "0.0.8" - }, - "mz": { - "version": "2.7.0", - "dev": true, - "requires": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "nanoid": { - "version": "3.3.7", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "dev": true - }, - "natural-compare-lite": { - "version": "1.4.0", - "dev": true - }, - "needle": { - "version": "3.3.1", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "iconv-lite": "^0.6.3", - "sax": "^1.2.4" - }, - "dependencies": { - "iconv-lite": { - "version": "0.6.3", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - } - } - }, - "negotiator": { - "version": "0.6.3", - "dev": true - }, - "neo-async": { - "version": "2.6.2", - "dev": true - }, - "netmask": { - "version": "2.0.2" - }, - "next-tick": { - "version": "1.1.0" - }, - "nice-try": { - "version": "1.0.5", - "dev": true - }, - "nise": { - "version": "1.5.3", - "dev": true, - "requires": { - "@sinonjs/formatio": "^3.2.1", - "@sinonjs/text-encoding": "^0.7.1", - "just-extend": "^4.0.2", - "lolex": "^5.0.1", - "path-to-regexp": "^1.7.0" - }, - "dependencies": { - "@sinonjs/commons": { - "version": "1.8.6", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "isarray": { - "version": "0.0.1", - "dev": true - }, - "lolex": { - "version": "5.1.2", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, - "path-to-regexp": { - "version": "1.8.0", - "dev": true, - "requires": { - "isarray": "0.0.1" - } - } - } - }, - "no-case": { - "version": "3.0.4", - "dev": true, - "requires": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node-abort-controller": { - "version": "3.1.1", - "dev": true - }, - "node-cleanup": { - "version": "2.1.2", - "dev": true - }, - "node-fetch": { - "version": "2.7.0", - "requires": { - "whatwg-url": "^5.0.0" - }, - "dependencies": { - "tr46": { - "version": "0.0.3" - }, - "webidl-conversions": { - "version": "3.0.1" - }, - "whatwg-url": { - "version": "5.0.0", - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - } - } - }, - "node-forge": { - "version": "1.3.1", - "dev": true - }, - "node-int64": { - "version": "0.4.0", - "dev": true - }, - "node-releases": { - "version": "2.0.14", - "dev": true - }, - "normalize-package-data": { - "version": "2.5.0", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - }, - "dependencies": { - "semver": { - "version": "5.7.2", - "dev": true - } - } - }, - "normalize-path": { - "version": "3.0.0", - "dev": true - }, - "normalize-range": { - "version": "0.1.2", - "dev": true - }, - "normalize-url": { - "version": "6.1.0" - }, - "npm-run-all": { - "version": "4.1.5", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "chalk": "^2.4.1", - "cross-spawn": "^6.0.5", - "memorystream": "^0.3.1", - "minimatch": "^3.0.4", - "pidtree": "^0.3.0", - "read-pkg": "^3.0.0", - "shell-quote": "^1.6.1", - "string.prototype.padend": "^3.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "path-key": { - "version": "2.0.1", - "dev": true - }, - "pidtree": { - "version": "0.3.1", - "dev": true - }, - "semver": { - "version": "5.7.2", - "dev": true - }, - "shebang-command": { - "version": "1.2.0", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "dev": true - }, - "which": { - "version": "1.3.1", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "npm-run-path": { - "version": "4.0.1", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "nth-check": { - "version": "2.1.1", - "dev": true, - "requires": { - "boolbase": "^1.0.0" - } - }, - "nwsapi": { - "version": "2.2.9", - "dev": true - }, - "object-assign": { - "version": "4.1.1" - }, - "object-hash": { - "version": "3.0.0", - "dev": true - }, - "object-inspect": { - "version": "1.13.1", - "dev": true - }, - "object-is": { - "version": "1.1.6", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1" - } - }, - "object-keys": { - "version": "1.1.1", - "dev": true - }, - "object.assign": { - "version": "4.1.5", - "dev": true, - "requires": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - } - }, - "object.entries": { - "version": "1.1.8", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - } - }, - "object.fromentries": { - "version": "2.0.8", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - } - }, - "object.groupby": { - "version": "1.0.3", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2" - } - }, - "object.hasown": { - "version": "1.1.4", - "dev": true, - "requires": { - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - } - }, - "object.values": { - "version": "1.2.0", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - } - }, - "objectorarray": { - "version": "1.0.5", - "dev": true - }, - "obuf": { - "version": "1.1.2", - "dev": true - }, - "on-finished": { - "version": "2.4.1", - "dev": true, - "requires": { - "ee-first": "1.1.1" - } - }, - "on-headers": { - "version": "1.0.2", - "dev": true - }, - "once": { - "version": "1.4.0", - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "open": { - "version": "8.4.2", - "dev": true, - "requires": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - } - }, - "optionator": { - "version": "0.9.4", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - } - }, - "p-cancelable": { - "version": "2.1.1" - }, - "p-limit": { - "version": "3.1.0", - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "p-queue": { - "version": "7.4.1", - "requires": { - "eventemitter3": "^5.0.1", - "p-timeout": "^5.0.2" - } - }, - "p-retry": { - "version": "4.6.2", - "dev": true, - "requires": { - "@types/retry": "0.12.0", - "retry": "^0.13.1" - } - }, - "p-timeout": { - "version": "5.1.0" - }, - "p-try": { - "version": "2.2.0", - "dev": true - }, - "pac-proxy-agent": { - "version": "7.0.1", - "requires": { - "@tootallnate/quickjs-emscripten": "^0.23.0", - "agent-base": "^7.0.2", - "debug": "^4.3.4", - "get-uri": "^6.0.1", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.2", - "pac-resolver": "^7.0.0", - "socks-proxy-agent": "^8.0.2" - }, - "dependencies": { - "agent-base": { - "version": "7.1.1", - "requires": { - "debug": "^4.3.4" - } - }, - "http-proxy-agent": { - "version": "7.0.2", - "requires": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - } - }, - "https-proxy-agent": { - "version": "7.0.4", - "requires": { - "agent-base": "^7.0.2", - "debug": "4" - } - } - } - }, - "pac-resolver": { - "version": "7.0.1", - "requires": { - "degenerator": "^5.0.0", - "netmask": "^2.0.2" - } - }, - "param-case": { - "version": "3.0.4", - "dev": true, - "requires": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "parent-module": { - "version": "1.0.1", - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "5.2.0", - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "parse-node-version": { - "version": "1.0.1", - "dev": true, - "peer": true - }, - "parse5": { - "version": "7.1.2", - "dev": true, - "requires": { - "entities": "^4.4.0" - } - }, - "parseurl": { - "version": "1.3.3", - "dev": true - }, - "pascal-case": { - "version": "3.1.2", - "dev": true, - "requires": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "path-browserify": { - "version": "1.0.1", - "dev": true - }, - "path-exists": { - "version": "4.0.0", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "dev": true - }, - "path-parse": { - "version": "1.0.7" - }, - "path-scurry": { - "version": "1.10.2", - "requires": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "dependencies": { - "lru-cache": { - "version": "10.2.2" - } - } - }, - "path-to-regexp": { - "version": "0.1.7", - "dev": true - }, - "path-type": { - "version": "4.0.0" - }, - "pause-stream": { - "version": "0.0.11", - "dev": true, - "requires": { - "through": "~2.3" - } - }, - "pend": { - "version": "1.2.0" - }, - "picocolors": { - "version": "1.0.0" - }, - "picomatch": { - "version": "2.3.1", - "dev": true - }, - "pidtree": { - "version": "0.6.0", - "dev": true - }, - "pify": { - "version": "3.0.0", - "dev": true - }, - "pirates": { - "version": "4.0.6", - "dev": true - }, - "pkg-dir": { - "version": "5.0.0", - "dev": true, - "requires": { - "find-up": "^5.0.0" - } - }, - "polished": { - "version": "4.3.1", - "dev": true, - "requires": { - "@babel/runtime": "^7.17.8" - } - }, - "possible-typed-array-names": { - "version": "1.0.0", - "dev": true - }, - "postcss": { - "version": "8.4.38", - "dev": true, - "requires": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" - } - }, - "postcss-import": { - "version": "15.1.0", - "dev": true, - "requires": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - } - }, - "postcss-js": { - "version": "4.0.1", - "dev": true, - "requires": { - "camelcase-css": "^2.0.1" - } - }, - "postcss-load-config": { - "version": "4.0.2", - "dev": true, - "requires": { - "lilconfig": "^3.0.0", - "yaml": "^2.3.4" - }, - "dependencies": { - "lilconfig": { - "version": "3.1.1", - "dev": true - }, - "yaml": { - "version": "2.4.2", - "dev": true - } - } - }, - "postcss-loader": { - "version": "7.3.4", - "dev": true, - "requires": { - "cosmiconfig": "^8.3.5", - "jiti": "^1.20.0", - "semver": "^7.5.4" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.6.0", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "dev": true - } - } - }, - "postcss-modules-extract-imports": { - "version": "3.1.0", - "dev": true, - "requires": {} - }, - "postcss-modules-local-by-default": { - "version": "4.0.5", - "dev": true, - "requires": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" - } - }, - "postcss-modules-scope": { - "version": "3.2.0", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.4" - } - }, - "postcss-modules-values": { - "version": "4.0.0", - "dev": true, - "requires": { - "icss-utils": "^5.0.0" - } - }, - "postcss-nested": { - "version": "6.0.1", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.11" - } - }, - "postcss-selector-parser": { - "version": "6.0.16", - "dev": true, - "requires": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - } - }, - "postcss-value-parser": { - "version": "4.2.0", - "dev": true - }, - "prelude-ls": { - "version": "1.2.1", - "dev": true - }, - "prettier": { - "version": "2.8.8", - "dev": true - }, - "prettier-linter-helpers": { - "version": "1.0.0", - "dev": true, - "requires": { - "fast-diff": "^1.1.2" - } - }, - "pretty-error": { - "version": "4.0.0", - "dev": true, - "requires": { - "lodash": "^4.17.20", - "renderkid": "^3.0.0" - } - }, - "pretty-format": { - "version": "27.5.1", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "dev": true - } - } - }, - "pretty-hrtime": { - "version": "1.0.3", - "dev": true - }, - "pretty-time": { - "version": "1.1.0", - "dev": true - }, - "process": { - "version": "0.11.10", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.1" - }, - "progress": { - "version": "2.0.3" - }, - "promptly": { - "version": "3.2.0", - "requires": { - "read": "^1.0.4" - } - }, - "prompts": { - "version": "2.4.2", - "dev": true, - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - } - }, - "prop-types": { - "version": "15.8.1", - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - }, - "dependencies": { - "react-is": { - "version": "16.13.1" - } - } - }, - "proxy-addr": { - "version": "2.0.7", - "dev": true, - "requires": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - } - }, - "proxy-agent": { - "version": "6.3.0", - "requires": { - "agent-base": "^7.0.2", - "debug": "^4.3.4", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.0", - "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.0.0", - "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.1" - }, - "dependencies": { - "agent-base": { - "version": "7.1.1", - "requires": { - "debug": "^4.3.4" - } - }, - "http-proxy-agent": { - "version": "7.0.2", - "requires": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - } - }, - "https-proxy-agent": { - "version": "7.0.4", - "requires": { - "agent-base": "^7.0.2", - "debug": "4" - } - }, - "lru-cache": { - "version": "7.18.3" - } - } - }, - "proxy-from-env": { - "version": "1.1.0" - }, - "prr": { - "version": "1.0.1", - "dev": true, - "optional": true, - "peer": true - }, - "ps-tree": { - "version": "1.2.0", - "dev": true, - "requires": { - "event-stream": "=3.3.4" - } - }, - "psl": { - "version": "1.9.0", - "dev": true - }, - "pump": { - "version": "3.0.0", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "punycode": { - "version": "2.3.1", - "dev": true - }, - "puppeteer": { - "version": "21.0.3", - "requires": { - "@puppeteer/browsers": "1.6.0", - "cosmiconfig": "8.2.0", - "puppeteer-core": "21.0.3" - }, - "dependencies": { - "argparse": { - "version": "2.0.1" - }, - "cosmiconfig": { - "version": "8.2.0", - "requires": { - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0" - } - }, - "js-yaml": { - "version": "4.1.0", - "requires": { - "argparse": "^2.0.1" - } - }, - "puppeteer-core": { - "version": "21.0.3", - "requires": { - "@puppeteer/browsers": "1.6.0", - "chromium-bidi": "0.4.20", - "cross-fetch": "4.0.0", - "debug": "4.3.4", - "devtools-protocol": "0.0.1147663", - "ws": "8.13.0" - } - }, - "ws": { - "version": "8.13.0", - "requires": {} - } - } - }, - "pure-rand": { - "version": "6.1.0", - "dev": true - }, - "qs": { - "version": "6.12.1", - "dev": true, - "requires": { - "side-channel": "^1.0.6" - } - }, - "querystringify": { - "version": "2.2.0", - "dev": true - }, - "queue-microtask": { - "version": "1.2.3", - "dev": true - }, - "queue-tick": { - "version": "1.0.1" - }, - "quick-lru": { - "version": "5.1.1" - }, - "ramda": { - "version": "0.29.0", - "dev": true - }, - "randombytes": { - "version": "2.1.0", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "range-parser": { - "version": "1.2.1", - "dev": true - }, - "raw-body": { - "version": "2.5.2", - "dev": true, - "requires": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "re-resizable": { - "version": "6.9.16", - "requires": {} - }, - "react": { - "version": "18.3.1", - "requires": { - "loose-envify": "^1.1.0" - } - }, - "react-colorful": { - "version": "5.6.1", - "dev": true, - "requires": {} - }, - "react-copy-to-clipboard": { - "version": "5.1.0", - "requires": { - "copy-to-clipboard": "^3.3.1", - "prop-types": "^15.8.1" - } - }, - "react-docgen": { - "version": "7.0.3", - "dev": true, - "requires": { - "@babel/core": "^7.18.9", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9", - "@types/babel__core": "^7.18.0", - "@types/babel__traverse": "^7.18.0", - "@types/doctrine": "^0.0.9", - "@types/resolve": "^1.20.2", - "doctrine": "^3.0.0", - "resolve": "^1.22.1", - "strip-indent": "^4.0.0" - }, - "dependencies": { - "@types/doctrine": { - "version": "0.0.9", - "dev": true - } - } - }, - "react-docgen-typescript": { - "version": "2.2.2", - "dev": true, - "requires": {} - }, - "react-dom": { - "version": "18.3.1", - "requires": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" - } - }, - "react-element-to-jsx-string": { - "version": "15.0.0", - "dev": true, - "requires": { - "@base2/pretty-print-object": "1.0.1", - "is-plain-object": "5.0.0", - "react-is": "18.1.0" - }, - "dependencies": { - "react-is": { - "version": "18.1.0", - "dev": true - } - } - }, - "react-error-boundary": { - "version": "4.0.13", - "requires": { - "@babel/runtime": "^7.12.5" - } - }, - "react-fast-compare": { - "version": "3.2.2" - }, - "react-is": { - "version": "17.0.2", - "dev": true - }, - "react-refresh": { - "version": "0.14.2", - "dev": true - }, - "react-remove-scroll": { - "version": "2.5.5", - "dev": true, - "requires": { - "react-remove-scroll-bar": "^2.3.3", - "react-style-singleton": "^2.2.1", - "tslib": "^2.1.0", - "use-callback-ref": "^1.3.0", - "use-sidecar": "^1.1.2" - } - }, - "react-remove-scroll-bar": { - "version": "2.3.6", - "dev": true, - "requires": { - "react-style-singleton": "^2.2.1", - "tslib": "^2.0.0" - } - }, - "react-shallow-renderer": { - "version": "16.15.0", - "dev": true, - "requires": { - "object-assign": "^4.1.1", - "react-is": "^16.12.0 || ^17.0.0 || ^18.0.0" - } - }, - "react-style-singleton": { - "version": "2.2.1", - "dev": true, - "requires": { - "get-nonce": "^1.0.0", - "invariant": "^2.2.4", - "tslib": "^2.0.0" - } - }, - "react-test-renderer": { - "version": "18.3.1", - "dev": true, - "requires": { - "react-is": "^18.3.1", - "react-shallow-renderer": "^16.15.0", - "scheduler": "^0.23.2" - }, - "dependencies": { - "react-is": { - "version": "18.3.1", - "dev": true - } - } - }, - "read": { - "version": "1.0.7", - "requires": { - "mute-stream": "~0.0.4" - } - }, - "read-cache": { - "version": "1.0.0", - "dev": true, - "requires": { - "pify": "^2.3.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "dev": true - } - } - }, - "read-pkg": { - "version": "3.0.0", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - }, - "dependencies": { - "path-type": { - "version": "3.0.0", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - } - } - }, - "readable-stream": { - "version": "3.6.2", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "readdirp": { - "version": "3.6.0", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "recast": { - "version": "0.23.6", - "dev": true, - "requires": { - "ast-types": "^0.16.1", - "esprima": "~4.0.0", - "source-map": "~0.6.1", - "tiny-invariant": "^1.3.3", - "tslib": "^2.0.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "dev": true - } - } - }, - "rechoir": { - "version": "0.8.0", - "dev": true, - "requires": { - "resolve": "^1.20.0" - } - }, - "redent": { - "version": "3.0.0", - "dev": true, - "requires": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - }, - "dependencies": { - "strip-indent": { - "version": "3.0.0", - "dev": true, - "requires": { - "min-indent": "^1.0.0" - } - } - } - }, - "reflect.getprototypeof": { - "version": "1.0.6", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.1", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.3", - "which-builtin-type": "^1.1.3" - } - }, - "regenerate": { - "version": "1.4.2", - "dev": true - }, - "regenerate-unicode-properties": { - "version": "10.1.1", - "dev": true, - "requires": { - "regenerate": "^1.4.2" - } - }, - "regenerator-runtime": { - "version": "0.14.1" - }, - "regenerator-transform": { - "version": "0.15.2", - "dev": true, - "requires": { - "@babel/runtime": "^7.8.4" - } - }, - "regex-parser": { - "version": "2.3.0", - "dev": true - }, - "regexp.prototype.flags": { - "version": "1.5.2", - "dev": true, - "requires": { - "call-bind": "^1.0.6", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "set-function-name": "^2.0.1" - } - }, - "regexpu-core": { - "version": "5.3.2", - "dev": true, - "requires": { - "@babel/regjsgen": "^0.8.0", - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" - } - }, - "regjsparser": { - "version": "0.9.1", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "dev": true - } - } - }, - "relateurl": { - "version": "0.2.7", - "dev": true - }, - "remark-external-links": { - "version": "8.0.0", - "dev": true, - "requires": { - "extend": "^3.0.0", - "is-absolute-url": "^3.0.0", - "mdast-util-definitions": "^4.0.0", - "space-separated-tokens": "^1.0.0", - "unist-util-visit": "^2.0.0" - } - }, - "remark-slug": { - "version": "6.1.0", - "dev": true, - "requires": { - "github-slugger": "^1.0.0", - "mdast-util-to-string": "^1.0.0", - "unist-util-visit": "^2.0.0" - } - }, - "renderkid": { - "version": "3.0.0", - "dev": true, - "requires": { - "css-select": "^4.1.3", - "dom-converter": "^0.2.0", - "htmlparser2": "^6.1.0", - "lodash": "^4.17.21", - "strip-ansi": "^6.0.1" - } - }, - "require-directory": { - "version": "2.1.1" - }, - "require-from-string": { - "version": "2.0.2", - "dev": true - }, - "requireindex": { - "version": "1.2.0", - "dev": true - }, - "requires-port": { - "version": "1.0.0", - "dev": true - }, - "resolve": { - "version": "1.22.8", - "requires": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-alpn": { - "version": "1.2.1" - }, - "resolve-cwd": { - "version": "3.0.0", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "dev": true - }, - "resolve-pkg-maps": { - "version": "1.0.0", - "dev": true - }, - "resolve-url-loader": { - "version": "5.0.0", - "dev": true, - "requires": { - "adjust-sourcemap-loader": "^4.0.0", - "convert-source-map": "^1.7.0", - "loader-utils": "^2.0.0", - "postcss": "^8.2.14", - "source-map": "0.6.1" - }, - "dependencies": { - "convert-source-map": { - "version": "1.9.0", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "dev": true - } - } - }, - "resolve.exports": { - "version": "2.0.2", - "dev": true - }, - "responselike": { - "version": "2.0.1", - "requires": { - "lowercase-keys": "^2.0.0" - } - }, - "restore-cursor": { - "version": "4.0.0", - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "dependencies": { - "signal-exit": { - "version": "3.0.7" - } - } - }, - "retry": { - "version": "0.13.1", - "dev": true - }, - "reusify": { - "version": "1.0.4", - "dev": true - }, - "rfdc": { - "version": "1.3.1", - "dev": true - }, - "rimraf": { - "version": "5.0.5", - "dev": true, - "requires": { - "glob": "^10.3.7" - } - }, - "run-parallel": { - "version": "1.2.0", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "safe-array-concat": { - "version": "1.1.2", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - } - }, - "safe-buffer": { - "version": "5.2.1" - }, - "safe-regex-test": { - "version": "1.0.3", - "dev": true, - "requires": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-regex": "^1.1.4" - } - }, - "safer-buffer": { - "version": "2.1.2", - "dev": true - }, - "sass-loader": { - "version": "13.3.3", - "dev": true, - "requires": { - "neo-async": "^2.6.2" - } - }, - "sax": { - "version": "1.3.0" - }, - "saxes": { - "version": "6.0.0", - "dev": true, - "requires": { - "xmlchars": "^2.2.0" - } - }, - "scheduler": { - "version": "0.23.2", - "requires": { - "loose-envify": "^1.1.0" - } - }, - "schema-utils": { - "version": "3.3.0", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - }, - "select-hose": { - "version": "2.0.0", - "dev": true - }, - "selfsigned": { - "version": "2.4.1", - "dev": true, - "requires": { - "@types/node-forge": "^1.3.0", - "node-forge": "^1" - } - }, - "semver": { - "version": "6.3.1", - "dev": true - }, - "send": { - "version": "0.18.0", - "dev": true, - "requires": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "dev": true, - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "dev": true - } - } - }, - "ms": { - "version": "2.1.3", - "dev": true - } - } - }, - "serialize-javascript": { - "version": "6.0.2", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "serve-index": { - "version": "1.9.1", - "dev": true, - "requires": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "depd": { - "version": "1.1.2", - "dev": true - }, - "http-errors": { - "version": "1.6.3", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - } - }, - "inherits": { - "version": "2.0.3", - "dev": true - }, - "ms": { - "version": "2.0.0", - "dev": true - }, - "setprototypeof": { - "version": "1.1.0", - "dev": true - }, - "statuses": { - "version": "1.5.0", - "dev": true - } - } - }, - "serve-static": { - "version": "1.15.0", - "dev": true, - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - } - }, - "set-function-length": { - "version": "1.2.2", - "dev": true, - "requires": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - } - }, - "set-function-name": { - "version": "2.0.2", - "dev": true, - "requires": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" - } - }, - "setimmediate": { - "version": "1.0.5" - }, - "setprototypeof": { - "version": "1.2.0", - "dev": true - }, - "shallow-clone": { - "version": "3.0.1", - "dev": true, - "requires": { - "kind-of": "^6.0.2" - } - }, - "shallow-equal": { - "version": "3.1.0" - }, - "shallowequal": { - "version": "1.1.0", - "dev": true, - "peer": true - }, - "shebang-command": { - "version": "2.0.0", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "dev": true - }, - "shell-quote": { - "version": "1.8.1", - "dev": true - }, - "side-channel": { - "version": "1.0.6", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" - } - }, - "signal-exit": { - "version": "4.1.0", - "dev": true - }, - "simple-cookie": { - "version": "1.0.15" - }, - "sinon": { - "version": "7.5.0", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.4.0", - "@sinonjs/formatio": "^3.2.1", - "@sinonjs/samsam": "^3.3.3", - "diff": "^3.5.0", - "lolex": "^4.2.0", - "nise": "^1.5.2", - "supports-color": "^5.5.0" - }, - "dependencies": { - "@sinonjs/commons": { - "version": "1.8.6", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - } - } - }, - "sinon-chrome": { - "version": "3.0.1", - "dev": true, - "requires": { - "lodash": "^4.16.3", - "sinon": "^7.2.3", - "urijs": "^1.18.2" - } - }, - "sisteransi": { - "version": "1.0.5", - "dev": true - }, - "sitemapper": { - "version": "3.2.8", - "requires": { - "got": "^11.8.0", - "is-gzip": "2.0.0", - "p-limit": "^3.1.0", - "xml2js": "^0.5.0" - }, - "dependencies": { - "is-gzip": { - "version": "2.0.0" - } - } - }, - "slash": { - "version": "3.0.0", - "dev": true - }, - "slice-ansi": { - "version": "5.0.0", - "dev": true, - "requires": { - "ansi-styles": "^6.0.0", - "is-fullwidth-code-point": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "6.2.1", - "dev": true - } - } - }, - "smart-buffer": { - "version": "4.2.0" - }, - "snake-case": { - "version": "3.0.4", - "dev": true, - "requires": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "sockjs": { - "version": "0.3.24", - "dev": true, - "requires": { - "faye-websocket": "^0.11.3", - "uuid": "^8.3.2", - "websocket-driver": "^0.7.4" - }, - "dependencies": { - "uuid": { - "version": "8.3.2", - "dev": true - } - } - }, - "socks": { - "version": "2.8.3", - "requires": { - "ip-address": "^9.0.5", - "smart-buffer": "^4.2.0" - } - }, - "socks-proxy-agent": { - "version": "8.0.3", - "requires": { - "agent-base": "^7.1.1", - "debug": "^4.3.4", - "socks": "^2.7.1" - }, - "dependencies": { - "agent-base": { - "version": "7.1.1", - "requires": { - "debug": "^4.3.4" - } - } - } - }, - "source-map": { - "version": "0.7.4", - "dev": true - }, - "source-map-js": { - "version": "1.2.0", - "dev": true - }, - "source-map-support": { - "version": "0.5.13", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "dev": true - } - } - }, - "space-separated-tokens": { - "version": "1.1.5", - "dev": true - }, - "spdx-correct": { - "version": "3.2.0", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - }, - "dependencies": { - "spdx-expression-parse": { - "version": "3.0.1", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - } - } - }, - "spdx-exceptions": { - "version": "2.5.0", - "dev": true - }, - "spdx-expression-parse": { - "version": "4.0.0", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.17", - "dev": true - }, - "spdy": { - "version": "4.0.2", - "dev": true, - "requires": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" - } - }, - "spdy-transport": { - "version": "3.0.0", - "dev": true, - "requires": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" - } - }, - "spinnies": { - "version": "0.5.1", - "requires": { - "chalk": "^2.4.2", - "cli-cursor": "^3.0.0", - "strip-ansi": "^5.2.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.1" - }, - "cli-cursor": { - "version": "3.1.0", - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "restore-cursor": { - "version": "3.1.0", - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "signal-exit": { - "version": "3.0.7" - }, - "strip-ansi": { - "version": "5.2.0", - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "split": { - "version": "0.3.3", - "dev": true, - "requires": { - "through": "2" - } - }, - "sprintf-js": { - "version": "1.0.3", - "dev": true - }, - "stack-utils": { - "version": "2.0.6", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "dev": true - } - } - }, - "stackframe": { - "version": "1.3.4", - "dev": true - }, - "statuses": { - "version": "2.0.1", - "dev": true - }, - "std-env": { - "version": "3.7.0", - "dev": true - }, - "stdin-discarder": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz", - "integrity": "sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==", - "requires": { - "bl": "^5.0.0" - }, - "dependencies": { - "bl": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", - "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", - "requires": { - "buffer": "^6.0.3", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - } - } - }, - "stop-iteration-iterator": { - "version": "1.0.0", - "dev": true, - "requires": { - "internal-slot": "^1.0.4" - } - }, - "store2": { - "version": "2.14.3", - "dev": true - }, - "stream-combiner": { - "version": "0.0.4", - "dev": true, - "requires": { - "duplexer": "~0.1.1" - } - }, - "streamx": { - "version": "2.16.1", - "requires": { - "bare-events": "^2.2.0", - "fast-fifo": "^1.1.0", - "queue-tick": "^1.0.1" - } - }, - "string_decoder": { - "version": "1.3.0", - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "string-argv": { - "version": "0.3.2", - "dev": true - }, - "string-length": { - "version": "4.0.2", - "dev": true, - "requires": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - } - }, - "string-width": { - "version": "5.1.2", - "dev": true, - "requires": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "6.0.1", - "dev": true - }, - "strip-ansi": { - "version": "7.1.0", - "dev": true, - "requires": { - "ansi-regex": "^6.0.1" - } - } - } - }, - "string-width-cjs": { - "version": "npm:string-width@4.2.3", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "dependencies": { - "emoji-regex": { - "version": "8.0.0", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "dev": true - } - } - }, - "string.prototype.matchall": { - "version": "4.0.11", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "regexp.prototype.flags": "^1.5.2", - "set-function-name": "^2.0.2", - "side-channel": "^1.0.6" - } - }, - "string.prototype.padend": { - "version": "3.1.6", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - } - }, - "string.prototype.trim": { - "version": "1.2.9", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-object-atoms": "^1.0.0" - } - }, - "string.prototype.trimend": { - "version": "1.0.8", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - } - }, - "string.prototype.trimstart": { - "version": "1.0.8", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - } - }, - "strip-ansi": { - "version": "6.0.1", - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-ansi-cjs": { - "version": "npm:strip-ansi@6.0.1", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-bom": { - "version": "2.0.0", - "dev": true, - "requires": { - "is-utf8": "^0.2.0" - } - }, - "strip-final-newline": { - "version": "2.0.0", - "dev": true - }, - "strip-indent": { - "version": "4.0.0", - "dev": true, - "requires": { - "min-indent": "^1.0.1" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "dev": true - }, - "strnum": { - "version": "1.0.5" - }, - "style-loader": { - "version": "3.3.4", - "dev": true, - "requires": {} - }, - "styled-components": { - "version": "6.1.8", - "dev": true, - "peer": true, - "requires": { - "@emotion/is-prop-valid": "1.2.1", - "@emotion/unitless": "0.8.0", - "@types/stylis": "4.2.0", - "css-to-react-native": "3.2.0", - "csstype": "3.1.2", - "postcss": "8.4.31", - "shallowequal": "1.1.0", - "stylis": "4.3.1", - "tslib": "2.5.0" - }, - "dependencies": { - "csstype": { - "version": "3.1.2", - "dev": true, - "peer": true - }, - "postcss": { - "version": "8.4.31", - "dev": true, - "peer": true, - "requires": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - } - }, - "tslib": { - "version": "2.5.0", - "dev": true, - "peer": true - } - } - }, - "stylis": { - "version": "4.3.1", - "dev": true, - "peer": true - }, - "sucrase": { - "version": "3.35.0", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "glob": "^10.3.10", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" - }, - "dependencies": { - "commander": { - "version": "4.1.1", - "dev": true - } - } - }, - "supports-color": { - "version": "5.5.0", - "requires": { - "has-flag": "^3.0.0" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0" - }, - "svg-parser": { - "version": "2.0.4", - "dev": true - }, - "svgo": { - "version": "3.2.0", - "dev": true, - "requires": { - "@trysound/sax": "0.2.0", - "commander": "^7.2.0", - "css-select": "^5.1.0", - "css-tree": "^2.3.1", - "css-what": "^6.1.0", - "csso": "^5.0.5", - "picocolors": "^1.0.0" - }, - "dependencies": { - "commander": { - "version": "7.2.0", - "dev": true - }, - "css-select": { - "version": "5.1.0", - "dev": true, - "requires": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - } - }, - "dom-serializer": { - "version": "2.0.0", - "dev": true, - "requires": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - } - }, - "domhandler": { - "version": "5.0.3", - "dev": true, - "requires": { - "domelementtype": "^2.3.0" - } - }, - "domutils": { - "version": "3.1.0", - "dev": true, - "requires": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - } - } - } - }, - "swc-loader": { - "version": "0.2.6", - "dev": true, - "requires": { - "@swc/counter": "^0.1.3" - } - }, - "symbol-tree": { - "version": "3.2.4", - "dev": true - }, - "synchronous-promise": { - "version": "2.0.17", - "dev": true - }, - "tailwindcss": { - "version": "3.4.3", - "dev": true, - "requires": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.5.3", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.0", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.21.0", - "lilconfig": "^2.1.0", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.23", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.1", - "postcss-nested": "^6.0.1", - "postcss-selector-parser": "^6.0.11", - "resolve": "^1.22.2", - "sucrase": "^3.32.0" - } - }, - "tapable": { - "version": "2.2.1", - "dev": true - }, - "tar-fs": { - "version": "2.1.1", - "requires": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - }, - "dependencies": { - "chownr": { - "version": "1.1.4" - } - } - }, - "tar-stream": { - "version": "2.2.0", - "requires": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - } - }, - "telejson": { - "version": "7.2.0", - "dev": true, - "requires": { - "memoizerific": "^1.11.3" - } - }, - "terser": { - "version": "5.31.0", - "dev": true, - "requires": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "dependencies": { - "acorn": { - "version": "8.11.3", - "dev": true - }, - "commander": { - "version": "2.20.3", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "dev": true - }, - "source-map-support": { - "version": "0.5.21", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - } - } - }, - "terser-webpack-plugin": { - "version": "5.3.10", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.20", - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.26.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "jest-worker": { - "version": "27.5.1", - "dev": true, - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - } - }, - "supports-color": { - "version": "8.1.1", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "test-exclude": { - "version": "6.0.0", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "dependencies": { - "glob": { - "version": "7.2.3", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "text-table": { - "version": "0.2.0", - "dev": true - }, - "thenify": { - "version": "3.3.1", - "dev": true, - "requires": { - "any-promise": "^1.0.0" - } - }, - "thenify-all": { - "version": "1.6.0", - "dev": true, - "requires": { - "thenify": ">= 3.1.0 < 4" - } - }, - "through": { - "version": "2.3.8" - }, - "thunky": { - "version": "1.1.0", - "dev": true - }, - "timers-ext": { - "version": "0.1.7", - "requires": { - "es5-ext": "~0.10.46", - "next-tick": "1" - } - }, - "tiny-invariant": { - "version": "1.3.3", - "dev": true - }, - "tldts": { - "version": "6.1.18", - "requires": { - "tldts-core": "^6.1.18" - } - }, - "tldts-core": { - "version": "6.1.18" - }, - "tmpl": { - "version": "1.0.5", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "tocbot": { - "version": "4.27.16", - "dev": true - }, - "toggle-selection": { - "version": "1.0.6" - }, - "toidentifier": { - "version": "1.0.1", - "dev": true - }, - "tough-cookie": { - "version": "4.1.4", - "dev": true, - "requires": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - }, - "dependencies": { - "universalify": { - "version": "0.2.0", - "dev": true - } - } - }, - "tr46": { - "version": "3.0.0", - "dev": true, - "requires": { - "punycode": "^2.1.1" - } - }, - "ts-dedent": { - "version": "2.2.0", - "dev": true - }, - "ts-interface-checker": { - "version": "0.1.13", - "dev": true - }, - "ts-loader": { - "version": "9.5.1", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "enhanced-resolve": "^5.0.0", - "micromatch": "^4.0.0", - "semver": "^7.3.4", - "source-map": "^0.7.4" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "lru-cache": { - "version": "6.0.0", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.6.0", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "dev": true - } - } - }, - "tsc-watch": { - "version": "6.2.0", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "node-cleanup": "^2.1.2", - "ps-tree": "^1.2.0", - "string-argv": "^0.3.1" - } - }, - "tsconfig-paths": { - "version": "3.15.0", - "dev": true, - "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "json5": { - "version": "1.0.2", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "dev": true - } - } - }, - "tslib": { - "version": "2.6.2" - }, - "tsutils": { - "version": "3.21.0", - "dev": true, - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "dev": true - } - } - }, - "type": { - "version": "2.7.2" - }, - "type-check": { - "version": "0.4.0", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-detect": { - "version": "4.0.8", - "dev": true - }, - "type-fest": { - "version": "2.19.0", - "dev": true - }, - "type-is": { - "version": "1.6.18", - "dev": true, - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "typed-array-buffer": { - "version": "1.0.2", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" - } - }, - "typed-array-byte-length": { - "version": "1.0.1", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - } - }, - "typed-array-byte-offset": { - "version": "1.0.2", - "dev": true, - "requires": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - } - }, - "typed-array-length": { - "version": "1.0.6", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" - } - }, - "typescript": { - "version": "5.4.5", - "devOptional": true - }, - "uglify-js": { - "version": "3.17.4", - "dev": true, - "optional": true - }, - "unbox-primitive": { - "version": "1.0.2", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - } - }, - "unbzip2-stream": { - "version": "1.4.3", - "requires": { - "buffer": "^5.2.1", - "through": "^2.3.8" - } - }, - "undici-types": { - "version": "5.26.5" - }, - "unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "dev": true - }, - "unicode-match-property-ecmascript": { - "version": "2.0.0", - "dev": true, - "requires": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "dev": true - }, - "unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "dev": true - }, - "unist-util-is": { - "version": "4.1.0", - "dev": true - }, - "unist-util-visit": { - "version": "2.0.3", - "dev": true, - "requires": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0", - "unist-util-visit-parents": "^3.0.0" - } - }, - "unist-util-visit-parents": { - "version": "3.1.1", - "dev": true, - "requires": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0" - } - }, - "universalify": { - "version": "2.0.1" - }, - "unpipe": { - "version": "1.0.0", - "dev": true - }, - "unplugin": { - "version": "1.10.1", - "dev": true, - "requires": { - "acorn": "^8.11.3", - "chokidar": "^3.6.0", - "webpack-sources": "^3.2.3", - "webpack-virtual-modules": "^0.6.1" - }, - "dependencies": { - "acorn": { - "version": "8.11.3", - "dev": true - }, - "webpack-virtual-modules": { - "version": "0.6.1", - "dev": true - } - } - }, - "update-browserslist-db": { - "version": "1.0.13", - "dev": true, - "requires": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - } - }, - "uri-js": { - "version": "4.4.1", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "urijs": { - "version": "1.19.11", - "dev": true - }, - "url": { - "version": "0.11.3", - "dev": true, - "requires": { - "punycode": "^1.4.1", - "qs": "^6.11.2" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "dev": true - } - } - }, - "url-parse": { - "version": "1.5.10", - "dev": true, - "requires": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, - "use-callback-ref": { - "version": "1.3.2", - "dev": true, - "requires": { - "tslib": "^2.0.0" - } - }, - "use-context-selector": { - "version": "1.4.4", - "requires": {} - }, - "use-debounce": { - "version": "9.0.4", - "requires": {} - }, - "use-resize-observer": { - "version": "9.1.0", - "dev": true, - "requires": { - "@juggle/resize-observer": "^3.3.1" - } - }, - "use-sidecar": { - "version": "1.1.2", - "dev": true, - "requires": { - "detect-node-es": "^1.1.0", - "tslib": "^2.0.0" - } - }, - "util": { - "version": "0.12.5", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "which-typed-array": "^1.1.2" - } - }, - "util-deprecate": { - "version": "1.0.2" - }, - "utila": { - "version": "0.4.0", - "dev": true - }, - "utils-merge": { - "version": "1.0.1", - "dev": true - }, - "uuid": { - "version": "9.0.1", - "dev": true - }, - "v8-to-istanbul": { - "version": "9.2.0", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - } - }, - "validate-npm-package-license": { - "version": "3.0.4", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - }, - "dependencies": { - "spdx-expression-parse": { - "version": "3.0.1", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - } - } - }, - "validate.js": { - "version": "0.13.1" - }, - "vary": { - "version": "1.1.2", - "dev": true - }, - "victory": { - "version": "36.9.2", - "requires": { - "victory-area": "^36.9.2", - "victory-axis": "^36.9.2", - "victory-bar": "^36.9.2", - "victory-box-plot": "^36.9.2", - "victory-brush-container": "^36.9.2", - "victory-brush-line": "^36.9.2", - "victory-candlestick": "^36.9.2", - "victory-canvas": "^36.9.2", - "victory-chart": "^36.9.2", - "victory-core": "^36.9.2", - "victory-create-container": "^36.9.2", - "victory-cursor-container": "^36.9.2", - "victory-errorbar": "^36.9.2", - "victory-group": "^36.9.2", - "victory-histogram": "^36.9.2", - "victory-legend": "^36.9.2", - "victory-line": "^36.9.2", - "victory-pie": "^36.9.2", - "victory-polar-axis": "^36.9.2", - "victory-scatter": "^36.9.2", - "victory-selection-container": "^36.9.2", - "victory-shared-events": "^36.9.2", - "victory-stack": "^36.9.2", - "victory-tooltip": "^36.9.2", - "victory-voronoi": "^36.9.2", - "victory-voronoi-container": "^36.9.2", - "victory-zoom-container": "^36.9.2" - } - }, - "victory-area": { - "version": "36.9.2", - "requires": { - "lodash": "^4.17.19", - "victory-core": "^36.9.2", - "victory-vendor": "^36.9.2" - } - }, - "victory-axis": { - "version": "36.9.2", - "requires": { - "lodash": "^4.17.19", - "victory-core": "^36.9.2" - } - }, - "victory-bar": { - "version": "36.9.2", - "requires": { - "lodash": "^4.17.19", - "victory-core": "^36.9.2", - "victory-vendor": "^36.9.2" - } - }, - "victory-box-plot": { - "version": "36.9.2", - "requires": { - "lodash": "^4.17.19", - "victory-core": "^36.9.2", - "victory-vendor": "^36.9.2" - } - }, - "victory-brush-container": { - "version": "36.9.2", - "requires": { - "lodash": "^4.17.19", - "react-fast-compare": "^3.2.0", - "victory-core": "^36.9.2" - } - }, - "victory-brush-line": { - "version": "36.9.2", - "requires": { - "lodash": "^4.17.19", - "react-fast-compare": "^3.2.0", - "victory-core": "^36.9.2" - } - }, - "victory-candlestick": { - "version": "36.9.2", - "requires": { - "lodash": "^4.17.19", - "victory-core": "^36.9.2" - } - }, - "victory-canvas": { - "version": "36.9.2", - "requires": { - "lodash": "^4.17.19", - "victory-bar": "^36.9.2", - "victory-core": "^36.9.2" - } - }, - "victory-chart": { - "version": "36.9.2", - "requires": { - "lodash": "^4.17.19", - "react-fast-compare": "^3.2.0", - "victory-axis": "^36.9.2", - "victory-core": "^36.9.2", - "victory-polar-axis": "^36.9.2", - "victory-shared-events": "^36.9.2" - } - }, - "victory-core": { - "version": "36.9.2", - "requires": { - "lodash": "^4.17.21", - "react-fast-compare": "^3.2.0", - "victory-vendor": "^36.9.2" - } - }, - "victory-create-container": { - "version": "36.9.2", - "requires": { - "lodash": "^4.17.19", - "victory-brush-container": "^36.9.2", - "victory-core": "^36.9.2", - "victory-cursor-container": "^36.9.2", - "victory-selection-container": "^36.9.2", - "victory-voronoi-container": "^36.9.2", - "victory-zoom-container": "^36.9.2" - } - }, - "victory-cursor-container": { - "version": "36.9.2", - "requires": { - "lodash": "^4.17.19", - "victory-core": "^36.9.2" - } - }, - "victory-errorbar": { - "version": "36.9.2", - "requires": { - "lodash": "^4.17.19", - "victory-core": "^36.9.2" - } - }, - "victory-group": { - "version": "36.9.2", - "requires": { - "lodash": "^4.17.19", - "react-fast-compare": "^3.2.0", - "victory-core": "^36.9.2", - "victory-shared-events": "^36.9.2" - } - }, - "victory-histogram": { - "version": "36.9.2", - "requires": { - "lodash": "^4.17.19", - "react-fast-compare": "^3.2.0", - "victory-bar": "^36.9.2", - "victory-core": "^36.9.2", - "victory-vendor": "^36.9.2" - } - }, - "victory-legend": { - "version": "36.9.2", - "requires": { - "lodash": "^4.17.19", - "victory-core": "^36.9.2" - } - }, - "victory-line": { - "version": "36.9.2", - "requires": { - "lodash": "^4.17.19", - "victory-core": "^36.9.2", - "victory-vendor": "^36.9.2" - } - }, - "victory-pie": { - "version": "36.9.2", - "requires": { - "lodash": "^4.17.19", - "victory-core": "^36.9.2", - "victory-vendor": "^36.9.2" - } - }, - "victory-polar-axis": { - "version": "36.9.2", - "requires": { - "lodash": "^4.17.19", - "victory-core": "^36.9.2" - } - }, - "victory-scatter": { - "version": "36.9.2", - "requires": { - "lodash": "^4.17.19", - "victory-core": "^36.9.2" - } - }, - "victory-selection-container": { - "version": "36.9.2", - "requires": { - "lodash": "^4.17.19", - "victory-core": "^36.9.2" - } - }, - "victory-shared-events": { - "version": "36.9.2", - "requires": { - "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.19", - "react-fast-compare": "^3.2.0", - "victory-core": "^36.9.2" - } - }, - "victory-stack": { - "version": "36.9.2", - "requires": { - "lodash": "^4.17.19", - "react-fast-compare": "^3.2.0", - "victory-core": "^36.9.2", - "victory-shared-events": "^36.9.2" - } - }, - "victory-tooltip": { - "version": "36.9.2", - "requires": { - "lodash": "^4.17.19", - "victory-core": "^36.9.2" - } - }, - "victory-vendor": { - "version": "36.9.2", - "requires": { - "@types/d3-array": "^3.0.3", - "@types/d3-ease": "^3.0.0", - "@types/d3-interpolate": "^3.0.1", - "@types/d3-scale": "^4.0.2", - "@types/d3-shape": "^3.1.0", - "@types/d3-time": "^3.0.0", - "@types/d3-timer": "^3.0.0", - "d3-array": "^3.1.6", - "d3-ease": "^3.0.1", - "d3-interpolate": "^3.0.1", - "d3-scale": "^4.0.2", - "d3-shape": "^3.1.0", - "d3-time": "^3.0.0", - "d3-timer": "^3.0.1" - } - }, - "victory-voronoi": { - "version": "36.9.2", - "requires": { - "d3-voronoi": "^1.1.4", - "lodash": "^4.17.19", - "victory-core": "^36.9.2" - } - }, - "victory-voronoi-container": { - "version": "36.9.2", - "requires": { - "delaunay-find": "0.0.6", - "lodash": "^4.17.19", - "react-fast-compare": "^3.2.0", - "victory-core": "^36.9.2", - "victory-tooltip": "^36.9.2" - } - }, - "victory-zoom-container": { - "version": "36.9.2", - "requires": { - "lodash": "^4.17.19", - "victory-core": "^36.9.2" - } - }, - "w3c-xmlserializer": { - "version": "4.0.0", - "dev": true, - "requires": { - "xml-name-validator": "^4.0.0" - } - }, - "walker": { - "version": "1.0.8", - "dev": true, - "requires": { - "makeerror": "1.0.12" - } - }, - "wappalyzer": { - "version": "6.10.66", - "requires": { - "puppeteer": "~19.7.0" - }, - "dependencies": { - "argparse": { - "version": "2.0.1" - }, - "brace-expansion": { - "version": "2.0.1", - "requires": { - "balanced-match": "^1.0.0" - } - }, - "chromium-bidi": { - "version": "0.4.5", - "requires": { - "mitt": "3.0.0" - } - }, - "cosmiconfig": { - "version": "8.1.0", - "requires": { - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0" - } - }, - "cross-fetch": { - "version": "3.1.5", - "requires": { - "node-fetch": "2.6.7" - } - }, - "devtools-protocol": { - "version": "0.0.1094867" - }, - "extract-zip": { - "version": "2.0.1", - "requires": { - "@types/yauzl": "^2.9.1", - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - } - }, - "get-stream": { - "version": "5.2.0", - "requires": { - "pump": "^3.0.0" - } - }, - "glob": { - "version": "9.3.5", - "requires": { - "fs.realpath": "^1.0.0", - "minimatch": "^8.0.2", - "minipass": "^4.2.4", - "path-scurry": "^1.6.1" - } - }, - "js-yaml": { - "version": "4.1.0", - "requires": { - "argparse": "^2.0.1" - } - }, - "minimatch": { - "version": "8.0.4", - "requires": { - "brace-expansion": "^2.0.1" - } - }, - "minipass": { - "version": "4.2.8" - }, - "mitt": { - "version": "3.0.0" - }, - "node-fetch": { - "version": "2.6.7", - "requires": { - "whatwg-url": "^5.0.0" - } - }, - "puppeteer": { - "version": "19.7.5", - "requires": { - "cosmiconfig": "8.1.0", - "https-proxy-agent": "5.0.1", - "progress": "2.0.3", - "proxy-from-env": "1.1.0", - "puppeteer-core": "19.7.5" - } - }, - "puppeteer-core": { - "version": "19.7.5", - "requires": { - "chromium-bidi": "0.4.5", - "cross-fetch": "3.1.5", - "debug": "4.3.4", - "devtools-protocol": "0.0.1094867", - "extract-zip": "2.0.1", - "https-proxy-agent": "5.0.1", - "proxy-from-env": "1.1.0", - "rimraf": "4.4.0", - "tar-fs": "2.1.1", - "unbzip2-stream": "1.4.3", - "ws": "8.12.1" - } - }, - "rimraf": { - "version": "4.4.0", - "requires": { - "glob": "^9.2.0" - } - }, - "tr46": { - "version": "0.0.3" - }, - "webidl-conversions": { - "version": "3.0.1" - }, - "whatwg-url": { - "version": "5.0.0", - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "ws": { - "version": "8.12.1", - "requires": {} - } - } - }, - "watchpack": { - "version": "2.4.1", - "dev": true, - "requires": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - } - }, - "wbuf": { - "version": "1.7.3", - "dev": true, - "requires": { - "minimalistic-assert": "^1.0.0" - } - }, - "webidl-conversions": { - "version": "7.0.0", - "dev": true - }, - "webpack": { - "version": "5.91.0", - "dev": true, - "requires": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.5", - "@webassemblyjs/ast": "^1.12.1", - "@webassemblyjs/wasm-edit": "^1.12.1", - "@webassemblyjs/wasm-parser": "^1.12.1", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", - "browserslist": "^4.21.10", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.16.0", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.10", - "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" - }, - "dependencies": { - "@types/estree": { - "version": "1.0.5", - "dev": true - }, - "acorn": { - "version": "8.11.3", - "dev": true - }, - "acorn-import-assertions": { - "version": "1.9.0", - "dev": true, - "requires": {} - } - } - }, - "webpack-cli": { - "version": "5.1.4", - "dev": true, - "requires": { - "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^2.1.1", - "@webpack-cli/info": "^2.0.2", - "@webpack-cli/serve": "^2.0.5", - "colorette": "^2.0.14", - "commander": "^10.0.1", - "cross-spawn": "^7.0.3", - "envinfo": "^7.7.3", - "fastest-levenshtein": "^1.0.12", - "import-local": "^3.0.2", - "interpret": "^3.1.1", - "rechoir": "^0.8.0", - "webpack-merge": "^5.7.3" - }, - "dependencies": { - "commander": { - "version": "10.0.1", - "dev": true - } - } - }, - "webpack-dev-middleware": { - "version": "6.1.3", - "dev": true, - "requires": { - "colorette": "^2.0.10", - "memfs": "^3.4.12", - "mime-types": "^2.1.31", - "range-parser": "^1.2.1", - "schema-utils": "^4.0.0" - }, - "dependencies": { - "ajv": { - "version": "8.13.0", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.3", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" - } - }, - "ajv-keywords": { - "version": "5.1.0", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.3" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "dev": true - }, - "schema-utils": { - "version": "4.2.0", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - } - } - } - }, - "webpack-dev-server": { - "version": "4.15.2", - "dev": true, - "requires": { - "@types/bonjour": "^3.5.9", - "@types/connect-history-api-fallback": "^1.3.5", - "@types/express": "^4.17.13", - "@types/serve-index": "^1.9.1", - "@types/serve-static": "^1.13.10", - "@types/sockjs": "^0.3.33", - "@types/ws": "^8.5.5", - "ansi-html-community": "^0.0.8", - "bonjour-service": "^1.0.11", - "chokidar": "^3.5.3", - "colorette": "^2.0.10", - "compression": "^1.7.4", - "connect-history-api-fallback": "^2.0.0", - "default-gateway": "^6.0.3", - "express": "^4.17.3", - "graceful-fs": "^4.2.6", - "html-entities": "^2.3.2", - "http-proxy-middleware": "^2.0.3", - "ipaddr.js": "^2.0.1", - "launch-editor": "^2.6.0", - "open": "^8.0.9", - "p-retry": "^4.5.0", - "rimraf": "^3.0.2", - "schema-utils": "^4.0.0", - "selfsigned": "^2.1.1", - "serve-index": "^1.9.1", - "sockjs": "^0.3.24", - "spdy": "^4.0.2", - "webpack-dev-middleware": "^5.3.4", - "ws": "^8.13.0" - }, - "dependencies": { - "ajv": { - "version": "8.13.0", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.3", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" - } - }, - "ajv-keywords": { - "version": "5.1.0", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.3" - } - }, - "glob": { - "version": "7.2.3", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "ipaddr.js": { - "version": "2.2.0", - "dev": true - }, - "json-schema-traverse": { - "version": "1.0.0", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "schema-utils": { - "version": "4.2.0", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - } - }, - "webpack-dev-middleware": { - "version": "5.3.4", - "dev": true, - "requires": { - "colorette": "^2.0.10", - "memfs": "^3.4.3", - "mime-types": "^2.1.31", - "range-parser": "^1.2.1", - "schema-utils": "^4.0.0" - } - } - } - }, - "webpack-hot-middleware": { - "version": "2.26.1", - "dev": true, - "requires": { - "ansi-html-community": "0.0.8", - "html-entities": "^2.1.0", - "strip-ansi": "^6.0.0" - } - }, - "webpack-merge": { - "version": "5.10.0", - "dev": true, - "requires": { - "clone-deep": "^4.0.1", - "flat": "^5.0.2", - "wildcard": "^2.0.0" - } - }, - "webpack-sources": { - "version": "3.2.3", - "dev": true - }, - "webpack-virtual-modules": { - "version": "0.5.0", - "dev": true - }, - "webpackbar": { - "version": "5.0.2", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "consola": "^2.15.3", - "pretty-time": "^1.1.0", - "std-env": "^3.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "consola": { - "version": "2.15.3", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "websocket-driver": { - "version": "0.7.4", - "dev": true, - "requires": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - } - }, - "websocket-extensions": { - "version": "0.1.4", - "dev": true - }, - "whatwg-encoding": { - "version": "2.0.0", - "dev": true, - "requires": { - "iconv-lite": "0.6.3" - }, - "dependencies": { - "iconv-lite": { - "version": "0.6.3", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - } - } - }, - "whatwg-mimetype": { - "version": "3.0.0", - "dev": true - }, - "whatwg-url": { - "version": "11.0.0", - "dev": true, - "requires": { - "tr46": "^3.0.0", - "webidl-conversions": "^7.0.0" - } - }, - "which": { - "version": "2.0.2", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-boxed-primitive": { - "version": "1.0.2", - "dev": true, - "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - } - }, - "which-builtin-type": { - "version": "1.1.3", - "dev": true, - "requires": { - "function.prototype.name": "^1.1.5", - "has-tostringtag": "^1.0.0", - "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", - "is-finalizationregistry": "^1.0.2", - "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" - } - }, - "which-collection": { - "version": "1.0.2", - "dev": true, - "requires": { - "is-map": "^2.0.3", - "is-set": "^2.0.3", - "is-weakmap": "^2.0.2", - "is-weakset": "^2.0.3" - } - }, - "which-typed-array": { - "version": "1.1.15", - "dev": true, - "requires": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.2" - } - }, - "wildcard": { - "version": "2.0.1", - "dev": true - }, - "word-wrap": { - "version": "1.2.5", - "dev": true - }, - "wordwrap": { - "version": "1.0.0", - "dev": true - }, - "wrap-ansi": { - "version": "8.1.0", - "dev": true, - "requires": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "6.0.1", - "dev": true - }, - "ansi-styles": { - "version": "6.2.1", - "dev": true - }, - "strip-ansi": { - "version": "7.1.0", - "dev": true, - "requires": { - "ansi-regex": "^6.0.1" - } - } - } - }, - "wrap-ansi-cjs": { - "version": "npm:wrap-ansi@7.0.0", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "emoji-regex": { - "version": "8.0.0", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "dev": true - }, - "string-width": { - "version": "4.2.3", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - } - } - }, - "wrappy": { - "version": "1.0.2" - }, - "write-file-atomic": { - "version": "4.0.2", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "dependencies": { - "signal-exit": { - "version": "3.0.7", - "dev": true - } - } - }, - "ws": { - "version": "8.17.0", - "dev": true, - "requires": {} - }, - "xml-name-validator": { - "version": "4.0.0", - "dev": true - }, - "xml2js": { - "version": "0.5.0", - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - } - }, - "xmlbuilder": { - "version": "11.0.1" - }, - "xmlchars": { - "version": "2.2.0", - "dev": true - }, - "y18n": { - "version": "5.0.8" - }, - "yallist": { - "version": "3.1.1", - "dev": true - }, - "yaml": { - "version": "2.3.1", - "dev": true - }, - "yargs": { - "version": "17.7.2", - "dev": true, - "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "dependencies": { - "emoji-regex": { - "version": "8.0.0", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "dev": true - }, - "string-width": { - "version": "4.2.3", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - } - } - }, - "yargs-parser": { - "version": "21.1.1" - }, - "yauzl": { - "version": "2.10.0", - "requires": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - }, - "yocto-queue": { - "version": "0.1.0" - } } } diff --git a/package.json b/package.json index c8b8e1238..57cb6d15e 100644 --- a/package.json +++ b/package.json @@ -1,34 +1,56 @@ { "name": "ps-analysis-tool", - "version": "0.8.0", + "version": "0.9.0", "description": "Cookie Analysis Tool and CLI for analysis and understanding of cookie usage on web pages.", "scripts": { - "cli:prebuild": "node ./scripts/delete-build-artifacts.cjs", - "cli:dev": "tsc-watch --build", - "cli:build": "npm run cli:prebuild && tsc --build", - "cli-dashboard:dev": "webpack serve --config dashboard.webpack.config.cjs", - "cli-dashboard:build": "rm -rf dist/extension && cross-env NODE_ENV=production webpack --config dashboard.webpack.config.cjs", - "cli": "node dist/cli/index.js", - "test": "jest --config=tests/jest.config.cjs", - "test:coverage": "npm run test -- --collectCoverage && open coverage/lcov-report/index.html", - "dev": "webpack --config extension.webpack.config.cjs --watch", - "build": "rm -rf dist/extension && cross-env NODE_ENV=production webpack --config extension.webpack.config.cjs", - "serve": "webpack serve", + "build-storybook": "storybook build", + "build:all": "npm-run-all **:build ", + "build-cli": "npm run cli-dashboard:build && npm run cli:build", + "publish:all:local": "npm run build:all && npm run publish:local --workspaces", + "unpublish:all:local": "npm run unpublish:local --workspaces", + "publish:all:remote": "npm run build:all && npm run publish:remote --workspaces", + "unpublish:all:remote": "npm run unpublish:remote --workspaces", + "i18n:prebuild": "npm run build:remove -w @google-psat/i18n", + "i18n:build": "npm run i18n:prebuild && npm run build -w @google-psat/i18n", + "common:prebuild": "npm run build:remove -w @google-psat/common", + "common:build": "npm run common:prebuild && npm run build -w @google-psat/common", + "design-system:prebuild": "npm run build:remove -w @google-psat/design-system", + "design-system:build": "npm run design-system:prebuild && npm run build -w @google-psat/design-system", + "library-detection:prebuild": "npm run build:remove -w @google-psat/library-detection", + "library-detection:build": "npm run library-detection:prebuild && npm run build -w @google-psat/library-detection", + "analysis-utils:prebuild": "npm run build:remove -w @google-psat/analysis-utils", + "analysis-utils:dev": "npm run dev -w @google-psat/analysis-utils", + "analysis-utils:build": "npm run analysis-utils:prebuild && npm run build -w @google-psat/analysis-utils", + "cli:dev": "npm run dev -w @google-psat/cli", + "cli:prebuild": "npm run build:remove -w @google-psat/cli", + "cli:build": "cross-env NODE_ENV=production npm run build -w @google-psat/cli", + "cli:start": "npm install && npm run cli:dev", + "cli": "node packages/cli/dist/main.js", + "cli-dashboard:dev": "npm run start -w @google-psat/cli-dashboard", + "cli-dashboard:prebuild": "npm run build:remove -w @google-psat/cli-dashboard", + "cli-dashboard:build": "npm run cli-dashboard:prebuild && cross-env NODE_ENV=production npm run build -w @google-psat/cli-dashboard", + "cookie-db:update": "node scripts/update-cookie-db.cjs", + "ext:dev": "npm run dev -w @google-psat/extension", + "ext:prebuild": "npm run build:remove -w @google-psat/extension", + "ext:build": "npm run ext:prebuild && cross-env NODE_ENV=production npm run build -w @google-psat/extension", + "ext:start": "npm install && npm run ext:dev", "lint": "npm-run-all --parallel lint:*", "lint:js": "eslint .", "lint:types": "tsc ", "lint:js:fix": "eslint --fix .", + "local-registry:start": "bash ./bin/setup-local-registry.sh", + "local-registry:stop": "bash ./bin/stop-local-registry.sh", + "merge-i18n-messages": "node packages/i18n/scripts/merge-messages.cjs", "prepare": "husky install", - "cookie-db:update": "node scripts/update-cookie-db.cjs", "rws-json:update": "node scripts/update-rws-json.cjs", - "merge-i18n-messages": "node packages/i18n/scripts/merge-messages.cjs", "storybook": "storybook dev -p 6006", - "build-storybook": "storybook build", - "start": "npm install && npm run dev" + "test": "jest --config=tests/jest.config.cjs", + "test:coverage": "npm run test -- --collectCoverage && open coverage/lcov-report/index.html", + "serve": "webpack serve" }, "repository": { "type": "git", - "url": "git+https://github.com/GoogleChromeLabs/ps-analysis-tool" + "url": "git+https://github.com/GoogleChromeLabs/ps-analysis-tool.git" }, "license": "Apache-2.0", "bugs": { @@ -40,7 +62,7 @@ "@babel/preset-env": "^7.22.5", "@babel/preset-react": "^7.22.5", "@babel/preset-typescript": "^7.22.5", - "@ps-analysis-tool/eslint-import-resolver": "*", + "@google-psat/eslint-import-resolver": "*", "@storybook/addon-essentials": "^7.0.27", "@storybook/addon-interactions": "^7.0.27", "@storybook/addon-links": "^7.0.27", @@ -104,6 +126,7 @@ "webpack": "^5.86.0", "webpack-cli": "^5.1.4", "webpack-dev-server": "^4.15.1", + "webpack-node-externals": "^3.0.0", "webpackbar": "^5.0.2" }, "lint-staged": { @@ -120,6 +143,9 @@ "packages/*" ], "overrides": { - "xml2js": "^0.5.0" + "xml2js": "^0.5.0", + "wappalyzer": { + "puppeteer": "^22.11.1" + } } } diff --git a/packages/analysis-utils/.eslintrc.json b/packages/analysis-utils/.eslintrc.json new file mode 100644 index 000000000..1d126b36b --- /dev/null +++ b/packages/analysis-utils/.eslintrc.json @@ -0,0 +1,7 @@ +{ + "rules": { + "no-console": "off", + "no-await-in-loop": "off" + }, + "ignorePatterns": ["dist/**", "dist-types/**"] +} diff --git a/packages/analysis-utils/.npmignore b/packages/analysis-utils/.npmignore new file mode 100644 index 000000000..aa8e45f12 --- /dev/null +++ b/packages/analysis-utils/.npmignore @@ -0,0 +1 @@ +src/ \ No newline at end of file diff --git a/packages/analysis-utils/README.md b/packages/analysis-utils/README.md new file mode 100644 index 000000000..aeb8dbfb6 --- /dev/null +++ b/packages/analysis-utils/README.md @@ -0,0 +1,4 @@ +# analysis-utils + +## Description +A package for managing puppeteer for various site analysis \ No newline at end of file diff --git a/packages/analysis-utils/package.json b/packages/analysis-utils/package.json new file mode 100644 index 000000000..6a515d7b9 --- /dev/null +++ b/packages/analysis-utils/package.json @@ -0,0 +1,49 @@ +{ + "name": "@google-psat/analysis-utils", + "version": "0.9.0-2", + "description": "A package for CLI analysis", + "main": "dist/index.js", + "types": "dist-types/index.d.ts", + "source": "src/index.ts", + "author": { + "name": "Google" + }, + "customExports": { + ".": { + "default": "./src/index.ts" + } + }, + "scripts": { + "build": "tsc", + "dev": "tsc-watch", + "build:remove": "rimraf dist dist-types tsconfig.tsbuildinfo", + "publish:local": "npm publish --registry=http://localhost:4873", + "publish:remote": "npm publish --access=public --registry=https://registry.npmjs.org", + "unpublish:local": "npm unpublish --registry=http://localhost:4873 --force", + "unpublish:remote": "npm unpublish --registry=https://registry.npmjs.org --force" + }, + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "https://github.com/GoogleChromeLabs/ps-analysis-tool", + "directory": "packages/analysis-utils" + }, + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/GoogleChromeLabs/ps-analysis-tool/issues" + }, + "homepage": "https://github.com/GoogleChromeLabs/ps-analysis-tool", + "dependencies": { + "@google-psat/common": "*", + "puppeteer": "^22.10.0", + "simple-cookie": "^1.0.15", + "tldts": "^6.0.14", + "wappalyzer": "^6.10.66" + }, + "devDependencies": { + "tsc-watch": "^6.0.4", + "typescript": "^5.0.4" + } +} diff --git a/packages/analysis-utils/src/browserManagement/collateCookieData.ts b/packages/analysis-utils/src/browserManagement/collateCookieData.ts new file mode 100644 index 000000000..7fc568697 --- /dev/null +++ b/packages/analysis-utils/src/browserManagement/collateCookieData.ts @@ -0,0 +1,55 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Internal dependencies. + */ +import { CookieDataFromNetwork } from './types'; + +/** + * Collate network and JS cookie data. + * @param cookieDataFromNetwork Cookie data from network. + * @param cookieDataFromJS cookie data from JS. + * @returns cookieDataFromNetwork Cookie data from network and JS if it exits. + */ +export default function collateCookieData( + cookieDataFromNetwork: CookieDataFromNetwork, + cookieDataFromJS: CookieDataFromNetwork +): CookieDataFromNetwork { + Object.entries(cookieDataFromJS).forEach(([frameUrl, { frameCookies }]) => { + Object.entries(frameCookies).forEach(([key, cookie]) => { + // Check if the frame's data exists. if not create frame data object + if (!cookieDataFromNetwork[frameUrl]) { + cookieDataFromNetwork[frameUrl] = { + frameCookies: { + [key]: { + parsedCookie: cookie.parsedCookie, + headerType: 'javascript', + }, + }, + }; + } + // Check if the cookie's data exists. if not add cookie data object to frame cookies. Otherwise cookie data from network stream will be used. + else if (!cookieDataFromNetwork[frameUrl].frameCookies[key]) { + cookieDataFromNetwork[frameUrl].frameCookies[key] = { + parsedCookie: cookie.parsedCookie, + headerType: 'javascript', + }; + } + }); + }); + + return cookieDataFromNetwork; +} diff --git a/packages/analysis-utils/src/browserManagement/index.ts b/packages/analysis-utils/src/browserManagement/index.ts new file mode 100644 index 000000000..45880fe81 --- /dev/null +++ b/packages/analysis-utils/src/browserManagement/index.ts @@ -0,0 +1,728 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * External dependencies. + */ +import puppeteer, { Browser, HTTPResponse, Page, Protocol } from 'puppeteer'; +import { parse } from 'simple-cookie'; +import { + type CookieData, + type ScriptTagUnderCheck, + type LibraryData, + type LibraryMatchers, + resolveWithTimeout, + delay, + RESPONSE_EVENT, + REQUEST_EVENT, +} from '@google-psat/common'; + +/** + * Internal dependencies. + */ +import { + ResponseData, + RequestData, + ViewportConfig, + CookieStoreCookie, + CookieDataFromNetwork, +} from './types'; +import { parseNetworkDataToCookieData } from './parseNetworkDataToCookieData'; +import collateCookieData from './collateCookieData'; + +export class BrowserManagement { + viewportConfig: ViewportConfig; + browser: Browser | null; + isHeadless: boolean; + pageWaitTime: number; + pages: Record; + pageFrames: Record>; + pageResponses: Record>; + pageRequests: Record>; + pageResourcesMaps: Record>; + shouldLogDebug: boolean; + + constructor( + viewportConfig: ViewportConfig, + isHeadless: boolean, + pageWaitTime: number, + shouldLogDebug: boolean + ) { + this.viewportConfig = viewportConfig; + this.browser = null; + this.isHeadless = isHeadless; + this.pageWaitTime = pageWaitTime; + this.pages = {}; + this.pageFrames = {}; + this.pageResponses = {}; + this.pageRequests = {}; + this.shouldLogDebug = shouldLogDebug; + this.pageResourcesMaps = {}; + } + + debugLog(msg: any) { + if (this.shouldLogDebug) { + console.log(msg); + } + } + + async initializeBrowser(enable3pCookiePhaseout: boolean) { + const args: string[] = []; + + if (enable3pCookiePhaseout) { + args.push('--test-third-party-cookie-phaseout'); + args.push( + '--enable-features="FirstPartySets,StorageAccessAPI,StorageAccessAPIForOriginExtension,PageInfoCookiesSubpage,PrivacySandboxFirstPartySetsUI,TpcdMetadataGrants,TpcdSupportSettings,TpcdHeuristicsGrants:TpcdReadHeuristicsGrants/true/TpcdWritePopupCurrentInteractionHeuristicsGrants/30d/TpcdBackfillPopupHeuristicsGrants/30d/TpcdPopupHeuristicEnableForIframeInitiator/all/TpcdWriteRedirectHeuristicGrants/15m/TpcdRedirectHeuristicRequireABAFlow/true/TpcdRedirectHeuristicRequireCurrentInteraction/true"' + ); + } + + this.browser = await puppeteer.launch({ + devtools: true, + headless: this.isHeadless, + args, + }); + this.debugLog('browser intialized'); + } + + async clickOnAcceptBanner(url: string) { + const page = this.pages[url]; + + if (!page) { + throw new Error('no page with the provided id was found'); + } + + await page.evaluate(() => { + const bannerNodes: Element[] = Array.from( + (document.querySelector('body')?.childNodes || []) as Element[] + ) + .filter((node: Element) => node && node?.tagName === 'DIV') + .filter((node) => { + if (!node || !node?.textContent) { + return false; + } + const regex = + /\b(consent|policy|cookie policy|privacy policy|personalize|preferences)\b/; + + return regex.test(node.textContent.toLowerCase()); + }); + + const buttonToClick: HTMLButtonElement[] = bannerNodes + .map((node: Element) => { + const buttonNodes = Array.from(node.getElementsByTagName('button')); + const isButtonForAccept = buttonNodes.filter( + (cnode) => + cnode.textContent && + (cnode.textContent.toLowerCase().includes('accept') || + cnode.textContent.toLowerCase().includes('allow') || + cnode.textContent.toLowerCase().includes('agree')) + ); + + return isButtonForAccept[0]; + }) + .filter((button) => button); + buttonToClick[0]?.click(); + }); + + await delay(this.pageWaitTime / 2); + } + + async openPage(): Promise { + if (!this.browser) { + throw new Error('Browser not intialized'); + } + const sitePage = await this.browser.newPage(); + + await sitePage.setUserAgent( + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36' + ); + + sitePage.setViewport({ + width: 1440, + height: 790, + deviceScaleFactor: 1, + }); + + this.debugLog('Page opened'); + + return sitePage; + } + + async navigateToPage(url: string) { + const page = this.pages[url]; + + if (!page) { + throw new Error('no page with the provided id was found'); + } + + this.debugLog(`starting navigation to url ${url}`); + + try { + await page.goto(url, { timeout: 10000 }); + this.debugLog(`done with navigation to url:${url}`); + } catch (error) { + this.debugLog( + `navigation did not finish in 10 seconds moving on to scrolling` + ); + //ignore + } + } + + async pageScroll(url: string) { + const page = this.pages[url]; + + if (!page) { + throw new Error('no page with the provided id was found'); + } + + try { + await page.evaluate(() => { + window.scrollBy(0, 10000); + }); + } catch (error) { + this.debugLog(`scrolling the page to the end.`); + //ignore + } + + this.debugLog(`scrolling on url:${url}`); + } + + responseEventListener(pageId: string, response: HTTPResponse) { + if ( + response?.headers()?.['content-type']?.includes('javascript') || + response?.headers()?.['content-type']?.includes('html') + ) { + response + .text() + .then((content) => { + this.pageResourcesMaps[pageId][response.url()] = { + origin: response.url(), + type: response?.headers()?.['content-type']?.includes('javascript') + ? 'Script' + : 'Document', + content, + }; + }) + .catch(() => undefined); + } + } + + responseReceivedListener( + pageId: string, + { requestId, frameId, response }: Protocol.Network.ResponseReceivedEvent + ) { + if (!this.pageResponses[pageId][requestId]) { + this.pageResponses[pageId][requestId] = { + frameId, + url: response.url, + cookies: [], + }; + } else { + const parsedCookies = this.pageResponses[pageId][requestId]?.cookies.map( + (cookie) => { + if (!cookie.url) { + cookie.url = response.url; + } + + if (!cookie.parsedCookie.domain) { + cookie.parsedCookie.domain = new URL(response.url).hostname; + } + if (cookie.parsedCookie.domain[0] !== '.') { + cookie.parsedCookie.domain = '.' + cookie.parsedCookie.domain; + } + return cookie; + } + ); + + this.pageResponses[pageId][requestId]?.cookies.forEach((cookie) => { + cookie.networkEvents?.responseEvents.map((event) => { + if (event.requestId === requestId) { + event.url = response.url; + } + return event; + }); + }); + + this.pageResponses[pageId][requestId] = { + frameId, + url: response.url, + cookies: parsedCookies, + }; + } + } + + responseReceivedExtraInfoListener( + pageId: string, + { + headers, + cookiePartitionKey, + blockedCookies, + requestId, + exemptedCookies, + }: Protocol.Network.ResponseReceivedExtraInfoEvent + ) { + const headersToBeParsed = headers['set-cookie'] ?? headers['Set-Cookie']; + if (!headersToBeParsed) { + return; + } + + const cookies: CookieData[] = headersToBeParsed + .split('\n') + .map((headerLine) => { + const parsedCookie = parse(headerLine); + const partitionKey = headerLine.includes('Partitioned') + ? cookiePartitionKey + : undefined; + + const url = this.pageResponses[pageId][requestId]?.url; + + if (!parsedCookie.domain && url) { + parsedCookie.domain = new URL(url).hostname; + } + + if (parsedCookie.domain && parsedCookie.domain[0] !== '.') { + parsedCookie.domain = '.' + parsedCookie.domain; + } + + const exemptedEntry = exemptedCookies?.find(({ cookie }) => { + return cookie?.name === parsedCookie.name; + }); + + const blockedEntry = blockedCookies.find((c) => { + if (c.cookie) { + return ( + c.cookie?.name?.trim() === parsedCookie.name?.trim() && + c.cookie.domain?.trim() === parsedCookie.domain?.trim() && + c.cookie.path?.trim() === parsedCookie.path?.trim() + ); + } else { + const temporaryParsedCookie = parse(c.cookieLine); + + return ( + temporaryParsedCookie.name?.trim() === + parsedCookie.name?.trim() && + temporaryParsedCookie.domain?.trim() === + parsedCookie.domain?.trim() && + temporaryParsedCookie.path?.trim() === parsedCookie.path?.trim() + ); + } + }); + + return { + parsedCookie: { + name: parsedCookie.name, + domain: parsedCookie.domain, + path: parsedCookie.path || '/', + value: parsedCookie.value, + sameSite: parsedCookie.samesite || 'Lax', + expires: parsedCookie.expires || 'Session', + httpOnly: parsedCookie.httponly || false, + secure: parsedCookie.secure || false, + partitionKey, + }, + networkEvents: { + responseEvents: [ + { + type: RESPONSE_EVENT.CDP_RESPONSE_RECEIVED_EXTRA_INFO, + requestId, + url, + blocked: Boolean(blockedEntry), + timeStamp: Date.now(), + }, + ], + requestEvents: [], + }, + isBlocked: Boolean(blockedEntry), + blockedReasons: blockedEntry?.blockedReasons, + exemptionReason: exemptedEntry?.exemptionReason, + url, + headerType: 'response', + }; + }); + + const prevCookies = this.pageResponses[pageId][requestId]?.cookies || []; + const mergedCookies = [...prevCookies, ...(cookies || [])]; + + this.pageResponses[pageId][requestId] = { + frameId: this.pageResponses[pageId][requestId]?.frameId || '', + url: this.pageResponses[pageId][requestId]?.url || '', + // @ts-ignore TODO: fix expires type mismatch + cookies: mergedCookies, + }; + } + + requestWillBeSentListener( + pageId: string, + { requestId, request, frameId }: Protocol.Network.RequestWillBeSentEvent + ) { + this.pageRequests[pageId][requestId] = { + ...(this.pageRequests[pageId][requestId] || {}), + frameId, + url: request.url, + }; + } + + requestWillBeSentExtraInfoListener( + pageId: string, + { + associatedCookies, + requestId, + }: Protocol.Network.RequestWillBeSentExtraInfoEvent + ) { + if (!associatedCookies || associatedCookies.length === 0) { + return; + } + + const cookies = associatedCookies.map((associatedCookie) => { + return { + parsedCookie: { + name: associatedCookie.cookie.name, + domain: associatedCookie.cookie.domain, + path: associatedCookie.cookie.path || '/', + value: associatedCookie.cookie.value, + sameSite: associatedCookie.cookie.sameSite || 'Lax', + expires: associatedCookie.cookie.expires || 'Session', + httpOnly: associatedCookie.cookie.httpOnly || false, + secure: associatedCookie.cookie.secure || false, + partitionKey: associatedCookie.cookie.partitionKey, + }, + networkEvents: { + requestEvents: [ + { + type: REQUEST_EVENT.CDP_REQUEST_WILL_BE_SENT_EXTRA_INFO, + requestId, + url: this.pageRequests[pageId][requestId]?.url || '', + blocked: associatedCookie.blockedReasons.length > 0, + timeStamp: Date.now(), + }, + ], + responseEvents: [], + }, + isBlocked: associatedCookie.blockedReasons.length > 0, + blockedReasons: associatedCookie.blockedReasons, + exemptionReason: associatedCookie?.exemptionReason, + url: this.pageRequests[pageId][requestId]?.url || '', + headerType: 'request', + }; + }); + + this.pageRequests[pageId][requestId] = { + ...(this.pageRequests[pageId][requestId] || {}), + cookies, + }; + } + + pageFrameAttachedListener( + pageId: string, + { frameId, parentFrameId }: Protocol.Page.FrameAttachedEvent + ) { + if (!this.pageFrames[pageId]) { + this.pageFrames[pageId] = {}; + } + + this.pageFrames[pageId][frameId] = parentFrameId; + } + + async getMainframeIds() { + const pageTargetIds: Record = {}; + + // This gets targets for all pages. + const cdpSession = await Object.values(this.pages)[0].createCDPSession(); + const res = await cdpSession.send('Target.getTargets'); + + for (const [_url, page] of Object.entries(this.pages)) { + const constructedUrl = page.url(); + + const mainFrameTargetId = res.targetInfos.find( + ({ url, type }) => constructedUrl === url && type === 'page' + )?.targetId; + + pageTargetIds[_url] = mainFrameTargetId || ''; + } + return pageTargetIds; + } + + async attachListenersToPage(pageId: string) { + const page = this.pages[pageId]; + + if (!page) { + throw new Error(`no page with the provided id was found:${pageId}`); + } + + const cdpSession = await page.createCDPSession(); + + await cdpSession.send('Target.setAutoAttach', { + // If this is set to true, debugger will be attached to every new target that is added to the current target. + autoAttach: true, + waitForDebuggerOnStart: false, + //Enables "flat" access to the session via specifying sessionId attribute in the commands. + // If this is set to true the debugger is also attached to the child targets of that the target it has been attached to. + flatten: true, + }); + + await cdpSession.send('Network.enable'); + await cdpSession.send('Page.enable'); + + this.pageRequests[pageId] = {}; + this.pageResponses[pageId] = {}; + this.pageResourcesMaps[pageId] = {}; + + page.on('response', (ev) => this.responseEventListener(pageId, ev)); + + cdpSession.on('Network.responseReceived', (ev) => + this.responseReceivedListener(pageId, ev) + ); + + cdpSession.on('Network.responseReceivedExtraInfo', (ev) => + this.responseReceivedExtraInfoListener(pageId, ev) + ); + + cdpSession.on('Network.requestWillBeSent', (ev) => + this.requestWillBeSentListener(pageId, ev) + ); + + cdpSession.on('Network.requestWillBeSentExtraInfo', (ev) => + this.requestWillBeSentExtraInfoListener(pageId, ev) + ); + + cdpSession.on('Page.frameAttached', (ev) => + this.pageFrameAttachedListener(pageId, ev) + ); + + this.debugLog('done attaching network event listeners'); + } + + async getJSCookies(page: Page) { + const frames = page.frames(); + + const cookies: CookieDataFromNetwork = {}; + + await Promise.all( + frames.map(async (frame) => { + if (!frame.url().includes('http')) { + return; + } + + const _JSCookies: CookieStoreCookie[] = await resolveWithTimeout( + frame.evaluate(() => { + // @ts-ignore + return cookieStore.getAll(); + }), + [], + 200 + ); + + const frameCookies: { + [key: string]: CookieData; + } = {}; + + _JSCookies.forEach((cookie) => { + if (!cookie.domain) { + cookie.domain = new URL(frame.url()).hostname; + } + if (cookie.domain[0] !== '.') { + cookie.domain = '.' + cookie.domain; + } + const key = cookie.name + ':' + cookie.domain + ':' + cookie.path; + frameCookies[key] = { parsedCookie: cookie }; + }); + + const frameUrl = new URL(frame.url()).origin; + cookies[frameUrl] = { frameCookies }; + }) + ); + + return cookies; + } + + getResources(urls: string[]) { + const allFetchedResources: { [key: string]: any } = {}; + + urls.forEach((url) => { + const page = this.pages[url]; + const resources = this.pageResourcesMaps[url]; + + if (!page || !resources) { + allFetchedResources[url] = []; + return; + } + + const mainFrameUrl = new URL(page.url()).origin; + + allFetchedResources[mainFrameUrl] = Array.from( + Object.values(resources) ?? [] + ); + }); + + return allFetchedResources; + } + + async insertAndRunDOMQueryFunctions( + url: string, + Libraries: LibraryMatchers[] + ) { + const page = this.pages[url]; + + if (!page) { + throw new Error('no page with the provided id was found'); + } + + const domQueryMatches: LibraryData = {}; + + await Promise.all( + Libraries.map(async ({ domQueryFunction, name }) => { + if (domQueryFunction && name) { + await page.addScriptTag({ + content: `window.${name.replaceAll('-', '')} = ${domQueryFunction}`, + }); + + const queryResult = await page.evaluate((library: string) => { + //@ts-ignore + const functionDOMQuery = window[`${library}`]; + + if (!functionDOMQuery) { + return []; + } + + return functionDOMQuery(); + }, name.replaceAll('-', '')); + + domQueryMatches[name] = { + domQuerymatches: queryResult as [string], + }; + } + }) + ); + const mainFrameUrl = new URL(page.url()).origin; + return { [mainFrameUrl]: domQueryMatches }; + } + + async analyzeCookies( + userProvidedUrls: string[], + shouldSkipAcceptBanner: boolean, + Libraries: LibraryMatchers[] + ) { + let consolidatedDOMQueryMatches: { [key: string]: LibraryData } = {}; + // Open tabs and attach network listeners + await Promise.all( + userProvidedUrls.map(async (url) => { + const sitePage = await this.openPage(); + this.pages[url] = sitePage; + await this.attachListenersToPage(url); + }) + ); + + // Navigate to URLs + await Promise.all( + userProvidedUrls.map(async (url) => { + await this.navigateToPage(url); + }) + ); + + // Delay for page to load resources + await delay(this.pageWaitTime / 2); + + // Accept Banners + if (shouldSkipAcceptBanner) { + // delay + + await Promise.all( + userProvidedUrls.map(async (url) => { + await this.clickOnAcceptBanner(url); + }) + ); + } + + // Scroll to bottom of the page + await Promise.all( + userProvidedUrls.map(async (url) => { + await this.pageScroll(url); + }) + ); + + await Promise.all( + userProvidedUrls.map(async (url) => { + const newMatches = await this.insertAndRunDOMQueryFunctions( + url, + Libraries + ); + + consolidatedDOMQueryMatches = { + ...consolidatedDOMQueryMatches, + ...newMatches, + }; + }) + ); + + // Delay for page to load more resources + await delay(this.pageWaitTime / 2); + + const mainFrameUrlIdMap = await this.getMainframeIds(); + + Object.entries(mainFrameUrlIdMap).forEach(([_url, id]) => { + if (!this.pageFrames[_url]) { + this.pageFrames[_url] = { + [id]: '0', + }; + } else { + this.pageFrames[_url][id] = '0'; + } + }); + + const result = await Promise.all( + userProvidedUrls.map(async (userProvidedUrl) => { + const _responses = this.pageResponses[userProvidedUrl]; + const _requests = this.pageRequests[userProvidedUrl]; + const _page = this.pages[userProvidedUrl]; + const _pageFrames = this.pageFrames[userProvidedUrl]; + + if (!_responses || !_requests || !_page) { + return { + url: userProvidedUrl, + cookieData: {}, + }; + } + + const cookieDataFromNetwork = await parseNetworkDataToCookieData( + _responses, + _requests, + _page, + _pageFrames + ); + + const cookieDataFromJS = await this.getJSCookies(_page); + + const mainFrameUrl = new URL(_page.url()).origin; + + const collatedCookieData = collateCookieData( + cookieDataFromNetwork, + cookieDataFromJS + ); + + return { + // Page may redirect. page.url() gives the redirected URL + url: mainFrameUrl, + cookieData: collatedCookieData, + }; + }) + ); + + return { result, consolidatedDOMQueryMatches }; + } + + async deinitialize() { + await this.browser?.close(); + } +} diff --git a/packages/analysis-utils/src/browserManagement/parseNetworkDataToCookieData.ts b/packages/analysis-utils/src/browserManagement/parseNetworkDataToCookieData.ts new file mode 100644 index 000000000..31140dfa2 --- /dev/null +++ b/packages/analysis-utils/src/browserManagement/parseNetworkDataToCookieData.ts @@ -0,0 +1,270 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import { getDomain } from 'tldts'; +import { CookieData } from '@google-psat/common'; +import type { Page } from 'puppeteer'; +/** + * Internal dependencies. + */ +import { CookieDataFromNetwork, RequestData, ResponseData } from './types'; + +// eslint-disable-next-line complexity +export const parseNetworkDataToCookieData = async ( + responses: Record, + requests: Record, + page: Page, + pageFrames: Record +): Promise => { + const mainFrameId = ''; + + const frameIdNetworkDataMap: Record< + string, + { + responses: ResponseData[]; + requests: RequestData[]; + } + > = {}; + + for (const response of Object.values(responses)) { + if (!response.cookies || response.cookies.length === 0) { + continue; + } + + const frameId = response.frameId || mainFrameId; + + if (!frameIdNetworkDataMap[frameId]) { + frameIdNetworkDataMap[frameId] = { + requests: [], + responses: [], + }; + } + + frameIdNetworkDataMap[frameId].responses.push(response); + } + + for (const request of Object.values(requests)) { + if (!request.cookies || request.cookies.length === 0) { + continue; + } + + const frameId = request.frameId || mainFrameId; + + if (!frameIdNetworkDataMap[frameId]) { + frameIdNetworkDataMap[frameId] = { + requests: [], + responses: [], + }; + } + + frameIdNetworkDataMap[frameId].requests.push(request); + } + + const frameIdCookiesMap: Record< + string, + { + frameCookies: { + [key: string]: CookieData; + }; + } + > = {}; + + for (const [frameId, data] of Object.entries(frameIdNetworkDataMap)) { + const _frameCookies: Record = {}; + + data.responses?.forEach((response: ResponseData) => { + response.cookies.forEach((cookie) => { + // domain update required. Domain based on the server url + let parsedDomain = + cookie.parsedCookie.domain === '' + ? getDomain(response.url) + : cookie.parsedCookie.domain; + + if (parsedDomain && parsedDomain[0] !== '.') { + parsedDomain = '.' + parsedDomain; + } + + const key = + cookie.parsedCookie.name + + ':' + + parsedDomain + + ':' + + cookie.parsedCookie.path; + + const prevEntry = _frameCookies[key.trim()]; + + const blockedReasonsSet = new Set([ + ...(cookie?.blockedReasons || []), + ...(prevEntry?.blockedReasons || []), + ]); + + const networkEvents = { + requestEvents: prevEntry?.networkEvents?.requestEvents || [], + responseEvents: [ + ...(cookie?.networkEvents?.responseEvents || []), + ...(prevEntry?.networkEvents?.responseEvents || []), + ], + }; + + _frameCookies[key.trim()] = { + ...cookie, + exemptionReason: + prevEntry?.exemptionReason ?? cookie?.exemptionReason, + url: response.url, + blockedReasons: Array.from(blockedReasonsSet), + isBlocked: Array.from(blockedReasonsSet).length > 0, + networkEvents, + parsedCookie: { + ...cookie.parsedCookie, + domain: parsedDomain || '', + }, + }; + }); + }); + + data.requests?.forEach((request: RequestData) => { + request.cookies.forEach((cookie) => { + // domain update required. Domain based on the server url + let parsedDomain = + cookie.parsedCookie.domain === '' + ? getDomain(request.url) + : cookie.parsedCookie.domain; + + if (parsedDomain && parsedDomain[0] !== '.') { + parsedDomain = '.' + parsedDomain; + } + + const key = + cookie.parsedCookie.name + + ':' + + parsedDomain + + ':' + + cookie.parsedCookie.path; + + const prevEntry = _frameCookies[key.trim()]; + + const blockedReasonsSet = new Set([ + ...(cookie?.blockedReasons || []), + ...(prevEntry?.blockedReasons || []), + ]); + + const networkEvents = { + requestEvents: [ + ...(cookie?.networkEvents?.requestEvents || []), + ...(prevEntry?.networkEvents?.requestEvents || []), + ], + responseEvents: prevEntry?.networkEvents?.responseEvents || [], + }; + + _frameCookies[key.trim()] = { + ...cookie, + exemptionReason: + prevEntry?.exemptionReason ?? cookie?.exemptionReason, + url: request.url, + blockedReasons: Array.from(blockedReasonsSet), + networkEvents, + isBlocked: Array.from(blockedReasonsSet).length > 0, + parsedCookie: { ...cookie.parsedCookie, domain: parsedDomain || '' }, + }; + }); + }); + + frameIdCookiesMap[frameId] = { + frameCookies: _frameCookies, + }; + } + + const cdpSession = await page.createCDPSession(); + + const { targetInfos } = await cdpSession.send('Target.getTargets'); + + const allTargets: Record = {}; + const pageTargetsFromNetwork: Record = {}; + + targetInfos.forEach(({ targetId, url }) => { + allTargets[targetId] = url; + }); + + for (const frameId of Object.keys(frameIdCookiesMap)) { + let url = ''; + let _frameId = frameId; + + while (url === '') { + if (_frameId === '0') { + url = page.url(); + } + + if (allTargets[frameId]) { + url = allTargets[frameId]; + } else { + // Seek parent + _frameId = pageFrames[_frameId] || '0'; + } + } + pageTargetsFromNetwork[frameId] = url; + } + + const frameUrlCookiesMap: Record< + string, + { + frameCookies: { + [key: string]: CookieData; + }; + } + > = {}; + + for (const [frameId, data] of Object.entries(frameIdCookiesMap)) { + const key = new URL(pageTargetsFromNetwork[frameId]).origin; + + frameUrlCookiesMap[key] = { + frameCookies: { + ...data.frameCookies, + ...(frameUrlCookiesMap[key]?.frameCookies || {}), + }, + }; + } + + // Loop over pageFrames and add empty entry for any frame which was not found in frameUrlCookiesMap. + for (const frameId of Object.keys(pageFrames)) { + let url = ''; + let _frameId = frameId; + + while (url === '') { + if (_frameId === '0') { + url = page.url(); + } + + if (allTargets[frameId]) { + url = allTargets[frameId]; + } else { + // Seek parent + _frameId = pageFrames[_frameId] || '0'; + } + } + + const key = new URL(url).origin; + + if (!frameUrlCookiesMap[key]) { + frameUrlCookiesMap[key] = { + frameCookies: {}, + }; + } + } + + return frameUrlCookiesMap; +}; diff --git a/packages/analysis-utils/src/browserManagement/types.ts b/packages/analysis-utils/src/browserManagement/types.ts new file mode 100644 index 000000000..1d2a66212 --- /dev/null +++ b/packages/analysis-utils/src/browserManagement/types.ts @@ -0,0 +1,60 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { CookieData } from '@google-psat/common'; + +export type ViewportConfig = { + width: number; + height: number; + deviceScaleFactor: number; +}; + +export type ResponseData = { + frameId?: string; + url: string; + cookies: CookieData[]; +}; + +export type RequestData = { + frameId?: string; + url: string; + cookies: CookieData[]; +}; + +// @see https://developer.mozilla.org/en-US/docs/Web/API/CookieStore/getAll +export type CookieStoreCookie = { + name: string; + domain: string; + expires: number; // UNIX time in miliseconds + partitioned: boolean; + path: string; + sameSite: 'strict' | 'lax' | 'none'; + secure: boolean; + value: string; +}; + +export type CookieDataFromNetwork = { + [frameUrl: string]: { + frameCookies: { + [key: string]: CookieData; + }; + }; +}; + +export type PageCookieData = { + url: string; + cookieData: CookieDataFromNetwork; +}; diff --git a/packages/common/src/constants/index.ts b/packages/analysis-utils/src/index.ts similarity index 92% rename from packages/common/src/constants/index.ts rename to packages/analysis-utils/src/index.ts index 92c2b2764..c410a1899 100644 --- a/packages/common/src/constants/index.ts +++ b/packages/analysis-utils/src/index.ts @@ -13,4 +13,5 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export const UNKNOWN_FRAME_KEY = 'Unknown Frames'; + +export * from './procedures'; diff --git a/packages/cli/src/procedures/analyzeCookieUrls.ts b/packages/analysis-utils/src/procedures/analyzeCookiesUrlsAndFetchResources.ts similarity index 65% rename from packages/cli/src/procedures/analyzeCookieUrls.ts rename to packages/analysis-utils/src/procedures/analyzeCookiesUrlsAndFetchResources.ts index 93a8241f4..05acc91de 100644 --- a/packages/cli/src/procedures/analyzeCookieUrls.ts +++ b/packages/analysis-utils/src/procedures/analyzeCookiesUrlsAndFetchResources.ts @@ -17,16 +17,22 @@ /** * External dependencies. */ -import { isFirstParty, findAnalyticsMatch } from '@ps-analysis-tool/common'; +import { + isFirstParty, + findAnalyticsMatch, + CookieDatabase, + LibraryMatchers, + deriveBlockingStatus, +} from '@google-psat/common'; /** * Internal dependencies. */ -import { CookieDatabase } from '../types'; -import { BrowserManagement } from '../utils/browserManagement'; +import { BrowserManagement } from '../browserManagement'; -export const analyzeCookiesUrls = async ( +export const analyzeCookiesUrlsAndFetchResources = async ( urls: string[], + Libraries: LibraryMatchers[], isHeadless: boolean, delayTime: number, cookieDictionary: CookieDatabase, @@ -44,12 +50,12 @@ export const analyzeCookiesUrls = async ( ); await browser.initializeBrowser(true); - const analysisCookieData = await browser.analyzeCookieUrls( - urls, - shouldSkipAcceptBanner - ); + const { result: analysisCookieData, consolidatedDOMQueryMatches } = + await browser.analyzeCookies(urls, shouldSkipAcceptBanner, Libraries); + + const resources = browser.getResources(urls); - const res = analysisCookieData.map(({ pageUrl, cookieData }) => { + const res = analysisCookieData.map(({ url: pageUrl, cookieData }) => { Object.entries(cookieData).forEach(([, frameData]) => { const frameCookies = frameData.frameCookies; Object.entries(frameCookies).forEach(([key, cookie]) => { @@ -58,22 +64,29 @@ export const analyzeCookiesUrls = async ( cookieDictionary ); - frameCookies[key].analytics = { + frameCookies[key.trim()].analytics = { platform: analytics?.platform || 'Unknown', category: analytics?.category || 'Uncategorized', gdprUrl: analytics?.gdprUrl || '', description: analytics?.description, }; - frameCookies[key].isFirstParty = isFirstParty( + + frameCookies[key.trim()].isFirstParty = isFirstParty( cookie.parsedCookie.domain, pageUrl ); + + frameCookies[key.trim()].blockingStatus = deriveBlockingStatus( + cookie.networkEvents + ); }); }); return { - pageUrl, + url: pageUrl, cookieData, + resources: resources[pageUrl], + domQueryMatches: consolidatedDOMQueryMatches[pageUrl], }; }); diff --git a/packages/cli/src/procedures/analyzeCookieUrlsInBatches.ts b/packages/analysis-utils/src/procedures/analyzeCookiesUrlsInBatchesAndFetchResources.ts similarity index 62% rename from packages/cli/src/procedures/analyzeCookieUrlsInBatches.ts rename to packages/analysis-utils/src/procedures/analyzeCookiesUrlsInBatchesAndFetchResources.ts index 93df0200c..15bb32428 100644 --- a/packages/cli/src/procedures/analyzeCookieUrlsInBatches.ts +++ b/packages/analysis-utils/src/procedures/analyzeCookiesUrlsInBatchesAndFetchResources.ts @@ -14,15 +14,24 @@ * limitations under the License. */ +/** + * External dependencies. + */ +import { + CookieData, + CookieDatabase, + LibraryData, + LibraryMatchers, +} from '@google-psat/common'; + /** * Internal dependencies. */ -import { CookieData } from '@ps-analysis-tool/common'; -import { CookieDatabase } from '../types'; -import { analyzeCookiesUrls } from './analyzeCookieUrls'; +import { analyzeCookiesUrlsAndFetchResources } from './analyzeCookiesUrlsAndFetchResources'; -export const analyzeCookiesUrlsInBatches = async ( +export const analyzeCookiesUrlsInBatchesAndFetchResources = async ( urls: string[], + Libraries: LibraryMatchers[], isHeadless: boolean, delayTime: number, cookieDictionary: CookieDatabase, @@ -40,15 +49,20 @@ export const analyzeCookiesUrlsInBatches = async ( shouldSkipAcceptBanner = false ) => { let report: { - pageUrl: string; + url: string; cookieData: { [frameUrl: string]: { - cookiesCount: number; frameCookies: { [key: string]: CookieData; }; }; }; + resources: { + origin: string | null; + content: string; + type?: string; + }[]; + domQueryMatches: LibraryData; }[] = []; for (let i = 0; i < urls.length; i += batchSize) { @@ -57,25 +71,27 @@ export const analyzeCookiesUrlsInBatches = async ( spinnies && spinnies.add(`cookie-batch-spinner`, { - text: `Analyzing cookies in urls ${start + 1} - ${end + 1} `, + text: `Analyzing cookies in URLs ${start + 1} - ${end + 1}...`, indent: 2, }); const urlsWindow = urls.slice(start, end + 1); - const cookieAnalysis = await analyzeCookiesUrls( - urlsWindow, - isHeadless, - delayTime, - cookieDictionary, - shouldSkipAcceptBanner - ); + const cookieAnalysisAndFetchedResources = + await analyzeCookiesUrlsAndFetchResources( + urlsWindow, + Libraries, + isHeadless, + delayTime, + cookieDictionary, + shouldSkipAcceptBanner + ); - report = [...report, ...cookieAnalysis]; + report = [...report, ...cookieAnalysisAndFetchedResources]; spinnies && spinnies.succeed(`cookie-batch-spinner`, { - text: `Done analyzing cookies in urls ${start + 1} - ${end + 1} `, + text: `Done analyzing cookies in URLs ${start + 1} - ${end + 1}.`, indent: 2, }); } diff --git a/packages/cli/src/procedures/analyzeTechnologiesUrlsInBatches.ts b/packages/analysis-utils/src/procedures/analyzeTechnologiesUrlsInBatches.ts similarity index 92% rename from packages/cli/src/procedures/analyzeTechnologiesUrlsInBatches.ts rename to packages/analysis-utils/src/procedures/analyzeTechnologiesUrlsInBatches.ts index bc55cf356..b380547d5 100644 --- a/packages/cli/src/procedures/analyzeTechnologiesUrlsInBatches.ts +++ b/packages/analysis-utils/src/procedures/analyzeTechnologiesUrlsInBatches.ts @@ -17,7 +17,6 @@ /** * External dependencies. */ - // @ts-ignore Package does not support typescript. import Wapplalyzer from 'wappalyzer'; @@ -51,7 +50,7 @@ export const analyzeTechnologiesUrlsInBatches = async ( spinnies && spinnies.add(`tech-batch-spinner`, { - text: `Analyzing technologies in urls ${start + 1} - ${end + 1} `, + text: `Analyzing technologies in URLs ${start + 1} - ${end + 1}...`, indent: 2, }); @@ -68,7 +67,7 @@ export const analyzeTechnologiesUrlsInBatches = async ( spinnies && spinnies.succeed(`tech-batch-spinner`, { - text: `Done analyzing technology in urls ${start + 1} - ${end + 1} `, + text: `Done analyzing technology in URLs ${start + 1} - ${end + 1}.`, indent: 2, }); await wappalyzer.destroy(); diff --git a/packages/analysis-utils/src/procedures/index.ts b/packages/analysis-utils/src/procedures/index.ts new file mode 100644 index 000000000..da3098d44 --- /dev/null +++ b/packages/analysis-utils/src/procedures/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { analyzeCookiesUrlsAndFetchResources } from './analyzeCookiesUrlsAndFetchResources'; +export { analyzeCookiesUrlsInBatchesAndFetchResources } from './analyzeCookiesUrlsInBatchesAndFetchResources'; +export { analyzeTechnologiesUrlsInBatches } from './analyzeTechnologiesUrlsInBatches'; diff --git a/packages/cli/src/types.ts b/packages/analysis-utils/src/types.ts similarity index 84% rename from packages/cli/src/types.ts rename to packages/analysis-utils/src/types.ts index c128950f1..03f7d44f9 100644 --- a/packages/cli/src/types.ts +++ b/packages/analysis-utils/src/types.ts @@ -106,30 +106,6 @@ export type UniqueCookiesLogDetail = { [key: string]: CookieLogDetails; }; -export type CookieAnalytics = { - platform: string; - category: string; - name: string; - domain: string; - description: string; - retention: string; - dataController: string; - gdprUrl: string; - wildcard: string; -}; - -export type CookieDatabase = { - [name: string]: Array; -}; - -export type CookieData = { - parsedCookie: ParsedCookie; - analytics: CookieAnalytics | null; - url: string; - headerType: 'response' | 'request'; - isFirstParty: boolean | null; -}; - export interface Job { data: any; status: string; diff --git a/packages/analysis-utils/tsconfig.json b/packages/analysis-utils/tsconfig.json new file mode 100644 index 000000000..6ca0c6b71 --- /dev/null +++ b/packages/analysis-utils/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "rootDir": "src", + "target": "es6", + "lib": ["es2021"], + "module": "commonjs", + "outDir": "dist", + "declarationDir": "dist-types", + "composite": true, + "strict": true, + "sourceMap": true, + "esModuleInterop": true, + "moduleResolution": "node" + }, + "exclude": ["**/tests/**/*.ts", "dist/**", "dist-types/**"] +} diff --git a/packages/cli-dashboard/.npmignore b/packages/cli-dashboard/.npmignore new file mode 100644 index 000000000..aa8e45f12 --- /dev/null +++ b/packages/cli-dashboard/.npmignore @@ -0,0 +1 @@ +src/ \ No newline at end of file diff --git a/packages/cli-dashboard/babel.config.cjs b/packages/cli-dashboard/babel.config.cjs new file mode 100644 index 000000000..6ab27bceb --- /dev/null +++ b/packages/cli-dashboard/babel.config.cjs @@ -0,0 +1,36 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +module.exports = function (api) { + const isProduction = api.env('production'); + + return { + presets: [ + ['@babel/preset-env'], + [ + '@babel/preset-react', + { + development: !isProduction, + }, + ], + '@babel/preset-typescript', + ], + plugins: [ + ['@babel/plugin-transform-react-jsx'], + ['babel-plugin-styled-components'], + ], + sourceMaps: true, + }; +}; diff --git a/packages/cli-dashboard/package.json b/packages/cli-dashboard/package.json index 73566af5b..ddda9cd35 100644 --- a/packages/cli-dashboard/package.json +++ b/packages/cli-dashboard/package.json @@ -1,24 +1,42 @@ { - "name": "@ps-analysis-tool/cli-dashboard", - "version": "0.8.0", + "name": "@google-psat/cli-dashboard", + "version": "0.9.0-2", "description": "Dashboard for visualizing cli analysis output", + "author": { + "name": "Google" + }, "repository": { "type": "git", - "url": "git+https://github.com/GoogleChromeLabs/ps-analysis-tool.git" + "url": "https://github.com/GoogleChromeLabs/ps-analysis-tool", + "directory": "packages/cli-dashboard" + }, + "main": "dist/index.js", + "publishConfig": { + "access": "public" }, + "files": [ + "dist", + "public" + ], "license": "Apache-2.0", "bugs": { "url": "https://github.com/GoogleChromeLabs/ps-analysis-tool/issues" }, "homepage": "https://github.com/GoogleChromeLabs/ps-analysis-tool#readme", "scripts": { - "start": "webpack-dev-server --config webpack.config.cjs", - "build": "webpack --config webpack.config.cjs", - "dev": "webpack-dev-server --config webpack.config.cjs" + "start": "webpack-dev-server --config ../../dashboard.webpack.config.cjs", + "build": "webpack --config ../../dashboard.webpack.config.cjs", + "build:remove": "rimraf dist", + "publish:local": "npm publish --registry=http://localhost:4873", + "publish:remote": "npm publish --access=public --registry=https://registry.npmjs.org", + "unpublish:local": "npm unpublish --registry=http://localhost:4873 --force", + "unpublish:remote": "npm unpublish --registry=https://registry.npmjs.org --force" }, "dependencies": { - "@ps-analysis-tool/common": "*", - "@ps-analysis-tool/design-system": "*", + "@google-psat/common": "*", + "@google-psat/design-system": "*", + "@google-psat/i18n": "*", + "@google-psat/library-detection": "*", "classnames": "^2.3.2", "file-saver": "^2.0.5", "jszip": "^3.10.1", diff --git a/packages/cli-dashboard/src/app.tsx b/packages/cli-dashboard/src/app.tsx index 3e609983d..683158944 100644 --- a/packages/cli-dashboard/src/app.tsx +++ b/packages/cli-dashboard/src/app.tsx @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - /** * External dependencies */ @@ -21,8 +20,10 @@ import React, { useEffect, useMemo, useState } from 'react'; import type { CompleteJson, CookieFrameStorageType, + LibraryData, TechnologyData, -} from '@ps-analysis-tool/common'; +} from '@google-psat/common'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies @@ -46,59 +47,67 @@ const App = () => { const [completeJsonReport, setCompleteJsonReport] = useState< CompleteJson[] | null >(null); + const [libraryMatches, setLibraryMatches] = useState<{ + [key: string]: LibraryData; + } | null>(null); + + const type = useMemo(() => { + // @ts-ignore + return globalThis?.PSAT_DATA?.type === 'sitemap' + ? DisplayType.SITEMAP + : DisplayType.SITE; + }, []); + + useEffect(() => { + const bodyTag = document.querySelector('body'); - const [type, path] = useMemo(() => { - const urlParams = new URLSearchParams(window.location.search); - const dir = urlParams.get('dir'); + if (!bodyTag) { + return; + } - return [ - urlParams.get('type') === 'sitemap' - ? DisplayType.SITEMAP - : DisplayType.SITE, - `/out/${dir}/out.json`, - ]; + bodyTag.style.fontSize = '75%'; }, []); useEffect(() => { - (async () => { - const response = await fetch(path); - const data: CompleteJson[] = await response.json(); - setCompleteJsonReport(data); - - let _cookies: CookieFrameStorageType = {}, - _technologies: TechnologyData[] = []; - - if (type === DisplayType.SITEMAP) { - const extractedData = extractReportData(data); - - _cookies = extractedData.cookies; - _technologies = extractedData.technologies; - setLandingPageCookies(extractedData.landingPageCookies); - } else { - _cookies = extractCookies(data[0].cookieData, data[0].pageUrl, true); - _technologies = data[0].technologyData; - } - - setCookies(_cookies); - setTechnologies(_technologies); - })(); - }, [path, type]); - - if (!path) { - return ( -
-
No path provided
-
- ); - } + sessionStorage.clear(); + //@ts-ignore + const messages = globalThis?.PSAT_DATA?.translations; + I18n.initMessages(messages); + + // @ts-ignore + const data: CompleteJson[] = globalThis?.PSAT_DATA?.json; + setCompleteJsonReport(data); + + let _cookies: CookieFrameStorageType = {}, + _technologies: TechnologyData[] = [], + _libraryMatches: { + [key: string]: LibraryData; + } = {}; + + if (type === DisplayType.SITEMAP) { + const extractedData = extractReportData(data); + + _libraryMatches = extractedData.consolidatedLibraryMatches; + setLandingPageCookies(extractedData.landingPageCookies); + } else { + _cookies = extractCookies(data[0].cookieData, data[0].pageUrl, true); + _technologies = data[0].technologyData; + _libraryMatches = { [data[0].pageUrl]: data[0].libraryMatches }; + } + + setCookies(_cookies); + setTechnologies(_technologies); + setLibraryMatches(_libraryMatches); + }, [type]); if (type === DisplayType.SITEMAP) { return ( ); } @@ -109,7 +118,15 @@ const App = () => { completeJson={completeJsonReport} cookies={cookies} technologies={technologies} - selectedSite={path.slice(5, -9)} + // @ts-ignore + selectedSite={globalThis?.PSAT_DATA?.selectedSite} + // @ts-ignore + path={globalThis?.PSAT_DATA?.selectedSite} + libraryMatches={ + libraryMatches + ? libraryMatches[Object.keys(libraryMatches ?? {})[0]] + : null + } /> ); diff --git a/packages/cli-dashboard/src/components/cookiesWithIssues/index.tsx b/packages/cli-dashboard/src/components/cookiesWithIssues/index.tsx index 9e987399c..4849e7234 100644 --- a/packages/cli-dashboard/src/components/cookiesWithIssues/index.tsx +++ b/packages/cli-dashboard/src/components/cookiesWithIssues/index.tsx @@ -19,8 +19,8 @@ */ import React, { useState } from 'react'; import { Resizable } from 're-resizable'; -import { CookieDetails, CookieTable } from '@ps-analysis-tool/design-system'; -import { type CookieTableData } from '@ps-analysis-tool/common'; +import { CookieDetails, CookieTable } from '@google-psat/design-system'; +import { type CookieTableData } from '@google-psat/common'; /** * Internal dependencies. @@ -75,6 +75,7 @@ const CookiesWithIssues = ({ selectedFrameCookie={selectedFrameCookie} hideExport={true} setSelectedFrameCookie={setSelectedFrameCookie} + isCLI /> { const [data, setData] = useState(sidebarData); @@ -53,11 +50,11 @@ const SiteMapReport = ({ ); diff --git a/packages/cli-dashboard/src/components/siteMapReport/layout.tsx b/packages/cli-dashboard/src/components/siteMapReport/layout.tsx index f680a3e79..57b774ba9 100644 --- a/packages/cli-dashboard/src/components/siteMapReport/layout.tsx +++ b/packages/cli-dashboard/src/components/siteMapReport/layout.tsx @@ -26,13 +26,13 @@ import { useSidebar, type SidebarItems, SIDEBAR_ITEMS_KEYS, -} from '@ps-analysis-tool/design-system'; +} from '@google-psat/design-system'; import { type TabFrames, - type TechnologyData, type CookieFrameStorageType, type CompleteJson, -} from '@ps-analysis-tool/common'; + type LibraryData, +} from '@google-psat/common'; /** * Internal dependencies. @@ -42,36 +42,35 @@ import SiteMapCookiesWithIssues from './sitemapCookiesWithIssues'; import CookiesLandingContainer from '../siteReport/tabs/cookies/cookiesLandingContainer'; import reshapeCookies from '../utils/reshapeCookies'; import { generateSiteMapReportandDownload } from '../utils/reportDownloader'; +import extractCookies from '../utils/extractCookies'; interface LayoutProps { landingPageCookies: CookieFrameStorageType; - cookies: CookieFrameStorageType; - technologies: TechnologyData[]; completeJson: CompleteJson[] | null; sidebarData: SidebarItems; setSidebarData: React.Dispatch>; + path: string; + libraryMatches: { [url: string]: LibraryData } | null; } const Layout = ({ - cookies, - technologies, landingPageCookies, completeJson, sidebarData, setSidebarData, + path, + libraryMatches, }: LayoutProps) => { const [sites, setSites] = useState([]); useEffect(() => { const _sites = new Set(); - Object.values(cookies).forEach((cookieData) => { - Object.values(cookieData).forEach((cookie) => { - _sites.add(cookie.pageUrl || ''); - }); + completeJson?.forEach(({ pageUrl }) => { + _sites.add(pageUrl); }); setSites(Array.from(_sites)); - }, [cookies]); + }, [completeJson]); const reshapedCookies = useMemo( () => reshapeCookies(landingPageCookies), @@ -98,26 +97,43 @@ const Layout = ({ const { Element: PanelElement, props } = activePanel.panel; - const siteFilteredCookies = useMemo(() => { - return Object.entries(cookies).reduce( - (acc: CookieFrameStorageType, [frame, _cookies]) => { - acc[frame] = Object.fromEntries( - Object.entries(_cookies).filter(([, cookie]) => - isKeySelected(cookie.pageUrl || '') - ) - ); - - return acc; - }, - {} + const [ + siteFilteredCookies, + siteFilteredTechnologies, + siteFilteredCompleteJson, + ] = useMemo(() => { + const reportData = completeJson?.find((data) => + isKeySelected(data.pageUrl) ); - }, [cookies, isKeySelected]); - const siteFilteredTechnologies = useMemo(() => { - return technologies.filter((technology) => - isKeySelected(technology.pageUrl || '') + if (!reportData) { + return [{}, [], null]; + } + + const _cookies = extractCookies( + reportData.cookieData, + reportData.pageUrl, + true ); - }, [isKeySelected, technologies]); + const _technologies = reportData.technologyData; + + return [_cookies, _technologies, [reportData]]; + }, [completeJson, isKeySelected]); + + const doesSiteHaveCookies = useMemo(() => { + const store = {} as Record; + + completeJson?.forEach((data) => { + store[data.pageUrl] = Object.entries(data.cookieData).reduce( + (acc, [, frameData]) => { + return acc || Object.keys(frameData.frameCookies || {}).length > 0; + }, + false + ); + }); + + return store; + }, [completeJson]); useEffect(() => { setSidebarData((prev) => { @@ -127,6 +143,7 @@ const Layout = ({ Element: CookiesLandingContainer, props: { tabCookies: reshapedCookies, + isSiteMapLandingContainer: true, tabFrames: sites.reduce((acc, site) => { acc[site] = {} as TabFrames[string]; @@ -138,8 +155,14 @@ const Layout = ({ return; } - generateSiteMapReportandDownload(completeJson, ''); + generateSiteMapReportandDownload( + completeJson, + //@ts-ignore + atob(globalThis.PSAT_REPORT_HTML), + '' + ); }, + menuBarScrollContainerId: 'dashboard-sitemap-layout-container', }, }; @@ -152,8 +175,10 @@ const Layout = ({ props: { cookies: siteFilteredCookies, technologies: siteFilteredTechnologies, - completeJson, + completeJson: siteFilteredCompleteJson, selectedSite: site, + path, + libraryMatches: libraryMatches ? libraryMatches[site] : {}, }, }, children: {}, @@ -163,6 +188,7 @@ const Layout = ({ selectedIcon: { Element: FileWhite, }, + isBlurred: doesSiteHaveCookies[site] === false, }; return acc; @@ -182,11 +208,14 @@ const Layout = ({ return _data; }); }, [ + libraryMatches, completeJson, cookiesWithIssues, - isKeySelected, + doesSiteHaveCookies, + path, reshapedCookies, setSidebarData, + siteFilteredCompleteJson, siteFilteredCookies, siteFilteredTechnologies, sites, @@ -210,7 +239,10 @@ const Layout = ({ > -
+
{PanelElement && }
diff --git a/packages/cli-dashboard/src/components/siteMapReport/sidebarData.ts b/packages/cli-dashboard/src/components/siteMapReport/sidebarData.ts index 6f3b8a2e8..9f9fc3252 100644 --- a/packages/cli-dashboard/src/components/siteMapReport/sidebarData.ts +++ b/packages/cli-dashboard/src/components/siteMapReport/sidebarData.ts @@ -20,16 +20,17 @@ import { SIDEBAR_ITEMS_KEYS, WarningBare, type SidebarItems, -} from '@ps-analysis-tool/design-system'; +} from '@google-psat/design-system'; +import { I18n } from '@google-psat/i18n'; const sidebarData: SidebarItems = { [SIDEBAR_ITEMS_KEYS.COOKIES]: { - title: 'Sitemap Report', + title: () => I18n.getMessage('sitemapReport'), children: {}, dropdownOpen: true, }, [SIDEBAR_ITEMS_KEYS.COOKIES_WITH_ISSUES]: { - title: 'Cookie Issues', + title: () => I18n.getMessage('cookieIssues'), children: {}, icon: { Element: WarningBare, diff --git a/packages/cli-dashboard/src/components/siteMapReport/sitemapCookiesWithIssues.tsx b/packages/cli-dashboard/src/components/siteMapReport/sitemapCookiesWithIssues.tsx index ffa2c023c..a23935a69 100644 --- a/packages/cli-dashboard/src/components/siteMapReport/sitemapCookiesWithIssues.tsx +++ b/packages/cli-dashboard/src/components/siteMapReport/sitemapCookiesWithIssues.tsx @@ -18,7 +18,7 @@ * External dependencies. */ import React from 'react'; -import type { CookieTableData } from '@ps-analysis-tool/common'; +import type { CookieTableData } from '@google-psat/common'; /** * Internal dependencies. diff --git a/packages/cli-dashboard/src/components/siteReport/components/layout.tsx b/packages/cli-dashboard/src/components/siteReport/components/layout.tsx index 992d67328..51331e649 100644 --- a/packages/cli-dashboard/src/components/siteReport/components/layout.tsx +++ b/packages/cli-dashboard/src/components/siteReport/components/layout.tsx @@ -27,8 +27,8 @@ import { SiteBoundariesIcon, SiteBoundariesIconWhite, SIDEBAR_ITEMS_KEYS, -} from '@ps-analysis-tool/design-system'; -import { UNKNOWN_FRAME_KEY } from '@ps-analysis-tool/common'; +} from '@google-psat/design-system'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. @@ -44,26 +44,16 @@ interface LayoutProps { } const Layout = ({ selectedSite, setSidebarData }: LayoutProps) => { - const { tabCookies, technologies } = useContentStore(({ state }) => ({ - tabCookies: state.tabCookies, + const { technologies, completeJson } = useContentStore(({ state }) => ({ technologies: state.technologies, + completeJson: state.completeJson, })); - const frameUrls = useMemo( - () => [ - ...new Set( - Object.values(tabCookies) - .reduce((acc, cookie) => { - acc.push(...(cookie.frameUrls as string[])); - return acc; - }, [] as string[]) - .filter( - (url) => url?.includes('http') || url === UNKNOWN_FRAME_KEY - ) as string[] - ), - ], - [tabCookies] - ); + const frameUrls = useMemo(() => { + const frames = Object.keys(completeJson?.[0].cookieData ?? {}); + + return frames.filter((url) => url?.includes('http')); + }, [completeJson]); const { activePanel, selectedItemKey, updateSelectedItemKey } = useSidebar( ({ state, actions }) => ({ @@ -86,6 +76,7 @@ const Layout = ({ selectedSite, setSidebarData }: LayoutProps) => { props: { selectedFrameUrl: null, selectedSite, + isSiteMapLandingContainer: false, }, }; @@ -111,6 +102,10 @@ const Layout = ({ selectedSite, setSidebarData }: LayoutProps) => { selectedIcon: { Element: CookieIconWhite, }, + isBlurred: + Object.keys( + completeJson?.[0].cookieData?.[url]?.frameCookies || {} + ).length === 0, }; return acc; @@ -127,7 +122,7 @@ const Layout = ({ selectedSite, setSidebarData }: LayoutProps) => { if (technologies && technologies.length > 0) { _data[SIDEBAR_ITEMS_KEYS.TECHNOLOGIES] = { - title: 'Technologies', + title: I18n.getMessage('technologies'), children: {}, icon: { Element: SiteBoundariesIcon, @@ -148,7 +143,14 @@ const Layout = ({ selectedSite, setSidebarData }: LayoutProps) => { return _data; }); - }, [frameUrls, selectedItemKey, selectedSite, setSidebarData, technologies]); + }, [ + completeJson, + frameUrls, + selectedItemKey, + selectedSite, + setSidebarData, + technologies, + ]); useEffect(() => { if (selectedItemKey === null) { diff --git a/packages/cli-dashboard/src/components/siteReport/index.tsx b/packages/cli-dashboard/src/components/siteReport/index.tsx index 6299268bf..7e8e49a81 100644 --- a/packages/cli-dashboard/src/components/siteReport/index.tsx +++ b/packages/cli-dashboard/src/components/siteReport/index.tsx @@ -21,12 +21,10 @@ import React, { useState } from 'react'; import type { CompleteJson, CookieJsonDataType, + LibraryData, TechnologyData, -} from '@ps-analysis-tool/common'; -import { - SidebarProvider, - type SidebarItems, -} from '@ps-analysis-tool/design-system'; +} from '@google-psat/common'; +import { SidebarProvider, type SidebarItems } from '@google-psat/design-system'; /** * Internal dependencies. @@ -44,6 +42,8 @@ interface SiteReportProps { technologies: TechnologyData[]; completeJson: CompleteJson[] | null; selectedSite: string | null; + path: string; + libraryMatches: LibraryData | null; } const SiteReport = ({ @@ -51,14 +51,17 @@ const SiteReport = ({ technologies, completeJson, selectedSite, + path, + libraryMatches, }: SiteReportProps) => { const [data, setData] = useState(Tabs); - return ( diff --git a/packages/cli-dashboard/src/components/siteReport/stateProviders/contentStore/index.tsx b/packages/cli-dashboard/src/components/siteReport/stateProviders/contentStore/index.tsx index 71544cbd8..58bd1aca9 100644 --- a/packages/cli-dashboard/src/components/siteReport/stateProviders/contentStore/index.tsx +++ b/packages/cli-dashboard/src/components/siteReport/stateProviders/contentStore/index.tsx @@ -24,7 +24,8 @@ import { type TechnologyData, useContextSelector, createContext, -} from '@ps-analysis-tool/common'; + type LibraryData, +} from '@google-psat/common'; /** * Internal dependencies. @@ -36,6 +37,8 @@ export interface ContentStore { tabCookies: { [key: string]: CookieTableData }; technologies: TechnologyData[] | undefined; completeJson: CompleteJson[] | null; + path: string; + libraryMatches: LibraryData | null; }; } @@ -44,6 +47,8 @@ const initialState: ContentStore = { tabCookies: {}, technologies: [], completeJson: null, + path: '', + libraryMatches: null, }, }; @@ -57,6 +62,8 @@ interface ContentStoreProviderProps { }; technologies?: TechnologyData[]; completeJson: CompleteJson[] | null; + path: string; + libraryMatches: LibraryData | null; } export const Provider = ({ @@ -64,6 +71,8 @@ export const Provider = ({ technologies, completeJson, children, + path, + libraryMatches, }: PropsWithChildren) => { const tabCookies = useMemo(() => reshapeCookies(cookies), [cookies]); @@ -74,6 +83,8 @@ export const Provider = ({ tabCookies, technologies, completeJson, + path, + libraryMatches, }, }} > diff --git a/packages/cli-dashboard/src/components/siteReport/tabs/cookies/cookiesLandingContainer/cookieLanding/blockedCookiesSection.tsx b/packages/cli-dashboard/src/components/siteReport/tabs/cookies/cookiesLandingContainer/cookieLanding/blockedCookiesSection.tsx index 1ae342550..93faeadb4 100644 --- a/packages/cli-dashboard/src/components/siteReport/tabs/cookies/cookiesLandingContainer/cookieLanding/blockedCookiesSection.tsx +++ b/packages/cli-dashboard/src/components/siteReport/tabs/cookies/cookiesLandingContainer/cookieLanding/blockedCookiesSection.tsx @@ -19,15 +19,20 @@ import React from 'react'; import { CookiesLandingWrapper, - type DataMapping, prepareCookieStatsComponents, prepareCookiesCount, MatrixContainer, type MatrixComponentProps, LEGEND_DESCRIPTION, useFiltersMapping, -} from '@ps-analysis-tool/design-system'; -import type { TabCookies, TabFrames } from '@ps-analysis-tool/common'; +} from '@google-psat/design-system'; +import { + type TabCookies, + type TabFrames, + type DataMapping, + getLegendDescription, +} from '@google-psat/common'; +import { I18n } from '@google-psat/i18n'; interface BlockedCookiesSectionProps { tabCookies: TabCookies | null; @@ -47,10 +52,17 @@ const BlockedCookiesSection = ({ const cookiesStatsComponents = prepareCookieStatsComponents(cookieStats); const blockedCookieDataMapping: DataMapping[] = [ { - title: 'Blocked cookies', + title: I18n.getMessage('blockedCookies'), count: cookieStats.blockedCookies.total, data: cookiesStatsComponents.blocked, - onClick: () => selectedItemUpdater('All', 'blockedReasons'), + onClick: + cookieStats.blockedCookies.total > 0 + ? () => + selectedItemUpdater( + I18n.getMessage('selectAll'), + 'blockedReasons' + ) + : null, }, ]; const dataComponents: MatrixComponentProps[] = @@ -58,7 +70,7 @@ const BlockedCookiesSection = ({ const legendDescription = LEGEND_DESCRIPTION[component.label] || ''; return { ...component, - description: legendDescription, + description: getLegendDescription(legendDescription), title: component.label, containerClasses: '', onClick: (title: string) => @@ -71,15 +83,16 @@ const BlockedCookiesSection = ({ prepareCookieStatsComponents(blockedCookiesStats); const blockedDataComponents: MatrixComponentProps[] = blockedCookiesStatsComponents.legend.map((component) => { - const legendDescription = LEGEND_DESCRIPTION[component.label] || ''; + const legendDescription = + LEGEND_DESCRIPTION[component.descriptionKey || '']; return { ...component, - description: legendDescription, + description: getLegendDescription(legendDescription), title: component.label, containerClasses: '', onClick: (title: string) => { multiSelectItemUpdater({ - blockedReasons: ['All'], + blockedReasons: [I18n.getMessage('selectAll')], 'analytics.category': [title], }); }, @@ -94,9 +107,9 @@ const BlockedCookiesSection = ({ {dataComponents.length > 0 && ( <>
diff --git a/packages/cli-dashboard/src/components/siteReport/tabs/cookies/cookiesLandingContainer/cookieLanding/cookiesSection.tsx b/packages/cli-dashboard/src/components/siteReport/tabs/cookies/cookiesLandingContainer/cookieLanding/cookiesSection.tsx index a595afa7c..d0acf7beb 100644 --- a/packages/cli-dashboard/src/components/siteReport/tabs/cookies/cookiesLandingContainer/cookieLanding/cookiesSection.tsx +++ b/packages/cli-dashboard/src/components/siteReport/tabs/cookies/cookiesLandingContainer/cookieLanding/cookiesSection.tsx @@ -25,11 +25,9 @@ import { prepareCookieStatsComponents, prepareCookiesCount, useFiltersMapping, -} from '@ps-analysis-tool/design-system'; -import type { TabCookies, TabFrames } from '@ps-analysis-tool/common'; -/** - * Internal dependencies - */ +} from '@google-psat/design-system'; +import type { TabCookies, TabFrames } from '@google-psat/common'; +import { I18n } from '@google-psat/i18n'; interface CookiesSectionProps { tabCookies: TabCookies | null; @@ -63,8 +61,8 @@ const CookiesSection = ({ tabCookies, tabFrames }: CookiesSectionProps) => { (cookieStats?.firstParty.total === 0 && cookieStats?.thirdParty.total === 0 && ( ))} { + const { selectedItemUpdater } = useFiltersMapping(tabFrames || {}); + const cookiesStatsComponents = prepareCookieStatsComponents(cookieStats); + + const dataComponents: MatrixComponentProps[] = + cookiesStatsComponents.exemptedCookiesLegend.map((component) => { + const legendDescription = LEGEND_DESCRIPTION[component.label] || ''; + return { + ...component, + description: legendDescription, + title: component.label, + containerClasses: '', + onClick: (title: string) => { + selectedItemUpdater(title, 'exemptionReason'); + }, + }; + }); + + const exemptedCookiesDataMapping: DataMapping[] = [ + { + title: 'Exempted cookies', + count: cookieStats.exemptedCookies.total, + data: cookiesStatsComponents.exempted, + onClick: + cookieStats.exemptedCookies.total > 0 + ? () => selectedItemUpdater('All', 'exemptionReason') + : null, + }, + ]; + + const description = !cookieStats.exemptedCookies.total ? ( +
+ No cookies were exempted by the browser. + + + +
+ ) : ( + '' + ); + + return ( + + {dataComponents.length > 0 && ( + + )} + + ); +}; + +export default ExemptedCookiesSection; diff --git a/packages/cli-dashboard/src/components/siteReport/tabs/cookies/cookiesLandingContainer/cookieLanding/knownBreakages.tsx b/packages/cli-dashboard/src/components/siteReport/tabs/cookies/cookiesLandingContainer/cookieLanding/knownBreakages.tsx new file mode 100644 index 000000000..d4fb0caff --- /dev/null +++ b/packages/cli-dashboard/src/components/siteReport/tabs/cookies/cookiesLandingContainer/cookieLanding/knownBreakages.tsx @@ -0,0 +1,88 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * External dependencies + */ +import React from 'react'; +import type { LibraryData } from '@google-psat/common'; +import { COLOR_MAP, CookiesLandingWrapper } from '@google-psat/design-system'; +import { Libraries, type AccordionProps } from '@google-psat/library-detection'; +import { I18n } from '@google-psat/i18n'; + +interface KnownBreakagesProps { + libraryMatches: LibraryData; +} + +const KnownBreakages = ({ libraryMatches }: KnownBreakagesProps) => { + const names = Object.keys(libraryMatches); + + const detectedLibraryNames = names.filter( + (name) => + libraryMatches[name as keyof LibraryData]?.matches?.length || + libraryMatches[name as keyof LibraryData]?.domQuerymatches?.length + ); + + const dataMapping = [ + { + title: I18n.getMessage('knownBreakages'), + count: Number(detectedLibraryNames.length), + data: [{ count: 1, color: COLOR_MAP.uncategorized.color }], + }, + ]; + + const result = + detectedLibraryNames.length > 0 ? ( + <> + {Libraries.map((library) => { + const Component = library.component as React.FC; + + const matches = + libraryMatches && libraryMatches[library.name as keyof LibraryData] + ? libraryMatches[library.name as keyof LibraryData]?.matches + : []; + const domQueryMatches = + libraryMatches && libraryMatches[library.name as keyof LibraryData] + ? libraryMatches[library.name as keyof LibraryData] + ?.domQuerymatches + : null; + + return ( + + ); + })} + + ) : ( +

+ {I18n.getMessage('noLibraries')} +

+ ); + + return ( + +
{result}
+
+ ); +}; + +export default KnownBreakages; diff --git a/packages/cli-dashboard/src/components/siteReport/tabs/cookies/cookiesLandingContainer/index.tsx b/packages/cli-dashboard/src/components/siteReport/tabs/cookies/cookiesLandingContainer/index.tsx index 1fb51a9f7..14b4eb77d 100644 --- a/packages/cli-dashboard/src/components/siteReport/tabs/cookies/cookiesLandingContainer/index.tsx +++ b/packages/cli-dashboard/src/components/siteReport/tabs/cookies/cookiesLandingContainer/index.tsx @@ -23,16 +23,27 @@ import { MenuBar, type CookiesLandingSection, type MenuData, -} from '@ps-analysis-tool/design-system'; -import type { TabCookies, TabFrames } from '@ps-analysis-tool/common'; + prepareCookiesCount, +} from '@google-psat/design-system'; +import type { LibraryData, TabCookies, TabFrames } from '@google-psat/common'; +import { I18n } from '@google-psat/i18n'; + +/** + * Internal dependencies. + */ import CookiesSection from './cookieLanding/cookiesSection'; import BlockedCookiesSection from './cookieLanding/blockedCookiesSection'; +import KnownBreakages from './cookieLanding/knownBreakages'; +import ExemptedCookiesSection from './cookieLanding/exemptedCookiesSection'; interface CookiesLandingContainerProps { tabFrames: TabFrames; tabCookies: TabCookies; cookiesWithIssues: TabCookies; downloadReport?: () => void; + libraryMatches: LibraryData | null; + isSiteMapLandingContainer?: boolean; + menuBarScrollContainerId?: string; } const CookiesLandingContainer = ({ @@ -40,11 +51,16 @@ const CookiesLandingContainer = ({ tabCookies, cookiesWithIssues, downloadReport, + libraryMatches, + isSiteMapLandingContainer = false, + menuBarScrollContainerId = 'dashboard-layout-container', }: CookiesLandingContainerProps) => { - const sections: Array = useMemo( - () => [ + const cookieStats = prepareCookiesCount(tabCookies); + + const sections: Array = useMemo(() => { + const baseSections: Array = [ { - name: 'Cookies', + name: I18n.getMessage('cookies'), link: 'cookies', panel: { Element: CookiesSection, @@ -55,7 +71,7 @@ const CookiesLandingContainer = ({ }, }, { - name: 'Blocked Cookies', + name: I18n.getMessage('blockedCookies'), link: 'blocked-cookies', panel: { Element: BlockedCookiesSection, @@ -66,9 +82,41 @@ const CookiesLandingContainer = ({ }, }, }, - ], - [tabCookies, tabFrames, cookiesWithIssues] - ); + { + name: 'Exempted Cookies', + link: 'exempted-cookies', + panel: { + Element: ExemptedCookiesSection, + props: { + cookieStats, + tabFrames, + }, + }, + }, + ]; + + if (!isSiteMapLandingContainer) { + baseSections.push({ + name: I18n.getMessage('knownBreakages'), + link: 'known-breakages', + panel: { + Element: KnownBreakages, + props: { + libraryMatches: libraryMatches ?? {}, + }, + }, + }); + } + + return baseSections; + }, [ + tabCookies, + tabFrames, + cookiesWithIssues, + cookieStats, + isSiteMapLandingContainer, + libraryMatches, + ]); const menuData: MenuData = useMemo( () => sections.map(({ name, link }) => ({ name, link })), @@ -82,7 +130,7 @@ const CookiesLandingContainer = ({ disableReportDownload={false} downloadReport={downloadReport} menuData={menuData} - scrollContainerId="dashboard-layout-container" + scrollContainerId={menuBarScrollContainerId} /> {sections.map(({ link, panel: { Element, props } }) => (
diff --git a/packages/cli-dashboard/src/components/siteReport/tabs/cookies/cookiesListing/index.tsx b/packages/cli-dashboard/src/components/siteReport/tabs/cookies/cookiesListing/index.tsx index 4e4bf7006..d53edda03 100644 --- a/packages/cli-dashboard/src/components/siteReport/tabs/cookies/cookiesListing/index.tsx +++ b/packages/cli-dashboard/src/components/siteReport/tabs/cookies/cookiesListing/index.tsx @@ -19,8 +19,8 @@ */ import React, { useMemo, useState } from 'react'; import { Resizable } from 're-resizable'; -import { CookieDetails, CookieTable } from '@ps-analysis-tool/design-system'; -import type { CookieTableData } from '@ps-analysis-tool/common'; +import { CookieDetails, CookieTable } from '@google-psat/design-system'; +import { type CookieTableData } from '@google-psat/common'; /** * Internal dependencies @@ -45,8 +45,9 @@ const CookiesListing = ({ [frame: string]: CookieTableData | null; } | null>(null); - const { tabCookies } = useContentStore(({ state }) => ({ + const { tabCookies, path } = useContentStore(({ state }) => ({ tabCookies: state.tabCookies, + path: state.path, })); const cookies = useMemo( @@ -66,7 +67,7 @@ const CookiesListing = ({ } = useCookieListing( Object.values(tabCookies), selectedFrameUrl, - 'cookiesListing', + 'cookiesListing' + selectedSite, selectedSite ); @@ -94,6 +95,8 @@ const CookiesListing = ({ selectedFrameCookie={selectedFrameCookie} setSelectedFrameCookie={setSelectedFrameCookie} isFiltersSidebarOpen={isSidebarOpen} + isCLI + hostname={path} /> { - const { tabCookies, completeJson } = useContentStore(({ state }) => ({ - tabCookies: state.tabCookies, - completeJson: state.completeJson, - })); - - const tabFrames = useMemo( - () => - Object.values(tabCookies).reduce((acc, cookie) => { - (cookie.frameUrls as string[]).forEach((url) => { - if (url?.includes('http') || url === UNKNOWN_FRAME_KEY) { - acc[url] = {} as TabFrames[string]; - } - }); - return acc; - }, {} as TabFrames), - [tabCookies] + const { tabCookies, completeJson, libraryMatches } = useContentStore( + ({ state }) => ({ + tabCookies: state.tabCookies, + completeJson: state.completeJson, + libraryMatches: state.libraryMatches, + }) ); + const tabFrames = useMemo(() => { + const frames = Object.keys( + completeJson?.[0].cookieData ?? {} + ).reduce((acc, url) => { + if (url?.includes('http')) { + acc[url] = {} as TabFrames[string]; + } + + return acc; + }, {}); + + return frames; + }, [completeJson]); + const cookiesWithIssues = useMemo( () => Object.fromEntries( @@ -60,9 +64,18 @@ const CookiesTab = ({ selectedFrameUrl, selectedSite }: CookiesTabProps) => { return; } if (completeJson.length > 1) { - generateSiteReportandDownload(completeJson, selectedSite); + generateSiteReportandDownload( + completeJson, + //@ts-ignore + atob(globalThis.PSAT_REPORT_HTML), + selectedSite + ); } else { - generateSiteReportandDownload(completeJson); + generateSiteReportandDownload( + completeJson, + //@ts-ignore + atob(globalThis.PSAT_REPORT_HTML) + ); } }, [completeJson, selectedSite]); @@ -76,6 +89,7 @@ const CookiesTab = ({ selectedFrameUrl, selectedSite }: CookiesTabProps) => { ) : (
I18n.getMessage('cookies'), children: {}, icon: { Element: CookieIcon, @@ -37,7 +38,7 @@ const Tabs: SidebarItems = { dropdownOpen: true, }, [SIDEBAR_ITEMS_KEYS.COOKIES_WITH_ISSUES]: { - title: 'Cookie Issues', + title: () => I18n.getMessage('cookieIssues'), children: {}, icon: { Element: WarningBare, diff --git a/packages/cli-dashboard/src/components/siteReport/tabs/technologies/index.tsx b/packages/cli-dashboard/src/components/siteReport/tabs/technologies/index.tsx index 416e0d67d..dc416c6a4 100644 --- a/packages/cli-dashboard/src/components/siteReport/tabs/technologies/index.tsx +++ b/packages/cli-dashboard/src/components/siteReport/tabs/technologies/index.tsx @@ -26,8 +26,9 @@ import { type TableRow, type TableFilter, TableProvider, -} from '@ps-analysis-tool/design-system'; -import { noop, type TechnologyData } from '@ps-analysis-tool/common'; +} from '@google-psat/design-system'; +import { noop, type TechnologyData } from '@google-psat/common'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies @@ -46,34 +47,30 @@ const Technologies = ({ selectedSite }: TechnologiesProps) => { const tableColumns = useMemo( () => [ { - header: 'Name', + header: I18n.getMessage('name'), accessorKey: 'name', cell: (info: InfoType) => info, enableHiding: false, }, { - header: 'Description', + header: I18n.getMessage('description'), accessorKey: 'description', - cell: (info: InfoType) => ( -

- {info} -

- ), + cell: (info: InfoType) => info, }, { - header: 'Confidence', + header: I18n.getMessage('confidence'), accessorKey: 'confidence', cell: (info: InfoType) => ( {info + '%'} ), }, { - header: 'Website', + header: I18n.getMessage('website'), accessorKey: 'website', cell: (info: InfoType) => info, }, { - header: 'Category', + header: I18n.getMessage('category'), accessorKey: 'categories', cell: (info: InfoType) => (info as TechnologyData['categories']).map((i) => i.name).join(' | '), @@ -134,7 +131,7 @@ const Technologies = ({ selectedSite }: TechnologiesProps) => { {selectedRow.name && ( <>

- Technology Details + {I18n.getMessage('technologyDetails')}

{selectedRow.name} @@ -143,17 +140,17 @@ const Technologies = ({ selectedSite }: TechnologiesProps) => { )} <>

- Description + {I18n.getMessage('description')}

- {selectedRow?.description || 'No description available.'} + {selectedRow?.description || I18n.getMessage('noDescription')}

) : (

- Select row to preview its value + {I18n.getMessage('selectRowToPreview')}

)} diff --git a/packages/cli-dashboard/src/components/utils/NamePrefixIconSelector.tsx b/packages/cli-dashboard/src/components/utils/NamePrefixIconSelector.tsx new file mode 100644 index 000000000..085330aab --- /dev/null +++ b/packages/cli-dashboard/src/components/utils/NamePrefixIconSelector.tsx @@ -0,0 +1,96 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * External dependencies. + */ +import React from 'react'; +import { BLOCK_STATUS, type CookieTableData } from '@google-psat/common'; +import { + type TableRow, + GreenTick, + InboundIcon, + OutboundIcon, + QuestionMark, + OutboundInboundIcon, + OutboundInboundColoredIcon, +} from '@google-psat/design-system'; + +const NamePrefixIconSelector = ({ originalData }: TableRow) => { + const data = originalData as CookieTableData; + + const isDomainInAllowList = data?.isDomainInAllowList; + if (isDomainInAllowList) { + return ; + } + + const isInboundBlocked = + data?.blockingStatus?.inboundBlock !== undefined && + data.blockingStatus.inboundBlock !== BLOCK_STATUS.NOT_BLOCKED; + const isInboundBlockedInAll = + data?.blockingStatus?.inboundBlock === BLOCK_STATUS.BLOCKED_IN_ALL_EVENTS; + + const isOutboundBlocked = + data?.blockingStatus?.outboundBlock !== undefined && + data.blockingStatus.outboundBlock !== BLOCK_STATUS.NOT_BLOCKED; + const isOutboundBlockedInAll = + data?.blockingStatus?.outboundBlock === BLOCK_STATUS.BLOCKED_IN_ALL_EVENTS; + + const hasValidBlockedReason = + data?.blockedReasons && data.blockedReasons.length !== 0; + + if (!hasValidBlockedReason) { + if (isInboundBlocked || isOutboundBlocked) { + return ; + } + + return <>; + } + + if (isInboundBlocked && isOutboundBlocked) { + if (isInboundBlockedInAll && isOutboundBlockedInAll) { + return ; + } + + if (!isInboundBlockedInAll && !isOutboundBlockedInAll) { + return ; + } + + if (isInboundBlockedInAll) { + return ; + } + + return ; + } + + if (isInboundBlocked) { + if (isInboundBlockedInAll) { + return ; + } + return ; + } + + if (isOutboundBlocked) { + if (isOutboundBlockedInAll) { + return ; + } + return ; + } + + return <>; +}; + +export default NamePrefixIconSelector; diff --git a/packages/cli-dashboard/src/components/utils/extractCookies.ts b/packages/cli-dashboard/src/components/utils/extractCookies.ts index ccb6c8abf..be599791d 100644 --- a/packages/cli-dashboard/src/components/utils/extractCookies.ts +++ b/packages/cli-dashboard/src/components/utils/extractCookies.ts @@ -18,7 +18,7 @@ import { calculateEffectiveExpiryDate, type CompleteJson, type CookieFrameStorageType, -} from '@ps-analysis-tool/common'; +} from '@google-psat/common'; /** * Internal dependencies diff --git a/packages/cli-dashboard/src/components/utils/extractReportData.ts b/packages/cli-dashboard/src/components/utils/extractReportData.ts index 8970695a2..803e71932 100644 --- a/packages/cli-dashboard/src/components/utils/extractReportData.ts +++ b/packages/cli-dashboard/src/components/utils/extractReportData.ts @@ -20,8 +20,9 @@ import type { CompleteJson, CookieFrameStorageType, + LibraryData, TechnologyData, -} from '@ps-analysis-tool/common'; +} from '@google-psat/common'; /** * Internal dependencies @@ -29,13 +30,11 @@ import type { import extractCookies from './extractCookies'; const extractReportData = (data: CompleteJson[]) => { - const cookies = {}; - const technologies: TechnologyData[] = []; const landingPageCookies = {}; + const technologies: TechnologyData[] = []; + const consolidatedLibraryMatches: { [url: string]: LibraryData } = {}; - data.forEach(({ cookieData, technologyData, pageUrl }) => { - formatCookieData(extractCookies(cookieData, pageUrl), cookies); - + data.forEach(({ cookieData, pageUrl, libraryMatches, technologyData }) => { formatCookieData( extractCookies(cookieData, pageUrl, true), landingPageCookies @@ -47,12 +46,13 @@ const extractReportData = (data: CompleteJson[]) => { pageUrl, })) ); + + consolidatedLibraryMatches[pageUrl] = libraryMatches; }); return { - cookies, - technologies, landingPageCookies, + consolidatedLibraryMatches, }; }; diff --git a/packages/cli-dashboard/src/components/utils/reportDownloader/generateSiteMapReportandDownload.ts b/packages/cli-dashboard/src/components/utils/reportDownloader/generateSiteMapReportandDownload.ts index 6dd53a5b1..b2e1a959e 100644 --- a/packages/cli-dashboard/src/components/utils/reportDownloader/generateSiteMapReportandDownload.ts +++ b/packages/cli-dashboard/src/components/utils/reportDownloader/generateSiteMapReportandDownload.ts @@ -19,7 +19,7 @@ */ import JSZip from 'jszip'; import { saveAs } from 'file-saver'; -import type { CompleteJson } from '@ps-analysis-tool/common'; +import type { CompleteJson } from '@google-psat/common'; /** * Internal dependencies @@ -28,6 +28,7 @@ import { createZip, getFolderName, generateSiemapHTMLFile } from './utils'; const generateSiteMapReportandDownload = async ( JSONReport: CompleteJson[], + reportHTML: string, sitemapUrl: string ) => { if (!JSONReport.length) { @@ -51,10 +52,10 @@ const generateSiteMapReportandDownload = async ( return; } - createZip(data, zipFolder, data.pageUrl); + createZip(data, zipFolder, data.pageUrl, reportHTML); }); - const report = generateSiemapHTMLFile(JSONReport, sitemapUrl); + const report = generateSiemapHTMLFile(JSONReport, sitemapUrl, reportHTML); zip.file('report.html', report); diff --git a/packages/cli-dashboard/src/components/utils/reportDownloader/generateSiteReportandDownload.ts b/packages/cli-dashboard/src/components/utils/reportDownloader/generateSiteReportandDownload.ts index b33d071e1..c8a06706d 100644 --- a/packages/cli-dashboard/src/components/utils/reportDownloader/generateSiteReportandDownload.ts +++ b/packages/cli-dashboard/src/components/utils/reportDownloader/generateSiteReportandDownload.ts @@ -18,7 +18,7 @@ */ import JSZip from 'jszip'; import { saveAs } from 'file-saver'; -import type { CompleteJson } from '@ps-analysis-tool/common'; +import type { CompleteJson } from '@google-psat/common'; /** * Internal dependencies @@ -27,6 +27,7 @@ import { createZip, getFolderName } from './utils'; const generateSiteReportandDownload = async ( JSONReport: CompleteJson[], + reportHTML: string, selectedPageUrl?: string | null ) => { if (!JSONReport.length) { @@ -61,7 +62,7 @@ const generateSiteReportandDownload = async ( return; } - createZip(siteAnalysisData, zipFolder, JSONReport[0].pageUrl); + createZip(siteAnalysisData, zipFolder, JSONReport[0].pageUrl, reportHTML); const content = await zip.generateAsync({ type: 'blob' }); saveAs( diff --git a/packages/cli-dashboard/src/components/utils/reportDownloader/utils.ts b/packages/cli-dashboard/src/components/utils/reportDownloader/utils.ts index 537c542d6..8e939ece3 100644 --- a/packages/cli-dashboard/src/components/utils/reportDownloader/utils.ts +++ b/packages/cli-dashboard/src/components/utils/reportDownloader/utils.ts @@ -25,14 +25,14 @@ import { generateTechnologyCSV, type CompleteJson, type DataMapping, - UNKNOWN_FRAME_KEY, type TabFrames, -} from '@ps-analysis-tool/common'; +} from '@google-psat/common'; import { prepareCookieStatsComponents, prepareCookiesCount, prepareFrameStatsComponent, -} from '@ps-analysis-tool/design-system'; +} from '@google-psat/design-system'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies @@ -72,7 +72,7 @@ function generateReportObject(analysisData: CompleteJson, siteURL: string) { const tabFrames = Object.values(tabCookies).reduce((acc, cookie) => { (cookie.frameUrls as string[]).forEach((url) => { - if (url?.includes('http') || url === UNKNOWN_FRAME_KEY) { + if (url?.includes('http')) { acc[url] = {} as TabFrames[string]; } }); @@ -85,17 +85,17 @@ function generateReportObject(analysisData: CompleteJson, siteURL: string) { const cookieClassificationDataMapping: DataMapping[] = [ { - title: 'Total cookies', + title: I18n.getMessage('totalCookies'), count: cookieStats.total, data: cookiesStatsComponents.legend, }, { - title: '1st party cookies', + title: I18n.getMessage('firstPartyCookies'), count: cookieStats.firstParty.total, data: cookiesStatsComponents.firstParty, }, { - title: '3rd party cookies', + title: I18n.getMessage('thirdPartyCookies'), count: cookieStats.thirdParty.total, data: cookiesStatsComponents.thirdParty, }, @@ -103,7 +103,7 @@ function generateReportObject(analysisData: CompleteJson, siteURL: string) { const blockedCookieDataMapping: DataMapping[] = [ { - title: 'Blocked cookies', + title: I18n.getMessage('blockedCookies'), count: cookieStats.blockedCookies.total, data: cookiesStatsComponents.blocked, }, @@ -111,7 +111,7 @@ function generateReportObject(analysisData: CompleteJson, siteURL: string) { const exemptedCookiesDataMapping: DataMapping[] = [ { - title: 'Exempted cookies', + title: I18n.getMessage('exemptedCookies'), count: cookieStats.exemptedCookies.total, data: cookiesStatsComponents.exempted, }, @@ -121,7 +121,7 @@ function generateReportObject(analysisData: CompleteJson, siteURL: string) { cookieClassificationDataMapping, tabCookies, cookiesStatsComponents, - libraryDetection: {}, + libraryMatches: analysisData.libraryMatches, tabFrames, showInfoIcon: true, showHorizontalMatrix: false, @@ -131,6 +131,9 @@ function generateReportObject(analysisData: CompleteJson, siteURL: string) { exemptedCookiesDataMapping, showBlockedCategory: true, url: siteURL, + source: 'cli', + // @ts-ignore - 'typeof globalThis' has no index signature. + translations: globalThis?.PSAT_DATA?.translations, }; } @@ -150,7 +153,7 @@ function generateSitemapReportObject( const tabFrames = Object.values(tabCookies).reduce((acc, cookie) => { (cookie.frameUrls as string[]).forEach((url) => { - if (url?.includes('http') || url === UNKNOWN_FRAME_KEY) { + if (url?.includes('http')) { acc[url] = {} as TabFrames[string]; } }); @@ -163,17 +166,17 @@ function generateSitemapReportObject( const cookieClassificationDataMapping: DataMapping[] = [ { - title: 'Total cookies', + title: I18n.getMessage('totalCookies'), count: cookieStats.total, data: cookiesStatsComponents.legend, }, { - title: '1st party cookies', + title: I18n.getMessage('firstPartyCookies'), count: cookieStats.firstParty.total, data: cookiesStatsComponents.firstParty, }, { - title: '3rd party cookies', + title: I18n.getMessage('thirdPartyCookies'), count: cookieStats.thirdParty.total, data: cookiesStatsComponents.thirdParty, }, @@ -181,7 +184,7 @@ function generateSitemapReportObject( const blockedCookieDataMapping: DataMapping[] = [ { - title: 'Blocked cookies', + title: I18n.getMessage('blockedCookies'), count: cookieStats.blockedCookies.total, data: cookiesStatsComponents.blocked, }, @@ -189,7 +192,7 @@ function generateSitemapReportObject( const exemptedCookiesDataMapping: DataMapping[] = [ { - title: 'Exempted cookies', + title: I18n.getMessage('exemptedCookies'), count: cookieStats.exemptedCookies.total, data: cookiesStatsComponents.exempted, }, @@ -210,13 +213,18 @@ function generateSitemapReportObject( showFramesSection: false, showBlockedCategory: true, url: sitemapURL, + // @ts-ignore - 'typeof globalThis' has no index signature + translations: globalThis?.PSAT_DATA?.translations, }; } -const generateHTMLFile = async (analysisData: CompleteJson, url: string) => { - const htmlText = await (await fetch('./report/index.html')).text(); +const generateHTMLFile = ( + analysisData: CompleteJson, + url: string, + reportHTML: string +) => { const parser = new DOMParser(); - const reportDom = parser.parseFromString(htmlText, 'text/html'); + const reportDom = parser.parseFromString(reportHTML, 'text/html'); // Injections const script = reportDom.createElement('script'); @@ -234,13 +242,13 @@ const generateHTMLFile = async (analysisData: CompleteJson, url: string) => { return html; }; -export const generateSiemapHTMLFile = async ( +export const generateSiemapHTMLFile = ( analysisData: CompleteJson[], - sitemapURL: string + sitemapURL: string, + reportHTML: string ) => { - const htmlText = await (await fetch('./report/index.html')).text(); const parser = new DOMParser(); - const reportDom = parser.parseFromString(htmlText, 'text/html'); + const reportDom = parser.parseFromString(reportHTML, 'text/html'); // Injections const script = reportDom.createElement('script'); @@ -261,7 +269,8 @@ export const generateSiemapHTMLFile = async ( export const createZip = ( analysisData: CompleteJson, zipObject: JSZip, - url: string + url: string, + reportHTML: string ) => { const { allCookiesCSV, @@ -270,7 +279,7 @@ export const createZip = ( summaryDataCSV, } = generateCSVFiles(analysisData); - const file = generateHTMLFile(analysisData, url); + const file = generateHTMLFile(analysisData, url, reportHTML); zipObject.file('cookies.csv', allCookiesCSV); if (technologyDataCSV) { diff --git a/packages/cli-dashboard/src/components/utils/reshapeCookies.ts b/packages/cli-dashboard/src/components/utils/reshapeCookies.ts index f0497b5d4..17357a180 100644 --- a/packages/cli-dashboard/src/components/utils/reshapeCookies.ts +++ b/packages/cli-dashboard/src/components/utils/reshapeCookies.ts @@ -18,11 +18,13 @@ * External dependencies */ import { - UNKNOWN_FRAME_KEY, type CookieTableData, type CookieData, type CookieFrameStorageType, -} from '@ps-analysis-tool/common'; + type BlockedReason, + deriveBlockingStatus, +} from '@google-psat/common'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies @@ -30,14 +32,52 @@ import { const reshapeCookies = (cookies: CookieFrameStorageType) => { return Object.entries(cookies) - .filter(([frame]) => frame.includes('http') || frame === UNKNOWN_FRAME_KEY) + .filter(([frame]) => frame.includes('http')) .map(([frame, _cookies]) => createCookieObj(frame, _cookies)) .reduce((acc, cookieObj) => { Object.keys(cookieObj).forEach((key) => { if (acc[key]) { - (acc[key].frameUrls as string[]).push( - ...(cookieObj[key].frameUrls as string[]) + const frameUrls: string[] = [ + ...(acc[key]?.frameUrls ?? []), + ...(cookieObj[key]?.frameUrls ?? []), + ]; + + const blockedReasons: BlockedReason[] = [ + ...new Set([ + ...(acc[key]?.blockedReasons ?? []), + ...(cookieObj[key]?.blockedReasons ?? []), + ]), + ]; + + const frameIdList = Array.from( + new Set([ + ...((acc[key]?.frameIdList ?? []) as number[]), + ...((cookieObj[key]?.frameIdList ?? []) as number[]), + ]) ); + + const networkEvents: CookieData['networkEvents'] = { + requestEvents: [ + ...(cookieObj[key]?.networkEvents?.requestEvents || []), + ...(acc[key].networkEvents?.requestEvents || []), + ], + responseEvents: [ + ...(cookieObj[key]?.networkEvents?.responseEvents || []), + ...(acc[key].networkEvents?.responseEvents || []), + ], + }; + + acc[key] = { + ...cookieObj[key], + ...acc[key], + blockedReasons, + frameIdList, + exemptionReason: + acc[key]?.exemptionReason || cookieObj[key]?.exemptionReason, + frameUrls, + networkEvents, + blockingStatus: deriveBlockingStatus(networkEvents), + }; } else { acc[key] = cookieObj[key]; } @@ -56,7 +96,9 @@ const createCookieObj = ( Object.fromEntries( Object.values(cookies).map((cookie) => [ cookie.parsedCookie.name + + ':' + cookie.parsedCookie.domain + + ':' + cookie.parsedCookie.path, { parsedCookie: cookie.parsedCookie, @@ -64,7 +106,7 @@ const createCookieObj = ( ...cookie.analytics, category: cookie.analytics?.category === 'Unknown Category' - ? 'Uncategorized' + ? I18n.getMessage('sdUncategorized') : cookie.analytics?.category, } as CookieTableData['analytics'], url: cookie.url, @@ -73,7 +115,10 @@ const createCookieObj = ( isFirstParty: cookie.isFirstParty, frameIdList: [frame], // Hot fix: For Displaying cookies in CLI Dashboard. isBlocked: cookie.isBlocked, + networkEvents: cookie.networkEvents, + blockingStatus: cookie.blockingStatus, frameUrls: [frame], + exemptionReason: cookie.exemptionReason, } as CookieTableData, ]) ); diff --git a/packages/cli-dashboard/src/components/utils/tests/data.mock.ts b/packages/cli-dashboard/src/components/utils/tests/data.mock.ts index 3e0db39c9..c94cb79e0 100644 --- a/packages/cli-dashboard/src/components/utils/tests/data.mock.ts +++ b/packages/cli-dashboard/src/components/utils/tests/data.mock.ts @@ -17,13 +17,13 @@ /** * External dependencies */ -import { UNKNOWN_FRAME_KEY, type CompleteJson } from '@ps-analysis-tool/common'; +import { type CompleteJson } from '@google-psat/common'; export const tempSinglePageData: CompleteJson = { pageUrl: 'https://edition.cnn.com/sitemaps/sitemap-section.xml', + libraryMatches: {}, cookieData: { 'https://edition.cnn.com': { - cookiesCount: 1, frameCookies: { 'countryCode:.cnn.com:/': { parsedCookie: { @@ -48,10 +48,6 @@ export const tempSinglePageData: CompleteJson = { }, }, }, - [UNKNOWN_FRAME_KEY]: { - frameCookies: {}, - cookiesCount: 0, - }, }, technologyData: [ { @@ -76,6 +72,7 @@ export const tempSinglePageData: CompleteJson = { export const tempMultiPageData: CompleteJson[] = [ { + libraryMatches: {}, pageUrl: 'https://www.cnn.com/index.html', technologyData: [ { @@ -98,7 +95,6 @@ export const tempMultiPageData: CompleteJson[] = [ ], cookieData: { 'https://edition.cnn.com': { - cookiesCount: 1, frameCookies: { 'countryCode:.cnn.com:/': { parsedCookie: { @@ -124,13 +120,10 @@ export const tempMultiPageData: CompleteJson[] = [ }, }, }, - [UNKNOWN_FRAME_KEY]: { - frameCookies: {}, - cookiesCount: 1, - }, }, }, { + libraryMatches: {}, pageUrl: 'https://edition.cnn.com/index.html', technologyData: [ { @@ -174,15 +167,11 @@ export const tempMultiPageData: CompleteJson[] = [ }, url: '', isBlocked: false, - blockedReasons: ['ExcludeDomainNonASCII'], + blockedReasons: ['ThirdPartyPhaseout'], isFirstParty: true, }, }, }, - [UNKNOWN_FRAME_KEY]: { - frameCookies: {}, - cookiesCount: 1, - }, }, }, ]; diff --git a/packages/cli-dashboard/src/components/utils/tests/extractCookies.ts b/packages/cli-dashboard/src/components/utils/tests/extractCookies.ts index 75047960c..a5bf35483 100644 --- a/packages/cli-dashboard/src/components/utils/tests/extractCookies.ts +++ b/packages/cli-dashboard/src/components/utils/tests/extractCookies.ts @@ -16,7 +16,6 @@ /** * Internal dependencies */ -import { UNKNOWN_FRAME_KEY } from '@ps-analysis-tool/common'; import extractCookies from '../extractCookies'; import { tempSinglePageData } from './data.mock'; @@ -56,7 +55,6 @@ describe('extractCookies', () => { isFirstParty: true, }, }, - [UNKNOWN_FRAME_KEY]: {}, }); isLandingPage = false; @@ -88,7 +86,6 @@ describe('extractCookies', () => { isFirstParty: true, }, }, - [UNKNOWN_FRAME_KEY]: {}, }); }); }); diff --git a/packages/cli-dashboard/src/components/utils/tests/extractReportData.ts b/packages/cli-dashboard/src/components/utils/tests/extractReportData.ts index e90845657..52b170abb 100644 --- a/packages/cli-dashboard/src/components/utils/tests/extractReportData.ts +++ b/packages/cli-dashboard/src/components/utils/tests/extractReportData.ts @@ -14,10 +14,6 @@ * limitations under the License. */ -/** - * External dependencies - */ -import { UNKNOWN_FRAME_KEY } from '@ps-analysis-tool/common'; /** * Internal dependencies */ @@ -27,103 +23,13 @@ import { tempMultiPageData } from './data.mock'; describe('extractReportData', () => { it('should return an empty object if no cookies are present', () => { expect(extractReportData([])).toEqual({ - cookies: {}, - technologies: [], landingPageCookies: {}, + consolidatedLibraryMatches: {}, }); }); it('should return an object with the cookies', () => { expect(extractReportData(tempMultiPageData)).toEqual({ - cookies: { - 'https://edition.cnn.com': { - ['countryCode:.cnn.com:/https://www.cnn.com/index.html']: { - parsedCookie: { - name: 'countryCode', - domain: '.cnn.com', - path: '/', - value: 'IN', - sameSite: 'None', - expires: 'Session', - httpOnly: false, - secure: true, - }, - analytics: { - platform: 'Unknown', - category: 'Uncategorized', - GDPR: '', - description: '', - }, - url: '', - pageUrl: 'https://www.cnn.com/index.html', - isBlocked: false, - blockedReasons: ['ThirdPartyPhaseout'], - isFirstParty: true, - }, - ['countryCode:.cnn.com:/https://edition.cnn.com/index.html']: { - parsedCookie: { - name: 'countryCode', - domain: '.cnn.com', - path: '/', - value: 'IN', - sameSite: 'None', - expires: 'Session', - httpOnly: false, - secure: true, - }, - analytics: { - platform: 'Unknown', - category: 'Uncategorized', - GDPR: '', - description: '', - }, - url: '', - pageUrl: 'https://edition.cnn.com/index.html', - isBlocked: false, - isFirstParty: true, - blockedReasons: ['ExcludeDomainNonASCII'], - }, - }, - [UNKNOWN_FRAME_KEY]: {}, - }, - technologies: [ - { - pageUrl: 'https://www.cnn.com/index.html', - slug: 'varnish', - name: 'Varnish', - description: 'Varnish is a reverse caching proxy.', - confidence: 100, - version: null, - icon: 'Varnish.svg', - website: 'https://www.varnish-cache.org', - cpe: 'cpe:2.3:a:varnish-software:varnish_cache:*:*:*:*:*:*:*:*', - categories: [ - { - id: 23, - slug: 'caching', - name: 'Caching', - }, - ], - }, - { - pageUrl: 'https://edition.cnn.com/index.html', - slug: 'varnish', - name: 'Varnish', - description: 'Varnish is a reverse caching proxy.', - confidence: 100, - version: null, - icon: 'Varnish.svg', - website: 'https://www.varnish-cache.org', - cpe: 'cpe:2.3:a:varnish-software:varnish_cache:*:*:*:*:*:*:*:*', - categories: [ - { - id: 23, - slug: 'caching', - name: 'Caching', - }, - ], - }, - ], landingPageCookies: { 'https://edition.cnn.com': { ['countryCode:.cnn.com:/']: { @@ -146,11 +52,14 @@ describe('extractReportData', () => { url: '', pageUrl: 'https://edition.cnn.com/index.html', isBlocked: false, - blockedReasons: ['ThirdPartyPhaseout', 'ExcludeDomainNonASCII'], + blockedReasons: ['ThirdPartyPhaseout'], isFirstParty: true, }, }, - [UNKNOWN_FRAME_KEY]: {}, + }, + consolidatedLibraryMatches: { + 'https://edition.cnn.com/index.html': {}, + 'https://www.cnn.com/index.html': {}, }, }); }); diff --git a/packages/cli-dashboard/src/components/utils/tests/reshapeCookies.ts b/packages/cli-dashboard/src/components/utils/tests/reshapeCookies.ts index 33f5ba593..b29b2ccd8 100644 --- a/packages/cli-dashboard/src/components/utils/tests/reshapeCookies.ts +++ b/packages/cli-dashboard/src/components/utils/tests/reshapeCookies.ts @@ -14,10 +14,6 @@ * limitations under the License. */ -/** - * External dependencies - */ -import { UNKNOWN_FRAME_KEY } from '@ps-analysis-tool/common'; /** * Internal dependencies */ @@ -51,16 +47,17 @@ describe('reshapeCookies', () => { url: 'https://www.cnn.com/index.html', isBlocked: false, blockedReasons: [], + blockingStatus: undefined, + networkEvents: undefined, isFirstParty: true, pageUrl: 'https://www.cnn.com/index.html', frameUrls: { sadf: 'https://edition.cnn.com' }, }, }, - [UNKNOWN_FRAME_KEY]: {}, }; expect(reshapeCookies(cookies)).toEqual({ - 'countryCode.cnn.com/': { + 'countryCode:.cnn.com:/': { parsedCookie: { name: 'countryCode', value: 'IN', @@ -77,6 +74,8 @@ describe('reshapeCookies', () => { description: '', GDPR: '', }, + blockingStatus: undefined, + networkEvents: undefined, isFirstParty: true, url: 'https://www.cnn.com/index.html', frameUrls: ['https://edition.cnn.com'], diff --git a/packages/cli-dashboard/src/hooks/useCookieListing/index.tsx b/packages/cli-dashboard/src/hooks/useCookieListing/index.tsx index 306ae1a8a..2f26a7033 100644 --- a/packages/cli-dashboard/src/hooks/useCookieListing/index.tsx +++ b/packages/cli-dashboard/src/hooks/useCookieListing/index.tsx @@ -27,8 +27,15 @@ import { calculateDynamicFilterValues, evaluateSelectAllOption, evaluateStaticFilterValues, -} from '@ps-analysis-tool/design-system'; -import { type CookieTableData } from '@ps-analysis-tool/common'; + calculateExemptionReason, + type TableRow, +} from '@google-psat/design-system'; +import { I18n } from '@google-psat/i18n'; +import { type CookieTableData, BLOCK_STATUS } from '@google-psat/common'; +/** + * Internal dependencies + */ +import NamePrefixIconSelector from '../../components/utils/NamePrefixIconSelector'; const useCookieListing = ( tabCookies: CookieTableData[], @@ -51,54 +58,71 @@ const useCookieListing = ( const tableColumns = useMemo( () => [ { - header: 'Name', + header: I18n.getMessage('name'), accessorKey: 'parsedCookie.name', cell: (info: InfoType) => info, enableHiding: false, - widthWeightagePercentage: 15, + widthWeightagePercentage: 13, + enableBodyCellPrefixIcon: true, + bodyCellPrefixIcon: { + Element: NamePrefixIconSelector, + }, + showBodyCellPrefixIcon: (row: TableRow) => { + const isBlocked = Boolean( + (row.originalData as CookieTableData)?.blockingStatus + ?.inboundBlock !== BLOCK_STATUS.NOT_BLOCKED || + (row.originalData as CookieTableData)?.blockingStatus + ?.outboundBlock !== BLOCK_STATUS.NOT_BLOCKED + ); + + const isDomainInAllowList = Boolean( + (row.originalData as CookieTableData)?.isDomainInAllowList + ); + + return isBlocked || isDomainInAllowList; + }, }, { - header: 'Scope', + header: I18n.getMessage('scope'), accessorKey: 'isFirstParty', - cell: (info: InfoType) => ( -

- {!info ? 'Third Party' : 'First Party'} -

- ), + cell: (info: InfoType) => + !info ? I18n.getMessage('thirdParty') : I18n.getMessage('firstParty'), widthWeightagePercentage: 8, }, { - header: 'Domain', + header: I18n.getMessage('domain'), accessorKey: 'parsedCookie.domain', cell: (info: InfoType) => info, widthWeightagePercentage: 9, }, { - header: 'Partition Key', + header: I18n.getMessage('partitionKey'), accessorKey: 'parsedCookie.partitionKey', cell: (info: InfoType) => info, widthWeightagePercentage: 9, }, { - header: 'SameSite', + header: I18n.getMessage('sameSite'), accessorKey: 'parsedCookie.sameSite', - cell: (info: InfoType) => {info}, + cell: (info: InfoType) => + I18n.getMessage((info?.toString() || '').toLowerCase()), widthWeightagePercentage: 8, }, { - header: 'Category', + header: I18n.getMessage('category'), accessorKey: 'analytics.category', - cell: (info: InfoType) => info, + cell: (info: InfoType) => + I18n.getMessage((info as string).toLowerCase() || 'uncategorized'), widthWeightagePercentage: 10, }, { - header: 'Platform', + header: I18n.getMessage('platform'), accessorKey: 'analytics.platform', - cell: (info: InfoType) => info, + cell: (info: InfoType) => (info ? info : I18n.getMessage('unknown')), widthWeightagePercentage: 10, }, { - header: 'HttpOnly', + header: I18n.getMessage('httpOnly'), accessorKey: 'parsedCookie.httponly', cell: (info: InfoType) => (

@@ -108,7 +132,7 @@ const useCookieListing = ( widthWeightagePercentage: 5, }, { - header: 'Secure', + header: I18n.getMessage('secure'), accessorKey: 'parsedCookie.secure', cell: (info: InfoType) => (

@@ -118,21 +142,22 @@ const useCookieListing = ( widthWeightagePercentage: 5, }, { - header: 'Value', + header: I18n.getMessage('value'), accessorKey: 'parsedCookie.value', cell: (info: InfoType) => info, widthWeightagePercentage: 10, }, { - header: 'Path', + header: I18n.getMessage('path'), accessorKey: 'parsedCookie.path', cell: (info: InfoType) => info, widthWeightagePercentage: 4, }, { - header: 'Expires / Max-Age', + header: I18n.getMessage('expires'), accessorKey: 'parsedCookie.expires', - cell: (info: InfoType) => info, + cell: (info: InfoType) => + info === 'Session' || !info ? I18n.getMessage('session') : info, widthWeightagePercentage: 7, }, ], @@ -142,28 +167,36 @@ const useCookieListing = ( const filters = useMemo( () => ({ 'analytics.category': { - title: 'Category', + title: I18n.getMessage('category'), hasStaticFilterValues: true, hasPrecalculatedFilterValues: true, filterValues: calculateDynamicFilterValues( 'analytics.category', tabCookies, parsedQuery?.filter?.['analytics.category'], - clearActivePanelQuery + clearActivePanelQuery, + true ), sortValues: true, useGenericPersistenceKey: true, + comparator: (value: InfoType, filterValue: string) => { + const val = value as string; + return ( + I18n.getMessage(val?.toLowerCase() || 'uncategorized') === + filterValue + ); + }, }, isFirstParty: { - title: 'Scope', + title: I18n.getMessage('scope'), hasStaticFilterValues: true, hasPrecalculatedFilterValues: true, filterValues: evaluateStaticFilterValues( { - 'First Party': { + [I18n.getMessage('firstParty')]: { selected: false, }, - 'Third Party': { + [I18n.getMessage('thirdParty')]: { selected: false, }, }, @@ -174,40 +207,40 @@ const useCookieListing = ( useGenericPersistenceKey: true, comparator: (value: InfoType, filterValue: string) => { const val = Boolean(value); - return val === (filterValue === 'First Party'); + return val === (filterValue === I18n.getMessage('firstParty')); }, }, 'parsedCookie.domain': { - title: 'Domain', + title: I18n.getMessage('domain'), }, 'parsedCookie.httponly': { - title: 'HttpOnly', + title: I18n.getMessage('httpOnly'), hasStaticFilterValues: true, filterValues: { - True: { + [I18n.getMessage('true')]: { selected: false, }, - False: { + [I18n.getMessage('false')]: { selected: false, }, }, useGenericPersistenceKey: true, comparator: (value: InfoType, filterValue: string) => { const val = Boolean(value); - return val === (filterValue === 'True'); + return val === (filterValue === I18n.getMessage('true')); }, }, - 'parsedCookie.samesite': { - title: 'SameSite', + 'parsedCookie.sameSite': { + title: I18n.getMessage('sameSite'), hasStaticFilterValues: true, filterValues: { - None: { + [I18n.getMessage('none')]: { selected: false, }, - Lax: { + [I18n.getMessage('lax')]: { selected: false, }, - Strict: { + [I18n.getMessage('strict')]: { selected: false, }, }, @@ -218,42 +251,42 @@ const useCookieListing = ( }, }, 'parsedCookie.secure': { - title: 'Secure', + title: I18n.getMessage('secure'), hasStaticFilterValues: true, filterValues: { - True: { + [I18n.getMessage('true')]: { selected: false, }, - False: { + [I18n.getMessage('false')]: { selected: false, }, }, useGenericPersistenceKey: true, comparator: (value: InfoType, filterValue: string) => { const val = Boolean(value); - return val === (filterValue === 'True'); + return val === (filterValue === I18n.getMessage('true')); }, }, 'parsedCookie.path': { - title: 'Path', + title: I18n.getMessage('path'), }, 'parsedCookie.expires': { - title: 'Retention Period', + title: I18n.getMessage('retentionPeriod'), hasStaticFilterValues: true, filterValues: { - Session: { + [I18n.getMessage('session')]: { selected: false, }, - 'Short Term (< 24h)': { + [I18n.getMessage('shortTerm')]: { selected: false, }, - 'Medium Term (24h - 1 week)': { + [I18n.getMessage('mediumTerm')]: { selected: false, }, - 'Long Term (1 week - 1 month)': { + [I18n.getMessage('longTerm')]: { selected: false, }, - 'Extended Term (> 1 month)': { + [I18n.getMessage('extentedTerm')]: { selected: false, }, }, @@ -262,22 +295,22 @@ const useCookieListing = ( let diff = 0; const val = value as string; switch (filterValue) { - case 'Session': + case I18n.getMessage('session'): return val === 'Session'; - case 'Short Term (< 24h)': + case I18n.getMessage('shortTerm'): diff = Date.parse(val) - Date.now(); return diff < 86400000; - case 'Medium Term (24h - 1 week)': + case I18n.getMessage('mediumTerm'): diff = Date.parse(val) - Date.now(); return diff >= 86400000 && diff < 604800000; - case 'Long Term (1 week - 1 month)': + case I18n.getMessage('longTerm'): diff = Date.parse(val) - Date.now(); return diff >= 604800000 && diff < 2629743833; - case 'Extended Term (> 1 month)': + case I18n.getMessage('extentedTerm'): diff = Date.parse(val) - Date.now(); return diff >= 2629743833; @@ -287,7 +320,7 @@ const useCookieListing = ( }, }, 'analytics.platform': { - title: 'Platform', + title: I18n.getMessage('platform'), hasStaticFilterValues: true, hasPrecalculatedFilterValues: true, filterValues: calculateDynamicFilterValues( @@ -300,7 +333,7 @@ const useCookieListing = ( useGenericPersistenceKey: true, }, blockedReasons: { - title: 'Blocked Reasons', + title: I18n.getMessage('blockedReasons'), hasStaticFilterValues: true, hasPrecalculatedFilterValues: true, enableSelectAllOption: true, @@ -320,6 +353,26 @@ const useCookieListing = ( return (value as string[])?.includes(filterValue); }, }, + exemptionReason: { + title: 'Exemption Reason', + hasStaticFilterValues: true, + hasPrecalculatedFilterValues: true, + enableSelectAllOption: true, + isSelectAllOptionSelected: evaluateSelectAllOption( + 'exemptionReason', + parsedQuery + ), + filterValues: calculateExemptionReason( + tabCookies, + clearActivePanelQuery, + parsedQuery?.filter?.exemptionReason + ), + comparator: (value: InfoType, filterValue: string) => { + const val = value as string; + return val === filterValue; + }, + useGenericPersistenceKey: true, + }, }), [clearActivePanelQuery, parsedQuery, tabCookies] ); diff --git a/packages/cli-dashboard/src/index.tsx b/packages/cli-dashboard/src/index.tsx index 03d1bf7c0..448177745 100644 --- a/packages/cli-dashboard/src/index.tsx +++ b/packages/cli-dashboard/src/index.tsx @@ -19,7 +19,7 @@ */ import React from 'react'; import { createRoot } from 'react-dom/client'; -import { Provider as TablePersistentSettingsProvider } from '@ps-analysis-tool/design-system'; +import { Provider as TablePersistentSettingsProvider } from '@google-psat/design-system'; /** * Internal dependencies diff --git a/packages/cli-dashboard/tsconfig.json b/packages/cli-dashboard/tsconfig.json index 3035eb432..f66ebf06d 100644 --- a/packages/cli-dashboard/tsconfig.json +++ b/packages/cli-dashboard/tsconfig.json @@ -1,7 +1,10 @@ { "extends": "../../tsconfig.shared.json", "compilerOptions": { - "rootDir": "src" + "rootDir": "src", + "outDir": "dist", + "declaration": true, + "declarationDir": "dist-types" }, "include": [ "src", diff --git a/packages/cli/.eslintrc.json b/packages/cli/.eslintrc.json index 2f1aea123..1d126b36b 100644 --- a/packages/cli/.eslintrc.json +++ b/packages/cli/.eslintrc.json @@ -2,5 +2,6 @@ "rules": { "no-console": "off", "no-await-in-loop": "off" - } + }, + "ignorePatterns": ["dist/**", "dist-types/**"] } diff --git a/packages/cli/.npmignore b/packages/cli/.npmignore new file mode 100644 index 000000000..aa8e45f12 --- /dev/null +++ b/packages/cli/.npmignore @@ -0,0 +1 @@ +src/ \ No newline at end of file diff --git a/packages/cli/README.md b/packages/cli/README.md index 6a28c3af5..e0b8f875d 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -1,4 +1,96 @@ -# PSAT CLI +

+ +

-## Description -A CLI tool to help developers to prepare for 3PCD. \ No newline at end of file +## Privacy Sandbox Analysis Tool CLI + +> The Privacy Sandbox Analysis Tool CLI is designed to help developers audit their websites for the upcoming deprecation of third-party cookies (3PCD) in Chrome as part of the Privacy Sandbox initiative. + + +## Using the CLI + +PSAT CLI allows you to audit a single website or multiple websites using a sitemap or a CSV file, and generates a report that should help any user draft their future action items to prepare their website for third-party cookie depreciation. + +> Required Node Version: 18 or later + + +### Installation + +``` +npm i -g @google-psat/cli +``` + +### Usage + +``` +psat https://example.com +``` + + +By default, PSAT generates the report as an HTML file. You can open it in a web browser of your choice and use it to filter the output. + +### CLI option + +``` +Usage: psat [options] + +CLI to test a URL for third-party cookies + +Arguments: + url The URL of a website or sitemap you want to analyse. + +Options: + -V, --version output the version number + -u, --url URL of a site + -s, --sitemap-url URL of a sitemap + -c, --csv-path Path to a CSV file with a set of URLs. + -p, --sitemap-path Path to a sitemap saved in the file system + -l, --locale Locale to use for the CLI, supported: en, hi, es, ja, ko, pt-BR + -ul, --url-limit No of URLs to analyze + -nh, --no-headless Flag for running puppeteer in non-headless mode + -np, --no-prompts Flags for skipping all prompts. Default options will be used + -nt, --no-technology Flags for skipping technology analysis. + -d, --out-dir Directory path where the analysis data will be stored + -ab, --accept-banner This will accept the GDPR banner if present. + -h, --help display help for command + +``` + +### Output +After running the analysis, it will create a report in `/out/` directory +```bash +psat -u https://www.google.co.in +✓ Done analyzing cookies. +✓ Done analyzing technologies. +Report created successfully: /Users/username/projects/psat-cli-tool/out/www-google-co-in/index.html +``` + +Open the generated HTML file in the browser. + +![PSAT CLI Output](https://s3.amazonaws.com/i.snag.gy/Dyzq6N.jpg) + +## Docs + +- Learn more about the [Privacy Sandbox](https://privacysandbox.com/) initiative. +- Visit our [wiki](https://github.com/GoogleChromeLabs/ps-analysis-tool/wiki/) for more information about PSAT. +- Please refer to the [CLI output](https://github.com/GoogleChromeLabs/ps-analysis-tool/wiki/PSAT-Command-Line-Interface#cli-output) section on our wiki for a better understanding of the reports. +- To learn about cookie filters, check the [Cookie table](https://github.com/GoogleChromeLabs/ps-analysis-tool/wiki/Cookies-Table) section on the wiki. + + +## Contributing +We welcome your patches and contributions to this project. Whether you're a frequent contributor or addressing a specific issue that matters to you, we appreciate your input. + +To develop and contribute, please refer to our [contribution guide](https://github.com/GoogleChromeLabs/ps-analysis-tool/blob/main/docs/CONTRIBUTING.md) for detailed information. + + +## Using PSAT in Chrome extension + +PSAT is available as a Chrome extension that allows you to do an analysis while interacting with websites. + +### Installation: + +You can install the PSAT extension from the [Chrome web store](https://chromewebstore.google.com/detail/privacy-sandbox-analysis/ehbnpceebmgpanbbfckhoefhdibijkef) + +To run it: Visit the website you want to analyze, open Chrome DevTools, and select the Privacy Sandbox panel. + +![PSAT Chrome Extension](https://s3.amazonaws.com/i.snag.gy/OcCl5i.jpg) \ No newline at end of file diff --git a/packages/cli/babel.config.cjs b/packages/cli/babel.config.cjs new file mode 100644 index 000000000..6ab27bceb --- /dev/null +++ b/packages/cli/babel.config.cjs @@ -0,0 +1,36 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +module.exports = function (api) { + const isProduction = api.env('production'); + + return { + presets: [ + ['@babel/preset-env'], + [ + '@babel/preset-react', + { + development: !isProduction, + }, + ], + '@babel/preset-typescript', + ], + plugins: [ + ['@babel/plugin-transform-react-jsx'], + ['babel-plugin-styled-components'], + ], + sourceMaps: true, + }; +}; diff --git a/packages/cli/package.json b/packages/cli/package.json index 2dad638f4..fbb2bcd32 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,46 +1,64 @@ { - "name": "@ps-analysis-tool/cli", - "version": "0.8.0", + "name": "@google-psat/cli", + "version": "0.9.0-2", "description": "CLI tool for analysis", - "main": "index.js", + "main": "dist/index.js", + "types": "dist-types/index.d.ts", + "keywords": ["psat", "privacy sandbox", "google", "chrome", "3pcd"], + "files": [ + "assets", + "dist", + "dist-types" + ], "scripts": { - "build": "tsc --build", - "dev": "tsc-watch --build" + "build": "webpack --config ../../cli.webpack.config.cjs ", + "dev": "webpack --config ../../cli.webpack.config.cjs --watch", + "build:remove": "rimraf dist dist-types tsconfig.tsbuildinfo", + "publish:local": "npm publish --registry=http://localhost:4873", + "publish:remote": "npm publish --access=public --registry=https://registry.npmjs.org", + "unpublish:local": "npm unpublish --registry=http://localhost:4873 --force", + "unpublish:remote": "npm unpublish --registry=https://registry.npmjs.org --force" }, - "type": "module", + "bin": { + "psat": "./dist/main.js" + }, + "publishConfig": { + "access": "public" + }, + "type": "commonjs", "repository": { "type": "git", - "url": "git+https://github.com/GoogleChromeLabs/ps-analysis-tool" + "url": "https://github.com/GoogleChromeLabs/ps-analysis-tool", + "directory": "packages/cli" + }, + "author": { + "name": "Google" }, - "author": "", "license": "Apache-2.0", "bugs": { "url": "https://github.com/GoogleChromeLabs/ps-analysis-tool/issues" }, "homepage": "https://github.com/GoogleChromeLabs/ps-analysis-tool", "dependencies": { - "@ps-analysis-tool/common": "*", - "cli-color": "^2.0.3", - "cli-table": "^0.3.11", - "commander": "^10.0.1", + "@google-psat/analysis-utils": "*", + "@google-psat/cli-dashboard": "*", + "@google-psat/common": "*", + "@google-psat/i18n": "*", + "@google-psat/library-detection": "*", + "commander": "^10.0.1", "fs-extra": "^11.1.1", - "ora": "^7.0.1", "promptly": "^3.2.0", - "puppeteer": "^21.0.3", - "simple-cookie": "^1.0.15", "sitemapper": "^3.1.8", "spinnies": "^0.5.1", - "tldts": "^6.0.14", - "wappalyzer": "^6.10.66", "xml2js": "^0.6.2" }, "devDependencies": { - "@types/cli-color": "^2.0.2", "@types/fs-extra": "^11.0.1", "@types/node": "^20.2.3", "@types/promptly": "^3.0.2", "@types/xml2js": "^0.4.14", "tsc-watch": "^6.0.4", - "typescript": "^5.0.4" + "typescript": "^5.0.4", + "webpack": "^5.86.0" } } diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index b06aab2fc..59a6c29a9 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -1,3 +1,4 @@ +#!/usr/bin/env node /* * Copyright 2023 Google LLC * @@ -18,27 +19,35 @@ */ import { Command } from 'commander'; import events from 'events'; -import { ensureFile, writeFile } from 'fs-extra'; +import { existsSync, ensureFile, writeFile } from 'fs-extra'; // @ts-ignore Package does not support typescript. import Spinnies from 'spinnies'; -import { spawn } from 'child_process'; +import fs from 'fs'; import path from 'path'; -import { CompleteJson } from '@ps-analysis-tool/common'; +import { I18n } from '@google-psat/i18n'; +import { CompleteJson, LibraryData } from '@google-psat/common'; +import { + analyzeCookiesUrlsInBatchesAndFetchResources, + analyzeTechnologiesUrlsInBatches, +} from '@google-psat/analysis-utils'; +import { + DetectionFunctions, + Libraries, + detectMatchingSignatures, +} from '@google-psat/library-detection'; +import URL from 'node:url'; /** * Internal dependencies. */ -import Utility from './utils/utility'; -import { analyzeCookiesUrlsInBatches } from './procedures/analyzeCookieUrlsInBatches'; -import { analyzeTechnologiesUrlsInBatches } from './procedures/analyzeTechnologiesUrlsInBatches'; import { fetchDictionary, - delay, getUrlListFromArgs, validateArgs, saveCSVReports, + askUserInput, + generatePrefix, } from './utils'; -import { checkPortInUse } from './utils/checkPortInUse'; events.EventEmitter.defaultMaxListeners = 15; @@ -46,8 +55,9 @@ const DELAY_TIME = 20000; const program = new Command(); program - .version('0.8.0') + .version('0.9.0-2') .description('CLI to test a URL for 3p cookies') + .argument('[website-url]', 'The URL of website you want to analyse') .option('-u, --url ', 'URL of a site') .option('-s, --sitemap-url ', 'URL of a sitemap') .option('-c, --csv-path ', 'Path to a CSV file with a set of URLs.') @@ -55,7 +65,10 @@ program '-p, --sitemap-path ', 'Path to a sitemap saved in the file system' ) - .option('-po, --port ', 'A port for the CLI dashboard server.') + .option( + '-l, --locale ', + 'Locale to use for the CLI, supported: en, hi, es, ja, ko, pt-BR' + ) .option('-ul, --url-limit ', 'No of URLs to analyze') .option( '-nh, --no-headless ', @@ -77,7 +90,7 @@ program program.parse(); -const saveResults = async ( +const saveResultsAsJSON = async ( outDir: string, result: CompleteJson | CompleteJson[] ) => { @@ -85,27 +98,92 @@ const saveResults = async ( await writeFile(outDir + '/out.json', JSON.stringify(result, null, 4)); }; -const startDashboardServer = async (dir: string, port: number) => { - spawn(/^win/.test(process.platform) ? 'npm.cmd' : 'npm', [ - 'run', - 'cli-dashboard:dev', - '--', - '--port', - port.toString(), - ]); +const saveResultsAsHTML = async ( + outDir: string, + result: CompleteJson | CompleteJson[], + isSiteMap: boolean +) => { + let htmlText = ''; + let reportHTML = ''; + + if ( + existsSync( + path.resolve( + __dirname + + '../../node_modules/@google-psat/cli-dashboard/dist/index.html' + ) + ) + ) { + htmlText = fs.readFileSync( + path.resolve( + __dirname + + '../../node_modules/@google-psat/cli-dashboard/dist/index.html' + ), + 'utf-8' + ); + + reportHTML = fs.readFileSync( + path.resolve( + __dirname + + '../../node_modules/@google-psat/cli-dashboard/dist/report/index.html' + ), + 'base64' + ); + + fs.copyFileSync( + path.resolve( + __dirname + + '../../node_modules/@google-psat/cli-dashboard/dist/index.js' + ), + outDir + '/index.js' + ); + } else { + htmlText = fs.readFileSync( + path.resolve(__dirname + '../../../cli-dashboard/dist/index.html'), + 'utf-8' + ); + + reportHTML = fs.readFileSync( + path.resolve(__dirname + '../../../cli-dashboard/dist/report/index.html'), + 'base64' + ); - await delay(2000); + fs.copyFileSync( + path.resolve(__dirname + '../../../cli-dashboard/dist/index.js'), + outDir + '/index.js' + ); + } - console.log(`Report: http://localhost:${port}?dir=${dir}`); + const messages = I18n.getMessages(); + + const html = + htmlText.substring(0, htmlText.indexOf('')) + + `` + + htmlText.substring(htmlText.indexOf('')); + + const outFileFullDir = path.resolve(outDir + '/index.html'); + const htmlBlob = new Blob([html]); + const buffer = Buffer.from(await htmlBlob.arrayBuffer()); + + fs.writeFile(outDir + '/index.html', buffer, () => + console.log(`Report: ${URL.pathToFileURL(outFileFullDir)}`) + ); }; // eslint-disable-next-line complexity (async () => { - const url = program.opts().url; + const url = program.args?.[0] ?? program.opts().url; const sitemapUrl = program.opts().sitemapUrl; const csvPath = program.opts().csvPath; const sitemapPath = program.opts().sitemapPath; - const port = parseInt(program.opts().port || '9000'); + const locale = program.opts().locale; const numberOfUrlsInput = program.opts().urlLimit; const isHeadless = Boolean(program.opts().headless); const shouldSkipPrompts = !program.opts().prompts; @@ -120,25 +198,12 @@ const startDashboardServer = async (dir: string, port: number) => { sitemapPath, numberOfUrlsInput, outDir, - port + locale ); - //check if devserver port in already in use only if the dashboard is goint to be used - - if (!outDir) { - const isPortInUse = await checkPortInUse(port); - - if (isPortInUse) { - console.error( - `Error: Report server port ${port} already in use. You might be already running CLI` - ); - process.exit(1); - } - } - const prefix = url || sitemapUrl - ? Utility.generatePrefix(url || sitemapUrl) + ? generatePrefix(url || sitemapUrl) : path.parse(csvPath || sitemapPath).name; let outputDir; @@ -168,18 +233,14 @@ const startDashboardServer = async (dir: string, port: number) => { let userInput: string | null = null; if (!shouldSkipPrompts && !numberOfUrlsInput) { - userInput = await Utility.askUserInput( - `Provided ${sitemapUrl || sitemapPath ? 'Sitemap' : 'CSV file'} has ${ - urls.length - } pages. Please enter the number of pages you want to analyze (Default ${ - urls.length - }):`, + userInput = await askUserInput( + `Please enter the number of pages to analyze (Default: ${urls.length}):`, { default: urls.length.toString() } ); numberOfUrls = userInput && isNaN(parseInt(userInput)) ? urls.length - : parseInt(userInput); + : parseInt(userInput as string); } else if (numberOfUrlsInput) { console.log(`Analysing ${numberOfUrlsInput} urls.`); numberOfUrls = parseInt(numberOfUrlsInput); @@ -196,21 +257,24 @@ const startDashboardServer = async (dir: string, port: number) => { const cookieDictionary = await fetchDictionary(); spinnies.add('cookie-spinner', { - text: 'Analysing cookies on first site visit', + text: 'Analysing cookies on first site visit...', }); - const cookieAnalysisData = await analyzeCookiesUrlsInBatches( - urlsToProcess, - isHeadless, - DELAY_TIME, - cookieDictionary, - 3, - urlsToProcess.length !== 1 ? spinnies : undefined, - shouldSkipAcceptBanner - ); + const cookieAnalysisAndFetchedResourceData = + await analyzeCookiesUrlsInBatchesAndFetchResources( + urlsToProcess, + //@ts-ignore Fix type. + Libraries, + isHeadless, + DELAY_TIME, + cookieDictionary, + 3, + urlsToProcess.length !== 1 ? spinnies : undefined, + shouldSkipAcceptBanner + ); spinnies.succeed('cookie-spinner', { - text: 'Done analyzing cookies.', + text: 'Done analyzing cookies!', }); let technologyAnalysisData: any = null; @@ -227,28 +291,43 @@ const startDashboardServer = async (dir: string, port: number) => { ); spinnies.succeed('technology-spinner', { - text: 'Done analyzing technologies.', + text: 'Done analyzing technologies!', }); } const result = urlsToProcess.map((_url, ind) => { + const detectedMatchingSignatures: LibraryData = { + ...detectMatchingSignatures( + cookieAnalysisAndFetchedResourceData[ind].resources ?? [], + Object.fromEntries( + Libraries.map((library) => [library.name, library.detectionFunction]) + ) as DetectionFunctions + ), + ...(cookieAnalysisAndFetchedResourceData[ind]?.domQueryMatches ?? {}), + }; return { pageUrl: _url, technologyData: technologyAnalysisData ? technologyAnalysisData[ind] : [], - cookieData: cookieAnalysisData[ind].cookieData, - } as CompleteJson; + cookieData: cookieAnalysisAndFetchedResourceData[ind].cookieData, + libraryMatches: detectedMatchingSignatures ?? [], + } as unknown as CompleteJson; }); - await saveResults(outputDir, result); + I18n.loadCLIMessagesData(locale); + + const isSiteMap = sitemapUrl || csvPath || sitemapPath ? true : false; if (outDir) { await saveCSVReports(path.resolve(outputDir), result); + console.log('Reports created successfully!'); process.exit(0); } - startDashboardServer( - encodeURIComponent(prefix) + - (sitemapUrl || csvPath || sitemapPath ? '&type=sitemap' : ''), - port - ); -})(); + await saveResultsAsJSON(outputDir, result); + await saveResultsAsHTML(outputDir, result, isSiteMap); +})().catch((error) => { + console.log('Some error occured while analysing the website.'); + console.log('For more information check the stack trace below:\n'); + console.log(error); + process.exit(process?.exitCode ?? 0); +}); diff --git a/packages/cli/src/utils/askUserInput.ts b/packages/cli/src/utils/askUserInput.ts new file mode 100644 index 000000000..1f3d6dc8d --- /dev/null +++ b/packages/cli/src/utils/askUserInput.ts @@ -0,0 +1,36 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * External dependencies. + */ +import promptly from 'promptly'; + +/** + * Ask user input. + * @param {string} message Message to user. + * @param {object} options Options. + * @returns {Promise} User's input. + */ +async function askUserInput( + message: string, + options: object = {} +): Promise { + const userInput: string = await promptly.prompt(message, options); + return userInput; +} + +export default askUserInput; diff --git a/packages/cli/src/utils/browserManagement/index.ts b/packages/cli/src/utils/browserManagement/index.ts deleted file mode 100644 index 3ae5af939..000000000 --- a/packages/cli/src/utils/browserManagement/index.ts +++ /dev/null @@ -1,421 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * External dependencies. - */ -import puppeteer, { Browser, Page, Protocol } from 'puppeteer'; -import { parse } from 'simple-cookie'; - -/** - * Internal dependencies. - */ -import { ResponseData, RequestData, ViewportConfig } from './types'; -import { parseNetworkDataToCookieData } from './parseNetworkDataToCookieData'; -import delay from '../delay'; -import { CookieData, UNKNOWN_FRAME_KEY } from '@ps-analysis-tool/common'; - -export class BrowserManagement { - viewportConfig: ViewportConfig; - browser: Browser | null; - isHeadless: boolean; - pageWaitTime: number; - pageMap: Map; - pageResponseMaps: Map>; - pageRequestMaps: Map>; - shouldLogDebug: boolean; - - constructor( - viewportConfig: ViewportConfig, - isHeadless: boolean, - pageWaitTime: number, - shouldLogDebug: boolean - ) { - this.viewportConfig = viewportConfig; - this.browser = null; - this.isHeadless = isHeadless; - this.pageWaitTime = pageWaitTime; - this.pageMap = new Map(); - this.pageResponseMaps = new Map(); - this.pageRequestMaps = new Map(); - this.shouldLogDebug = shouldLogDebug; - } - - debugLog(msg: any) { - if (this.shouldLogDebug) { - console.log(msg); - } - } - - async initializeBrowser(enable3pCookiePhaseout: boolean) { - const args = []; - - if (enable3pCookiePhaseout) { - args.push('--test-third-party-cookie-phaseout'); - } - - this.browser = await puppeteer.launch({ - devtools: true, - headless: this.isHeadless ? 'new' : false, - args, - }); - this.debugLog('browser intialized'); - } - - async clickOnAcceptBanner(url: string) { - const page = this.pageMap.get(url); - - if (!page) { - throw new Error('no page with the provided id was found'); - } - - await page.evaluate(() => { - const bannerNodes: Element[] = Array.from( - (document.querySelector('body')?.childNodes || []) as Element[] - ) - .filter((node: Element) => node && node?.tagName === 'DIV') - .filter((node) => { - if (!node || !node?.textContent) { - return false; - } - const regex = - /\b(consent|policy|cookie policy|privacy policy|personalize|preferences)\b/; - - return regex.test(node.textContent.toLowerCase()); - }); - - const buttonToClick: HTMLButtonElement[] = bannerNodes - .map((node: Element) => { - const buttonNodes = Array.from(node.getElementsByTagName('button')); - const isButtonForAccept = buttonNodes.filter( - (cnode) => - cnode.textContent && - (cnode.textContent.toLowerCase().includes('accept') || - cnode.textContent.toLowerCase().includes('allow') || - cnode.textContent.toLowerCase().includes('agree')) - ); - - return isButtonForAccept[0]; - }) - .filter((button) => button); - buttonToClick[0]?.click(); - }); - - await delay(this.pageWaitTime / 2); - } - - async openPage(): Promise { - if (!this.browser) { - throw new Error('Browser not intialized'); - } - const sitePage = await this.browser.newPage(); - - await sitePage.setUserAgent( - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36' - ); - - sitePage.setViewport({ - width: 1440, - height: 790, - deviceScaleFactor: 1, - }); - - this.debugLog('Page opened'); - - return sitePage; - } - - async navigateToPage(url: string) { - const page = this.pageMap.get(url); - - if (!page) { - throw new Error('no page with the provided id was found'); - } - - this.debugLog(`starting navigation to url ${url}`); - - try { - await page.goto(url, { timeout: 10000 }); - this.debugLog(`done with navigation to url:${url}`); - } catch (error) { - this.debugLog( - `navigation did not finish in 10 seconds moving on to scrolling` - ); - //ignore - } - } - - async pageScroll(url: string) { - const page = this.pageMap.get(url); - - if (!page) { - throw new Error('no page with the provided id was found'); - } - - try { - await page.evaluate(() => { - window.scrollBy(0, 10000); - }); - } catch (error) { - this.debugLog(`scrolling the page to the end.`); - //ignore - } - - await delay(this.pageWaitTime / 2); - - this.debugLog(`scrolling on url:${url}`); - } - - async attachNetworkListenersToPage(pageId: string) { - const page = this.pageMap.get(pageId); - if (!page) { - throw new Error(`no page with the provided id was found:${pageId}`); - } - const cdpSession = await page.createCDPSession(); - await cdpSession.send('Network.enable'); - //@ts-ignore - const mainFrameId: string = page.mainFrame()._id; - - const responseMap: Map = new Map(); - this.pageResponseMaps.set(pageId, responseMap); - - cdpSession.on( - 'Network.responseReceived', - (response: Protocol.Network.ResponseReceivedEvent) => { - responseMap.set(response.requestId, { - frameId: response.frameId || mainFrameId, - serverUrl: response.response.url, - cookies: responseMap.get(response.requestId)?.cookies || [], - }); - } - ); - - cdpSession.on( - 'Network.responseReceivedExtraInfo', - (event: Protocol.Network.ResponseReceivedExtraInfoEvent) => { - if (!event.headers['set-cookie']) { - return; - } - const cookies = event.headers['set-cookie'] - .split('\n') - .map((headerLine) => { - const parsedCookie = parse(headerLine); - const partitionKey = headerLine.includes('Partitioned') - ? event.cookiePartitionKey - : undefined; - - const blockedEntry = event.blockedCookies.find((c) => { - return c.cookie?.name === parsedCookie.name; - }); - - return { - parsedCookie: { - name: parsedCookie.name, - domain: parsedCookie.domain, - path: parsedCookie.path || '/', - value: parsedCookie.value, - sameSite: parsedCookie.samesite || 'Lax', - expires: parsedCookie.expires || 'Session', - httpOnly: parsedCookie.httponly || false, - secure: parsedCookie.secure || false, - partitionKey, - }, - isBlocked: Boolean(blockedEntry), - blockedReasons: blockedEntry?.blockedReasons, - }; - }); - - const prevCookies = responseMap.get(event.requestId)?.cookies || []; - const mergedCookies = [...prevCookies, ...(cookies || [])]; - - responseMap.set(event.requestId, { - frameId: responseMap.get(event.requestId)?.frameId || '', - serverUrl: responseMap.get(event.requestId)?.serverUrl || '', - // @ts-ignore TODO: fix expires type mismatch - cookies: mergedCookies, - }); - } - ); - - const requestMap = new Map(); - this.pageRequestMaps.set(pageId, requestMap); - - cdpSession.on('Network.requestWillBeSent', (request) => { - requestMap.set(request.requestId, { - ...(requestMap.get(request.requestId) || {}), - frameId: request.frameId, - serverUrl: request.request.url, - }); - }); - - cdpSession.on( - 'Network.requestWillBeSentExtraInfo', - (event: Protocol.Network.RequestWillBeSentExtraInfoEvent) => { - if (event.associatedCookies && event.associatedCookies.length !== 0) { - const cookies = event.associatedCookies.map((associatedCookie) => { - return { - parsedCookie: { - name: associatedCookie.cookie.name, - domain: associatedCookie.cookie.domain, - path: associatedCookie.cookie.path || '/', - value: associatedCookie.cookie.value, - sameSite: associatedCookie.cookie.sameSite || 'Lax', - expires: associatedCookie.cookie.expires || 'Session', - httpOnly: associatedCookie.cookie.httpOnly || false, - secure: associatedCookie.cookie.secure || false, - partitionKey: associatedCookie.cookie.partitionKey, - }, - isBlocked: associatedCookie.blockedReasons.length > 0, - blockedReasons: associatedCookie.blockedReasons, - }; - }); - requestMap.set(event.requestId, { - ...(requestMap.get(event.requestId) || {}), - cookies, - }); - } - } - ); - - this.debugLog('done attaching network event listeners'); - } - - getPageFrameIdToUrlMap(id: string) { - const page = this.pageMap.get(id); - if (!page) { - throw new Error('no page with the provided id was found'); - } - const frameIdMapFromTree = new Map(); - const frames = page.frames(); - const frameCallback = (frame: any) => { - const frameId = frame._id; - const _url = frame.url(); - frameIdMapFromTree.set(frameId, _url); - - if (frame.childFrames()) { - frame.childFrames().forEach(frameCallback); - } - }; - - frames.forEach(frameCallback); - return frameIdMapFromTree; - } - - async analyzeCookieUrls(urls: string[], shouldSkipAcceptBanner: boolean) { - for (const url of urls) { - const sitePage = await this.openPage(); - this.pageMap.set(url, sitePage); - await this.attachNetworkListenersToPage(url); - } - - // start navigation in parallel - await Promise.all( - urls.map(async (url) => { - await this.navigateToPage(url); - if (shouldSkipAcceptBanner) { - await this.clickOnAcceptBanner(url); - } - await this.pageScroll(url); - }) - ); - - //parse cookie data in parallel - const result = await Promise.all( - urls.map(async (url) => { - const responseMap = this.pageResponseMaps.get(url); - const requestMap = this.pageRequestMaps.get(url); - const page = this.pageMap.get(url); - - const frameIdUrlMap = this.getPageFrameIdToUrlMap(url); - // @ts-ignore - const mainFrameId = this.pageMap.get(url)?.mainFrame()._id; - - if ( - !responseMap || - !requestMap || - !frameIdUrlMap || - !mainFrameId || - !page - ) { - return { - pageUrl: url, - cookieData: {}, - }; - } - - const cookieDataFromNetwork = parseNetworkDataToCookieData( - responseMap, - requestMap, - frameIdUrlMap, - mainFrameId, - page.url() - ); - - const networkCookieKeySet = new Set(); - - Object.values(cookieDataFromNetwork).forEach(({ frameCookies }) => { - Object.keys(frameCookies).forEach((key) => { - networkCookieKeySet.add(key); - }); - }); - - //Get cookies from another API - const session = await page.createCDPSession(); - - const applicationCookies = (await session.send('Page.getCookies')) - .cookies; - - const filteredApplicationCookies = applicationCookies.filter( - (cookie) => - !networkCookieKeySet.has( - `${cookie.name}:${cookie.domain}:${cookie.path}` - ) - ); - - const reshapedApplicationCookies = filteredApplicationCookies.reduce<{ - [key: string]: CookieData; - }>((acc, cookie) => { - const key = `${cookie.name}:${cookie.domain}:${cookie.path}`; - acc[key] = { - parsedCookie: cookie, - url: '', - }; - return acc; - }, {}); - - // handles redirection - const pageUrl = page.url(); - - return { - pageUrl, - cookieData: { - ...cookieDataFromNetwork, - [UNKNOWN_FRAME_KEY]: { - frameCookies: reshapedApplicationCookies, - cookiesCount: Object.keys(reshapedApplicationCookies).length, - }, - }, - }; - }) - ); - - return result; - } - - async deinitialize() { - await this.browser?.close(); - } -} diff --git a/packages/cli/src/utils/browserManagement/parseNetworkDataToCookieData.ts b/packages/cli/src/utils/browserManagement/parseNetworkDataToCookieData.ts deleted file mode 100644 index a8f41e15b..000000000 --- a/packages/cli/src/utils/browserManagement/parseNetworkDataToCookieData.ts +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * External dependencies. - */ -import { getDomain } from 'tldts'; -import { CookieData } from '@ps-analysis-tool/common'; -/** - * Internal dependencies. - */ -import { RequestData, ResponseData } from './types'; - -export const parseNetworkDataToCookieData = ( - responseMap: Map, - requestMap: Map, - frameIdUrlMap: Map, - mainFrameId: string, - pageUrl: string -): { - [frameUrl: string]: { - cookiesCount: number; - frameCookies: { - [key: string]: CookieData; - }; - }; -} => { - const frameIdNetworkDataMap = new Map< - string, - { responses: ResponseData[]; requests: RequestData[] } - >(); - - for (const [, response] of responseMap) { - if (!response.cookies || response.cookies.length === 0) { - continue; - } - const frameId = response.frameId || mainFrameId; - const prevResponses = frameIdNetworkDataMap.get(frameId)?.responses || []; - const prevRequests = frameIdNetworkDataMap.get(frameId)?.responses || []; - - frameIdNetworkDataMap.set(frameId, { - responses: [...prevResponses, response], - requests: prevRequests, - }); - } - for (const [, request] of requestMap) { - if (!request.cookies || request.cookies.length === 0) { - continue; - } - const frameId = request.frameId || mainFrameId; - const prevResponses = frameIdNetworkDataMap.get(frameId)?.responses || []; - const prevRequests = frameIdNetworkDataMap.get(frameId)?.responses || []; - - frameIdNetworkDataMap.set(frameId, { - responses: prevResponses, - requests: [...prevRequests, request], - }); - } - - const frameIdCookiesMap = new Map< - string, - { - frameUrl: string; - frameCookies: { - [key: string]: CookieData; - }; - } - >(); - - for (const [frameId, data] of frameIdNetworkDataMap) { - const _frameCookies = new Map(); - - data.responses?.forEach((response: ResponseData) => { - response.cookies.forEach((cookie) => { - // domain update required. Domain based on the server url - let parsedDomain = - cookie.parsedCookie.domain === '' - ? getDomain(response.serverUrl) - : cookie.parsedCookie.domain; - - if (parsedDomain && parsedDomain[0] !== '.') { - parsedDomain = '.' + parsedDomain; - } - - const key = - cookie.parsedCookie.name + - ':' + - parsedDomain + - ':' + - cookie.parsedCookie.path; - - const prevEntry = _frameCookies.get(key); - - const blockedReasonsSet = new Set([ - ...(cookie?.blockedReasons || []), - ...(prevEntry?.blockedReasons || []), - ]); - - _frameCookies.set(key, { - ...cookie, - url: response.serverUrl, - blockedReasons: Array.from(blockedReasonsSet), - parsedCookie: { - ...cookie.parsedCookie, - domain: parsedDomain || '', - }, - }); - }); - }); - - data.requests?.forEach((request: RequestData) => { - request.cookies.forEach((cookie) => { - // domain update required. Domain based on the server url - let parsedDomain = - cookie.parsedCookie.domain === '' - ? getDomain(request.serverUrl) - : cookie.parsedCookie.domain; - - if (parsedDomain && parsedDomain[0] !== '.') { - parsedDomain = '.' + parsedDomain; - } - - const key = - cookie.parsedCookie.name + - ':' + - parsedDomain + - ':' + - cookie.parsedCookie.path; - - const prevEntry = _frameCookies.get(key); - - const blockedReasonsSet = new Set([ - ...(cookie?.blockedReasons || []), - ...(prevEntry?.blockedReasons || []), - ]); - - _frameCookies.set(key, { - ...cookie, - url: request.serverUrl, - blockedReasons: Array.from(blockedReasonsSet), - parsedCookie: { ...cookie.parsedCookie, domain: parsedDomain || '' }, - }); - }); - }); - - frameIdCookiesMap.set(frameId, { - frameUrl: frameIdUrlMap.get(frameId) || new URL(pageUrl).origin, - frameCookies: Object.fromEntries(_frameCookies), - }); - } - const frameUrlCookies = new Map< - string, - { - cookiesCount: number; - frameCookies: { - [key: string]: CookieData; - }; - } - >(); - - for (const [, data] of frameIdCookiesMap) { - if (!data.frameUrl.includes('http')) { - continue; - } - - const _url = new URL(data.frameUrl); - - const newFrameCookies = { - ...data.frameCookies, - ...(frameUrlCookies.get(_url.origin)?.frameCookies || {}), - }; - - frameUrlCookies.set(_url.origin, { - cookiesCount: Object.keys(newFrameCookies)?.length || 0, - frameCookies: { - ...newFrameCookies, - }, - }); - } - - return Object.fromEntries(frameUrlCookies); -}; diff --git a/packages/cli/src/utils/fetchCookieDictionary.ts b/packages/cli/src/utils/fetchCookieDictionary.ts index 1e6633b3c..400cf0d2d 100644 --- a/packages/cli/src/utils/fetchCookieDictionary.ts +++ b/packages/cli/src/utils/fetchCookieDictionary.ts @@ -19,11 +19,11 @@ */ import fs from 'fs/promises'; import path from 'path'; +import { CookieDatabase } from '@google-psat/common'; /** * Internal dependencies. */ -import { CookieDatabase } from '../types'; /** * Fetch dictionary from local data folder. @@ -31,9 +31,12 @@ import { CookieDatabase } from '../types'; */ export async function fetchDictionary(): Promise { const data = JSON.parse( - await fs.readFile(path.resolve('./assets/data/open-cookie-database.json'), { - encoding: 'utf8', - }) + await fs.readFile( + path.resolve(__dirname, './assets/data/open-cookie-database.json'), + { + encoding: 'utf8', + } + ) ); return data; diff --git a/packages/cli/src/utils/generateCSVfiles.ts b/packages/cli/src/utils/generateCSVfiles.ts index b29534698..9290e7fb1 100644 --- a/packages/cli/src/utils/generateCSVfiles.ts +++ b/packages/cli/src/utils/generateCSVfiles.ts @@ -22,7 +22,7 @@ import { generateAllCookiesCSV, generateSummaryDataCSV, generateTechnologyCSV, -} from '@ps-analysis-tool/common'; +} from '@google-psat/common'; const generateCSVFiles = (data: CompleteJson) => { const allCookiesCSV = generateAllCookiesCSV(data); diff --git a/packages/cli/src/utils/generatePrefix.ts b/packages/cli/src/utils/generatePrefix.ts index 6cb87993e..2ba7b2884 100644 --- a/packages/cli/src/utils/generatePrefix.ts +++ b/packages/cli/src/utils/generatePrefix.ts @@ -21,8 +21,14 @@ export default function generatePrefix(url: string): string { const urlObject = new URL(url); - return (urlObject.hostname + urlObject.pathname).replace( + const path = (urlObject.hostname + urlObject.pathname).replace( /[^a-zA-Z0-9 ]/g, '-' ); + + if (path.endsWith('-')) { + return path.slice(0, -1); + } + + return path; } diff --git a/packages/cli/src/utils/getUrlListFromArgs.ts b/packages/cli/src/utils/getUrlListFromArgs.ts index df184be37..0b756e2af 100644 --- a/packages/cli/src/utils/getUrlListFromArgs.ts +++ b/packages/cli/src/utils/getUrlListFromArgs.ts @@ -18,10 +18,11 @@ */ import { readFile } from 'fs-extra'; import { parseStringPromise } from 'xml2js'; +import { basename } from 'path'; /** * Internal dependencies. */ -import Utility from './utility'; +import getUrlsFromSitemap from './getUrlsfromSitemap'; const parseUrlsFromSitemap = async (sitemapUrl: string, spinnies: any) => { spinnies?.add('sitemap-spinner', { @@ -29,9 +30,9 @@ const parseUrlsFromSitemap = async (sitemapUrl: string, spinnies: any) => { }); try { - const _urls = await Utility.getUrlsFromSitemap(sitemapUrl); + const _urls = await getUrlsFromSitemap(sitemapUrl); spinnies?.succeed('sitemap-spinner', { - text: 'Done parsing Sitemap', + text: 'Done parsing Sitemap!', }); return _urls; } catch (error) { @@ -58,17 +59,17 @@ const parseUrlsFromCSV = async (csvPath: string, spinnies: any) => { }, [] as string[]); if (_urls.length === 0) { - console.log('Provided CSV files has no urls'); + console.log(`Error: CSV file contains no URLs: ${basename(csvPath)}`); process.exit(1); } _urls.forEach((_url) => { if (!_url.includes('http')) { - console.log(`${_url} is not a valid URL`); + console.log(`Error: Invalid URL: ${_url}.`); process.exit(1); } }); spinnies?.succeed('csv-spinner', { - text: 'Done parsing CSV file', + text: 'Done parsing CSV file!', }); return _urls; } catch (error) { @@ -91,14 +92,12 @@ const parseUrlsFromLocalSitemap = async ( const isSiteMap = Boolean(data['urlset']); if (isSiteIndex) { - throw new Error( - 'Sorry, XML Schema for Sitemap index files is not supported by the tool' - ); + throw new Error('Error: Sitemap index not supported'); } if (!isSiteMap) { throw new Error( - 'Sorry, XML Schema for Sitemap index files is not supported by the tool' + `Error: Sitemap file contains no URLs: ${basename(sitemapPath)}` ); } @@ -111,19 +110,23 @@ const parseUrlsFromLocalSitemap = async ( [] ); } catch (error) { - throw new Error('Provided XML files has no urls in its urlset'); + throw new Error( + `Error: Sitemap file contains no URLs: ${basename(sitemapPath)}` + ); } if (_urls.length === 0) { - throw new Error('Provided XML files has no urls '); + throw new Error( + `Error: Sitemap file contains no URLs: ${basename(sitemapPath)}` + ); } _urls.forEach((_url) => { if (!_url.includes('http')) { - throw new Error(`${_url} is not a valid URL`); + throw new Error(`Error: Invalid URL: ${_url}.`); } }); spinnies?.succeed('sitemap-spinner', { - text: 'Done parsing XML file', + text: 'Done parsing XML file!', }); return _urls; @@ -155,7 +158,7 @@ const getUrlListFromArgs = async ( const _urls = await parseUrlsFromSitemap(sitemapUrl, spinnies); urls = urls.concat(_urls); } catch (error) { - console.log('Error parsing sitemap'); + console.log('Error: Error parsing sitemap.'); process.exit(1); } } else if (csvPath) { diff --git a/packages/cli/src/utils/getUrlsfromSitemap.ts b/packages/cli/src/utils/getUrlsfromSitemap.ts new file mode 100644 index 000000000..8a6d0ad36 --- /dev/null +++ b/packages/cli/src/utils/getUrlsfromSitemap.ts @@ -0,0 +1,45 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * External dependencies. + */ + +import Sitemapper from 'sitemapper'; + +/** + * Get List of urls from sitemap url. + * @param {string} sitemapURL Site map URL. + * @returns {Promise>} List of URLs. + */ +async function getUrlsFromSitemap(sitemapURL: string): Promise> { + const siteMapper = new Sitemapper({ + url: sitemapURL, + timeout: 3000, + }); + + let urls: Array = []; + try { + const { sites } = await siteMapper.fetch(); + urls = sites; + } catch (error) { + console.log('Error: Error parsing sitemap'); + } + + return urls; +} + +export default getUrlsFromSitemap; diff --git a/packages/cli/src/utils/index.ts b/packages/cli/src/utils/index.ts index e940a8f82..b05488131 100644 --- a/packages/cli/src/utils/index.ts +++ b/packages/cli/src/utils/index.ts @@ -13,9 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export { default as delay } from './delay'; export { fetchDictionary } from './fetchCookieDictionary'; export { default as validateArgs } from './validateArgs'; export { default as getUrlListFromArgs } from './getUrlListFromArgs'; export { default as generateCSVFiles } from './generateCSVfiles'; export { default as saveCSVReports } from './saveCSVReports'; +export { default as getUrlsFromSitemap } from './getUrlListFromArgs'; +export { default as askUserInput } from './askUserInput'; +export { default as generatePrefix } from './generatePrefix'; diff --git a/packages/cli/src/utils/saveCSVReports.ts b/packages/cli/src/utils/saveCSVReports.ts index d8a1d5c55..684fabfb5 100644 --- a/packages/cli/src/utils/saveCSVReports.ts +++ b/packages/cli/src/utils/saveCSVReports.ts @@ -17,7 +17,7 @@ /** * External dependencies. */ -import { CompleteJson } from '@ps-analysis-tool/common'; +import { CompleteJson } from '@google-psat/common'; import { ensureFile, writeFile } from 'fs-extra'; /** * Internal dependencies. diff --git a/packages/cli/src/utils/tests/validateArgs.ts b/packages/cli/src/utils/tests/validateArgs.ts index bcfb0c5f2..c3b8027f5 100644 --- a/packages/cli/src/utils/tests/validateArgs.ts +++ b/packages/cli/src/utils/tests/validateArgs.ts @@ -27,10 +27,10 @@ describe('validateArgs', () => { let mockExit: jest.SpyInstance; beforeAll(() => { - mockExit = jest - .spyOn(process, 'exit') + mockExit = jest.spyOn(process, 'exit').mockImplementation( // eslint-disable-next-line @typescript-eslint/no-unused-vars - .mockImplementation((code?: number) => undefined as never); + (code?: string | number | null | undefined) => undefined as never + ); }); it('works with one arg', () => { @@ -44,7 +44,7 @@ describe('validateArgs', () => { return; }); - validateArgs('https://example.com', '', '', '', '', '', 9000); + validateArgs('https://example.com', '', '', '', '', ''); expect(mockExit).toHaveBeenCalledTimes(0); }); @@ -59,7 +59,7 @@ describe('validateArgs', () => { jest.spyOn(fse, 'mkdir').mockImplementation(() => { return; }); - validateArgs('', '', '', '', '', '', 9000); + validateArgs('', '', '', '', '', ''); expect(mockExit).toHaveBeenCalled(); }); @@ -80,8 +80,7 @@ describe('validateArgs', () => { '', '', '', - '', - 9000 + '' ); expect(mockExit).toHaveBeenCalled(); @@ -93,7 +92,7 @@ describe('validateArgs', () => { return false; }); - validateArgs('', '', '', './path/list.xml', '', '', 9000); + validateArgs('', '', '', './path/list.xml', '', ''); expect(mockExit).toHaveBeenCalled(); }); @@ -104,7 +103,7 @@ describe('validateArgs', () => { return true; }); - validateArgs('', '', '', './path/list.xml', 'a', '', 9000); + validateArgs('', '', '', './path/list.xml', 'a', ''); expect(mockExit).toHaveBeenCalled(); }); diff --git a/packages/cli/src/utils/utility/index.ts b/packages/cli/src/utils/utility/index.ts deleted file mode 100644 index 64ebf6d54..000000000 --- a/packages/cli/src/utils/utility/index.ts +++ /dev/null @@ -1,300 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * External dependencies. - */ -import promptly from 'promptly'; -import Sitemapper from 'sitemapper'; - -/** - * Internal dependencies. - */ -import { Cookie } from '../../types'; - -// eslint-disable-next-line @typescript-eslint/no-var-requires -const Table = require('cli-table'); - -export default class Utility { - /** - * Create chunk of array from array. - * @param {Array} list List. - * @param {number} batchSize Batch size. - * @returns {Array>} List of chunks. - */ - public static chunk(list: Array, batchSize = 5): Array> { - const chunks: Array> = []; - - for (let i = 0; i < list.length; i += batchSize) { - chunks.push(list.slice(i, i + batchSize)); - } - return chunks; - } - - /** - * Pluck given key from list of object. - * @param {Array} list List of object. - * @param {string} key Key to pluck. - * @returns {Array} List of value. - */ - public static listPluck(list: Array, key: string): Array { - const output: Array = []; - - // eslint-disable-next-line array-callback-return - list.map((listItem: object) => { - if (listItem && key in listItem) { - output.push(listItem[key as keyof object]); - } - }); - - return output; - } - - /** - * Remove the key from object. - * @param {Array} list List of objects. - * @param {Array} keys Key that need to remove. - * @returns {Array} List of object without key. - */ - public static listRemoveKey( - list: Array, - keys: Array - ): Array { - const output: Array = [...list]; - - // eslint-disable-next-line array-callback-return - output.map((listItem: object) => { - keys.forEach((key) => { - delete listItem[key as keyof object]; - }); - }); - - return output; - } - - /** - * Convert object into pretty JSON. - * @param {object|Array} data Data. - * @returns Pretty JSON string. - */ - public static prettyJson(data: object | Array): string { - return JSON.stringify(data, null, 4); - } - - public static mergeObjects(object1: object, object2: object): object { - const output: { [key: string]: any } = { ...object1 }; - - Object.entries(object2).forEach(([key, value]) => { - output[key] = value; - }); - - return output; - } - - /** - * Merge all the array. - * @param {Array>} list List of Arrays. - * @returns {Array} Merged array. - */ - public static mergeAll(list: Array>): Array { - let allData: Array = []; - - // eslint-disable-next-line array-callback-return - list.map((listItem: object) => { - // @ts-ignore - if (listItem) { - // @ts-ignore - allData = [...allData, ...listItem]; - } - }); - - return allData; - } - - /** - * Get List of urls from sitemap url. - * @param {string} sitemapURL Site map URL. - * @returns {Promise>} List of URLs. - */ - public static async getUrlsFromSitemap( - sitemapURL: string - ): Promise> { - const siteMapper = new Sitemapper({ - url: sitemapURL, - timeout: 3000, - }); - - let urls: Array = []; - try { - const { sites } = await siteMapper.fetch(); - urls = sites; - } catch (error) { - console.log('Error: error parsing sitemap '); - } - - return urls; - } - - /** - * Ask user input. - * @param {string} message Message to user. - * @param {object} options Options. - * @returns {Promise} User's input. - */ - public static async askUserInput( - message: string, - options: object = {} - ): Promise { - const userInput: string = await promptly.prompt(message, options); - return userInput; - } - - /** - * Parse cookies from string. - * @param {string} cookiesString Browser cookies string. - * @returns {Array} List of cookies. - */ - public static parseCookies(cookiesString: string): Array { - const cookies: Array = []; - const rows: Array = cookiesString.split('\n'); - - rows.forEach((row) => { - const cookie: Cookie = Utility.parseCookieLine(row); - cookies.push(cookie); - }); - - return cookies; - } - - /** - * Parse cookie line. - * @param cookieLine - * @returns Cookie - */ - public static parseCookieLine(cookieLine: string): Cookie { - const regex = /[;]{1,9}/gm; - cookieLine = cookieLine.replace(regex, ';'); - - const segments = cookieLine.split(';'); - const allColumns = segments.map((v) => v.split('=')); - // @ts-ignore - const cookie: Cookie = { - name: '', - value: '', - expires: 0, - path: '', - domain: '', - httpOnly: false, - secure: false, - sameSite: undefined, - }; - - const cookieKeys: Array = [ - 'expires', - 'path', - 'domain', - 'httponly', - 'secure', - 'samesite', - ]; - - allColumns.forEach((currentValue) => { - let key: string = currentValue.shift() ?? ''; - key = Utility.decodeURI(key); - - if ('max-age' === key.toLowerCase()) { - key = 'expires'; - } - - let value = currentValue.join('='); - value = Utility.decodeURI(value); - - if (!key) { - return; - } - - if (!cookie['name'] && !cookieKeys.includes(key.toLowerCase())) { - cookie['name'] = key; - cookie['value'] = value; - } else if (['httponly', 'secure'].includes(key.toLowerCase())) { - // @ts-ignore - cookie[key.toLowerCase()] = true; - } else if (key) { - // @ts-ignore - cookie[key.toLowerCase()] = value; - } - }); - - return cookie; - } - - public static decodeURI(value: any): any { - try { - value = decodeURIComponent(value.trim()); - } catch (exception) { - console.error('Utility:decodeURI : ', exception, ' Value : ', value); - value = ''; - } - - return value; - } - - /** - * Get printable table in CLI from Array of object. - * @param {Array} items Array og object. - * @returns {object} - */ - public static getCliTable(items: Array): object { - if (!items || 0 === items.length) { - return {}; - } - - const table = new Table({ - // @ts-ignore - head: Object.keys([...items].shift()), - }); - - items.forEach((item) => { - table.push(Object.values(item)); - }); - - return table; - } - - /** - * Sort the object by key. - * @param {object} object Object to sort - * @returns {object} Object sorted by key. - */ - public static sortObjectByKey(object: { [key: string]: string }): object { - return Object.keys(object) - .sort() - .reduce((result: { [key: string]: string }, key: string) => { - result[key] = object[key]; - return result; - }, {}); - } - - /** - * Generate Prefix. - * @param {string} url Url in a string format. - * @returns {string} string with protocol removed and special characters replaces with "-". - */ - public static generatePrefix(url: string): string { - const urlObject = new URL(url); - - return urlObject.hostname.replace('.', '-'); - } -} diff --git a/packages/cli/src/utils/validateArgs.ts b/packages/cli/src/utils/validateArgs.ts index abf31e2ae..1f6943fa2 100644 --- a/packages/cli/src/utils/validateArgs.ts +++ b/packages/cli/src/utils/validateArgs.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { parseUrl } from '@ps-analysis-tool/common'; +import { parseUrl } from '@google-psat/common'; import { exists, mkdir } from 'fs-extra'; import path from 'path'; @@ -26,7 +26,7 @@ import path from 'path'; * @param {string} sitemapPath File system path to a sitemap xml file. * @param {string} numberOfUrls Url limit argument. * @param {string} outDir File system path to the output directory. - * @param port + * @param {string} locale Locale of the site. */ // eslint-disable-next-line complexity const validateArgs = async ( @@ -36,13 +36,8 @@ const validateArgs = async ( sitemapPath: string, numberOfUrls: string, outDir: string, - port: number + locale: string ) => { - if (isNaN(port) || (!isNaN(port) && (port < 0 || port > 65536))) { - console.log(`Invalid port argument. Please porvide a port >=0 and <=65536`); - process.exit(1); - } - const numArgs: number = [ Boolean(url), Boolean(sitemapUrl), @@ -56,7 +51,7 @@ const validateArgs = async ( if (numArgs !== 1) { console.log( `Please provide one and only one of the following - a) URL of a site (-u or --url) + a) URL of a site (-u or --url or default argument) b) URL of a sitemap (-s or --sitemap-url) c) Path to a CSV file (-c or --csv-path) d) Path to an XML file (-p or --sitemap-path)` @@ -67,7 +62,7 @@ const validateArgs = async ( if (csvPath) { const csvFileExists = await exists(csvPath); if (!csvFileExists) { - console.log(`No file at ${csvPath}`); + console.log(`Error: No file at ${csvPath}`); process.exit(1); } } @@ -75,7 +70,7 @@ const validateArgs = async ( if (sitemapPath) { const sitemapFileExists = await exists(sitemapPath); if (!sitemapFileExists) { - console.log(`No file at ${sitemapPath}`); + console.log(`Error: No file at ${sitemapPath}`); process.exit(1); } } @@ -86,7 +81,7 @@ const validateArgs = async ( const parsedUrl = parseUrl(_url); if (parsedUrl === null) { - console.log(`Provided Url ${parsedUrl} is not valid`); + console.log(`Error: Invalid Url ${parsedUrl}`); process.exit(1); } } @@ -116,10 +111,21 @@ const validateArgs = async ( const outDirExists = await exists(output); if (!outDirExists) { - console.log(`"${output}" does not exist, creating now.`); + console.log(`"${output}" does not exist, creating!`); await mkdir(output); } } + + const availableLocales = ['en [default]', 'hi', 'es', 'ja', 'ko', 'pt-BR']; + + if (locale && !availableLocales.includes(locale)) { + console.error( + `Locale '${locale}' is not supported, please use ${availableLocales.join( + ', ' + )}.` + ); + process.exit(1); + } }; export default validateArgs; diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json index 9df306aff..45b6e6028 100644 --- a/packages/cli/tsconfig.json +++ b/packages/cli/tsconfig.json @@ -3,11 +3,18 @@ "rootDir": "src", "target": "es6", "module": "commonjs", - "outDir": "../../dist/cli", "strict": true, "sourceMap": true, "esModuleInterop": true, - "moduleResolution": "node" + "moduleResolution": "node", + "outDir": "dist", + "declarationDir": "dist-types", + "composite": true }, - "references": [{ "path": "../common" }, { "path": "../i18n" }] + "exclude": ["**/tests/**/*.ts", "dist/**", "dist-types/**"], + "references": [ + { "path": "../common" }, + { "path": "../i18n" }, + { "path": "../analysis-utils" } + ] } diff --git a/packages/common/.npmignore b/packages/common/.npmignore new file mode 100644 index 000000000..aa8e45f12 --- /dev/null +++ b/packages/common/.npmignore @@ -0,0 +1 @@ +src/ \ No newline at end of file diff --git a/packages/common/package.json b/packages/common/package.json index 62ad56878..cb7a74d38 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -1,10 +1,10 @@ { - "name": "@ps-analysis-tool/common", - "version": "0.8.0", + "name": "@google-psat/common", + "version": "0.9.0-2", "description": "A package for common utilities that are being used in multiple packages", - "main": "./dist/index.js", - "types": "./dist-types/index.d.ts", - "source": "./src/index.ts", + "main": "dist/index.js", + "types": "dist-types/index.d.ts", + "source": "src/index.ts", "customExports": { ".": { "default": "./src/index.ts" @@ -12,19 +12,31 @@ }, "scripts": { "build": "tsc", - "dev": "tsc-watch" + "dev": "tsc-watch", + "build:remove": "rimraf dist dist-types tsconfig.tsbuildinfo", + "publish:local": "npm publish --registry=http://localhost:4873", + "publish:remote": "npm publish --access=public --registry=https://registry.npmjs.org", + "unpublish:local": "npm unpublish --registry=http://localhost:4873 --force", + "unpublish:remote": "npm unpublish --registry=https://registry.npmjs.org --force" }, "repository": { "type": "git", - "url": "git+https://github.com/GoogleChromeLabs/ps-analysis-tool" + "url": "https://github.com/GoogleChromeLabs/ps-analysis-tool", + "directory": "packages/common" + }, + "publishConfig": { + "access": "public" + }, + "author": { + "name": "Google" }, - "author": "", "license": "Apache-2.0", "bugs": { "url": "https://github.com/GoogleChromeLabs/ps-analysis-tool/issues" }, "homepage": "https://github.com/GoogleChromeLabs/ps-analysis-tool", "dependencies": { + "@google-psat/i18n": "*", "tldts": "^6.0.14" }, "devDependencies": { diff --git a/packages/common/src/cookies.types.ts b/packages/common/src/cookies.types.ts index 13ed47885..27835d486 100644 --- a/packages/common/src/cookies.types.ts +++ b/packages/common/src/cookies.types.ts @@ -13,12 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - /** * External dependencies. */ import { type Cookie as ParsedCookie } from 'simple-cookie'; import type { Protocol } from 'devtools-protocol'; +import { LibraryData } from './libraryDetection.types'; export type CookiesCount = { total: number; @@ -64,8 +64,7 @@ export type CookieDatabase = { export type BlockedReason = | Protocol.Network.SetCookieBlockedReason - | Protocol.Network.CookieBlockedReason - | Protocol.Audits.CookieExclusionReason; + | Protocol.Network.CookieBlockedReason; export enum RESPONSE_EVENT { CHROME_WEBREQUEST_ON_RESPONSE_STARTED = 'CHROME_WEBREQUEST_ON_RESPONSE_STARTED', @@ -93,7 +92,7 @@ export type requestEvent = { timeStamp: number; }; -export type responsEvent = { +export type responseEvent = { type: RESPONSE_EVENT; requestId: string; url: string; @@ -109,10 +108,10 @@ export type CookieData = { }; networkEvents?: { requestEvents: requestEvent[]; - responseEvents: responsEvent[]; + responseEvents: responseEvent[]; }; analytics?: CookieAnalytics | null; - url: string; + url?: string; headerType?: 'response' | 'request' | 'javascript'; isFirstParty?: boolean | null; frameIdList?: Array; @@ -155,14 +154,14 @@ export interface TabCookies { export interface TabFrames { [key: string]: { - frameIds: number[]; - isOnRWS?: boolean; + frameIds: string[]; frameType?: 'outermost_frame' | 'fenced_frame' | 'sub_frame'; }; } export interface Legend { label: string; + descriptionKey?: string; count: number; color: string; countClassName: string; @@ -192,7 +191,7 @@ export interface CookieStatsComponents { } export interface FramesWithCookies { - [key: string]: { frameIds: number[] }; + [key: string]: { frameIds: number[] | string[] }; } export type CookieJsonDataType = { @@ -236,12 +235,12 @@ export type CompleteJson = { pageUrl: string; cookieData: { [frame: string]: { - cookiesCount: number; frameCookies: { [cookieKey: string]: CookieJsonDataType; }; }; }; + libraryMatches: { [key: string]: LibraryData }; technologyData: TechnologyData[]; }; @@ -252,7 +251,7 @@ export interface DataMapping { count: number; color: string; }[]; - onClick?: () => void; + onClick?: (() => void) | null; } export type FrameStateCreator = { diff --git a/packages/common/src/data/cookieExclusionAndWarningReasons/cookieBlockedReason.ts b/packages/common/src/data/cookieExclusionAndWarningReasons/cookieBlockedReason.ts index 97fdec660..8e7e09975 100644 --- a/packages/common/src/data/cookieExclusionAndWarningReasons/cookieBlockedReason.ts +++ b/packages/common/src/data/cookieExclusionAndWarningReasons/cookieBlockedReason.ts @@ -13,24 +13,41 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +//out/win-Debug/gen/third_party/devtools-frontend/src/front_end/core/sdk/NetworkRequest.js const CookieBlockedReason = { - SecureOnly: `

This cookie was blocked because it had the 'Secure' attribute and the connection was not secure.


`, - NotOnPath: `

This cookie was blocked because its path was not an exact match for, or a superdirectory of, the request URL's path.


`, - DomainMismatch: `

This cookie was blocked because the request URL's domain did not exactly match the cookie's domain, nor was the request URL's domain a subdomain of the cookie's Domain attribute value.


`, - SameSiteStrict: `

This cookie was blocked because it had the SameSite=Strict attribute and the request was made from a different site. This includes top-level navigation requests initiated by other sites.


`, - SameSiteLax: `

This cookie was blocked because it had the SameSite=Lax attribute and the request was made from a different site and was not initiated by a top-level navigation.


`, - SameSiteUnspecifiedTreatedAsLax: `

This cookie didn't specify a 'SameSite' attribute when it was stored, was defaulted to SameSite=Lax, and was blocked because the request was made from a different site and was not initiated by a top-level navigation. The cookie had to have been set with SameSite=None to enable cross-site usage.


`, - SameSiteNoneInsecure: `This cookie was blocked because it had the SameSite=None attribute but was not marked 'Secure'. Cookies without SameSite restrictions must be marked 'Secure' and sent over a secure connection.


`, - UserPreferences: `

This cookie was blocked due to user preferences.


`, - ThirdPartyPhaseout: `

Prepare for phasing out third-party cookies.


`, - ThirdPartyBlockedInFirstPartySet: `

The cookie was blocked by third-party cookie blocking between sites in the same First-Party Set.


`, - UnknownError: `

Unknown error.


`, - SchemefulSameSiteStrict: `

This cookie was blocked because it had the SameSite=Strict attribute but the request was cross-site. This includes top-level navigation requests initiated by other sites. This request is considered cross-site because the URL has a different scheme than the current site.


`, - SchemefulSameSiteLax: `

This cookie was blocked because it had the SameSite=Lax attribute but the request was cross-site and was not initiated by a top-level navigation. This request is considered cross-site because the URL has a different scheme than the current site.


`, - SchemefulSameSiteUnspecifiedTreatedAsLax: `

This cookie didn't specify a 'SameSite' attribute when it was stored, was defaulted to SameSite=Lax, and was blocked because the request was cross-site and was not initiated by a top-level navigation. This request is considered cross-site because the URL has a different scheme than the current site.


`, - SamePartyFromCrossPartyContext: `

This cookie was blocked because it had the 'SameParty' attribute but the request was cross-party. The request was considered cross-party because the domain of the resource's URL and the domains of the resource's enclosing frames/documents are neither owners nor members in the same first-party set.


`, - NameValuePairExceedsMaxSize: `

This cookie was blocked because it was too large. The combined size of the name and value must be less than or equal to 4,096 characters.


`, - InvalidDomain: `

This attempt to set a cookie via 'Set-Cookie' header was blocked because its Domain value was invalid with regards to the current host url.


`, + SecureOnly: ['body_SecureOnly'], + NotOnPath: ['body_NotOnPath'], + DomainMismatch: ['body_DomainMismatch'], + SameSiteStrict: ['body_SameSiteStrict'], + SameSiteUnspecifiedTreatedAsLax: ['body_SameSiteUnspecifiedTreatedAsLax'], + SameSiteNoneInsecure: ['body_SameSiteNoneInsecure'], + UserPreferences: ['body_UserPreferences'], + ThirdPartyPhaseout: ['body_ThirdPartyPhaseout'], + ThirdPartyBlockedInFirstPartySet: ['body_ThirdPartyBlockedInFirstPartySet'], + UnknownError: ['body_UnknownError'], + SchemefulSameSiteStrict: ['body_SchemefulSameSiteStrict'], + SchemefulSameSiteLax: ['body_SchemefulSameSiteLax'], + SchemefulSameSiteUnspecifiedTreatedAsLax: [ + 'body_SchemefulSameSiteUnspecifiedTreatedAsLax', + ], + SamePartyFromCrossPartyContext: ['body_SamePartyFromCrossPartyContext'], + NameValuePairExceedsMaxSize: ['body_NameValuePairExceedsMaxSize'], + InvalidDomain: ['body_InvalidDomain'], + SameSiteLax: + "This cookie was blocked because it had the 'SameSite=Lax' attribute and the request was made from a different site and was not initiated by a top-level navigation.", + SyntaxError: "This 'Set-Cookie' header had invalid syntax.", + SchemeNotSupported: + 'The scheme of this connection is not allowed to store cookies.', + OverwriteSecure: + "This attempt to set a cookie via a 'Set-Cookie' header was blocked because it was not sent over a secure connection and would have overwritten a cookie with the Secure attribute.", + InvalidPrefix: + "This attempt to set a cookie via a 'Set-Cookie' header was blocked because it used the '__Secure-' or '__Host-' prefix in its name and broke the additional rules applied to cookies with these prefixes.", + SamePartyConflictsWithOtherAttributes: + "This attempt to set a cookie via a 'Set-Cookie' header was blocked because it had the 'SameParty' attribute but also had other conflicting attributes. Chrome requires cookies that use the 'SameParty' attribute to also have the 'Secure' attribute, and to not be restricted to 'SameSite=Strict'.", + DisallowedCharacter: + "This 'Set-Cookie' header contained a disallowed character (a forbidden ASCII control character, or the tab character if it appears in the middle of the cookie name, value, an attribute name, or an attribute value).", + NoCookieContent: + "This attempt to set a cookie via a 'Set-Cookie' header was blocked because the header did not contain any value.", }; export default CookieBlockedReason; diff --git a/packages/common/src/data/cookieExclusionAndWarningReasons/cookieExclusionReason.ts b/packages/common/src/data/cookieExclusionAndWarningReasons/cookieExclusionReason.ts index 157f37bdb..4d5703420 100644 --- a/packages/common/src/data/cookieExclusionAndWarningReasons/cookieExclusionReason.ts +++ b/packages/common/src/data/cookieExclusionAndWarningReasons/cookieExclusionReason.ts @@ -13,6 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/** + * External dependencies. + */ +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. */ @@ -32,10 +36,14 @@ const codeForCookieIssueDetails = ( if (warningReasons.includes('WarnSameSiteStrictLaxDowngradeStrict')) { // For secure URL. if (isURLSecure) { - return CookieExclusionReasonHTMLContent.sameSiteExcludeNavigationContextDowngradeSecure; + return I18n.getFormattedMessages( + CookieExclusionReasonHTMLContent.sameSiteExcludeNavigationContextDowngradeSecure + ); } // For insecure URL. - return CookieExclusionReasonHTMLContent.sameSiteExcludeNavigationContextDowngradeInsecure; + return I18n.getFormattedMessages( + CookieExclusionReasonHTMLContent.sameSiteExcludeNavigationContextDowngradeInsecure + ); } if ( @@ -48,31 +56,43 @@ const codeForCookieIssueDetails = ( if (operation === 'SetCookie') { // For secure URL. if (isURLSecure) { - return CookieExclusionReasonHTMLContent.sameSiteExcludeContextDowngradeSetCookieSecure; + return I18n.getFormattedMessages( + CookieExclusionReasonHTMLContent.sameSiteExcludeContextDowngradeSetCookieSecure + ); } // For insecure URL. - return CookieExclusionReasonHTMLContent.sameSiteExcludeContextDowngradeSetCookieInsecure; + return I18n.getFormattedMessages( + CookieExclusionReasonHTMLContent.sameSiteExcludeContextDowngradeSetCookieInsecure + ); } // For ReadCookie operation. // For secure URL. if (isURLSecure) { - return CookieExclusionReasonHTMLContent.sameSiteExcludeContextDowngradeReadCookieSecure; + return I18n.getFormattedMessages( + CookieExclusionReasonHTMLContent.sameSiteExcludeContextDowngradeReadCookieSecure + ); } // For insecure URL. - return CookieExclusionReasonHTMLContent.sameSiteExcludeContextDowngradeReadCookieInsecure; + return I18n.getFormattedMessages( + CookieExclusionReasonHTMLContent.sameSiteExcludeContextDowngradeReadCookieInsecure + ); } } if ( warningReasons?.includes('WarnCrossSiteRedirectDowngradeChangesInclusion') ) { - return CookieExclusionReasonHTMLContent.cookieCrossSiteRedirectDowngrade; + return I18n.getFormattedMessages( + CookieExclusionReasonHTMLContent.cookieCrossSiteRedirectDowngrade + ); } // If we have ExcludeSameSiteUnspecifiedTreatedAsLax but no corresponding warnings, then add just // the Issue code for ExcludeSameSiteUnspecifiedTreatedAsLax. if (reason === 'ExcludeSameSiteUnspecifiedTreatedAsLax') { - return CookieExclusionReasonHTMLContent.ExcludeSameSiteUnspecifiedTreatedAsLax; + return I18n.getFormattedMessages( + CookieExclusionReasonHTMLContent.ExcludeSameSiteUnspecifiedTreatedAsLax + ); } // ExcludeSameSiteStrict and ExcludeSameSiteLax require being paired with an appropriate warning. We didn't @@ -97,10 +117,14 @@ const CookieExclusionReason = { ExcludeSameSiteNoneInsecure: (operation?: string): string => { // For SetCookie operation. if (operation === 'SetCookie') { - return CookieExclusionReasonHTMLContent.ExcludeSameSiteNoneInsecureSetCookie; + return I18n.getFormattedMessages( + CookieExclusionReasonHTMLContent.ExcludeSameSiteNoneInsecureSetCookie + ); } // For ReadCookie operation. - return CookieExclusionReasonHTMLContent.ExcludeSameSiteNoneInsecureReadCookie; + return I18n.getFormattedMessages( + CookieExclusionReasonHTMLContent.ExcludeSameSiteNoneInsecureReadCookie + ); }, ExcludeSameSiteLax: ( warningReasons?: string[], @@ -129,24 +153,36 @@ const CookieExclusionReason = { ); }, ExcludeInvalidSameParty: (): string => { - return CookieExclusionReasonHTMLContent.ExcludeInvalidSameParty; + return I18n.getFormattedMessages( + CookieExclusionReasonHTMLContent.ExcludeInvalidSameParty + ); }, ExcludeSamePartyCrossPartyContext: (): string => { - return CookieExclusionReasonHTMLContent.ExcludeSamePartyCrossPartyContext; + return I18n.getFormattedMessages( + CookieExclusionReasonHTMLContent.ExcludeSamePartyCrossPartyContext + ); }, ExcludeDomainNonASCII: (): string => { - return CookieExclusionReasonHTMLContent.ExcludeDomainNonASCII; + return I18n.getFormattedMessages( + CookieExclusionReasonHTMLContent.ExcludeDomainNonASCII + ); }, ExcludeThirdPartyCookieBlockedInFirstPartySet: (): string => { - return CookieExclusionReasonHTMLContent.ExcludeThirdPartyCookieBlockedInFirstPartySet; + return I18n.getFormattedMessages( + CookieExclusionReasonHTMLContent.ExcludeThirdPartyCookieBlockedInFirstPartySet + ); }, ExcludeThirdPartyPhaseout: (operation?: string): string => { // For SetCookie operation. if (operation === 'SetCookie') { - return CookieExclusionReasonHTMLContent.ExcludeThirdPartyPhaseoutSetCookie; + return I18n.getFormattedMessages( + CookieExclusionReasonHTMLContent.ExcludeThirdPartyPhaseoutSetCookie + ); } // For ReadCookie operation. - return CookieExclusionReasonHTMLContent.ExcludeThirdPartyPhaseoutReadCookie; + return I18n.getFormattedMessages( + CookieExclusionReasonHTMLContent.ExcludeThirdPartyPhaseoutReadCookie + ); }, }; diff --git a/packages/common/src/data/cookieExclusionAndWarningReasons/cookieExclusionReasonHTMLContent.ts b/packages/common/src/data/cookieExclusionAndWarningReasons/cookieExclusionReasonHTMLContent.ts index 816b25b13..59729f80e 100644 --- a/packages/common/src/data/cookieExclusionAndWarningReasons/cookieExclusionReasonHTMLContent.ts +++ b/packages/common/src/data/cookieExclusionAndWarningReasons/cookieExclusionReasonHTMLContent.ts @@ -13,147 +13,105 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export const sameSiteExcludeNavigationContextDowngradeSecure = ` -

Migrate entirely to HTTPS to have cookies sent on same-site requests

-

A cookie was not sent to a secure origin from an insecure context on a navigation. Because this cookie would have been sent across schemes on the same site, it was not sent. This behavior enhances the SameSite attribute's protection of user data from request forgery by network attackers.

-

Resolve this issue by migrating your site (as defined by the eTLD+1) entirely to HTTPS. It is also recommended to mark the cookie with the Secure attribute if that is not already the case.

-
-`; +export const sameSiteExcludeNavigationContextDowngradeSecure = [ + 'header_sameSiteExcludeNavigationContextDowngradeSecure', + 'body_1_sameSiteExcludeNavigationContextDowngradeSecure', + 'body_2_sameSiteExcludeNavigationContextDowngradeSecure', +]; // Filename: SameSiteExcludeNavigationContextDowngrade.md -export const sameSiteExcludeNavigationContextDowngradeInsecure = ` -

Migrate entirely to HTTPS to have cookies sent on same-site requests

-

A cookie was not sent to an insecure origin from a secure context on a navigation. Because this cookie would have been sent across schemes on the same site, it was not sent. This behavior enhances the SameSite attribute's protection of user data from request forgery by network attackers.

-

Resolve this issue by migrating your site (as defined by the eTLD+1) entirely to HTTPS. It is also recommended to mark the cookie with the Secure attribute if that is not already the case.

-
-`; +export const sameSiteExcludeNavigationContextDowngradeInsecure = [ + 'header_sameSiteExcludeNavigationContextDowngradeInsecure', + 'body_1_sameSiteExcludeNavigationContextDowngradeInsecure', + 'body_2_sameSiteExcludeNavigationContextDowngradeInsecure', +]; // Filename: SameSiteExcludeContextDowngradeSet.md -export const sameSiteExcludeContextDowngradeSetCookieSecure = ` -

Migrate entirely to HTTPS to allow cookies to be set by same-site subresources

-

A cookie was not set by an insecure origin in a secure context. Because this cookie would have been set across schemes on the same site, it was blocked. This behavior enhances the SameSite attribute's protection of user data from request forgery by network attackers.

-

Resolve this issue by migrating your site (as defined by the eTLD+1) entirely to HTTPS. It is also recommended to mark the cookie with the Secure attribute if that is not already the case.

-
-`; +export const sameSiteExcludeContextDowngradeSetCookieSecure = [ + 'header_sameSiteExcludeContextDowngradeSetCookieSecure', + 'body_1_sameSiteExcludeContextDowngradeSetCookieSecure', + 'body_2_sameSiteExcludeContextDowngradeSetCookieSecure', +]; // Filename: SameSiteExcludeContextDowngradeSet.md -export const sameSiteExcludeContextDowngradeSetCookieInsecure = ` -

Migrate entirely to HTTPS to allow cookies to be set by same-site subresources

-

A cookie was not set by a secure origin in an insecure context. Because this cookie would have been set across schemes on the same site, it was blocked. This behavior enhances the SameSite attribute's protection of user data from request forgery by network attackers.

-

Resolve this issue by migrating your site (as defined by the eTLD+1) entirely to HTTPS. It is also recommended to mark the cookie with the Secure attribute if that is not already the case.

-
-`; +export const sameSiteExcludeContextDowngradeSetCookieInsecure = [ + 'header_sameSiteExcludeContextDowngradeSetCookieInsecure', + 'body_1_sameSiteExcludeContextDowngradeSetCookieInsecure', + 'body_2_sameSiteExcludeContextDowngradeSetCookieInsecure', +]; // Filename: SameSiteExcludeContextDowngradeRead.md -export const sameSiteExcludeContextDowngradeReadCookieSecure = ` -

Migrate entirely to HTTPS to have cookies sent to same-site subresources

-

A cookie was not sent to a secure origin from an insecure context. Because this cookie would have been sent across schemes on the same site, it was not sent. This behavior enhances the SameSite attribute's protection of user data from request forgery by network attackers.

-

Resolve this issue by migrating your site (as defined by the eTLD+1) entirely to HTTPS. It is also recommended to mark the cookie with the Secure attribute if that is not already the case.

-
-`; +export const sameSiteExcludeContextDowngradeReadCookieSecure = [ + 'header_sameSiteExcludeContextDowngradeReadCookieSecure', + 'body_1_sameSiteExcludeContextDowngradeReadCookieSecure', + 'body_2_sameSiteExcludeContextDowngradeReadCookieSecure', +]; // Filename: SameSiteExcludeContextDowngradeRead.md -export const sameSiteExcludeContextDowngradeReadCookieInsecure = ` -

Migrate entirely to HTTPS to have cookies sent to same-site subresources

-

A cookie was not sent to an insecure origin from a secure context. Because this cookie would have been sent across schemes on the same site, it was not sent. This behavior enhances the SameSite attribute's protection of user data from request forgery by network attackers.

-

Resolve this issue by migrating your site (as defined by the eTLD+1) entirely to HTTPS. It is also recommended to mark the cookie with the Secure attribute if that is not already the case.

-
-`; +export const sameSiteExcludeContextDowngradeReadCookieInsecure = [ + 'header_sameSiteExcludeContextDowngradeReadCookieInsecure', + 'body_1_sameSiteExcludeContextDowngradeReadCookieInsecure', + 'body_2_sameSiteExcludeContextDowngradeReadCookieInsecure', +]; // Filename: NetworkRequest.ts -export const ExcludeSameSiteUnspecifiedTreatedAsLax = - "

This Set-Cookie header didn't specify a 'SameSite' attribute and was defaulted to SameSite=Lax and was blocked because it came from a cross-site response which was not the response to a top-level navigation. The Set-Cookie had to have been set with SameSite=None to enable cross-site usage.


"; +export const ExcludeSameSiteUnspecifiedTreatedAsLax = [ + 'body_ExcludeSameSiteUnspecifiedTreatedAsLax', +]; // Filename: cookieCrossSiteRedirectDowngrade.md -export const cookieCrossSiteRedirectDowngrade = ` -

Cookie is blocked due to a cross-site redirect chain

-

The cookie was blocked because the URL redirect chain was not fully same-site, meaning the final request was treated as a cross-site request. Like other cross-site requests, this blocks cookies with SameSite=Lax or SameSite=Strict.

-

For example: If site A redirects to site B which then redirects back to site A, the final request to site A will be a cross-site request.

-
-`; +export const cookieCrossSiteRedirectDowngrade = [ + 'header_cookieCrossSiteRedirectDowngrade', + 'body_1_cookieCrossSiteRedirectDowngrade', + 'body_2_cookieCrossSiteRedirectDowngrade', +]; // Filename: SameSiteNoneInsecureErrorSet.md -export const ExcludeSameSiteNoneInsecureSetCookie = ` -

Mark cross-site cookies as Secure to allow setting them in cross-site contexts

-

Cookies marked with SameSite=None must also be marked with Secure to allow setting them in a cross-site context. This behavior protects user data from being sent over an insecure connection.

-

Resolve this issue by updating the attributes of the cookie:

-
    -
  • Specify SameSite=None and Secure if the cookie is intended to be set in cross-site contexts. Note that only cookies sent over HTTPS may use the Secure attribute.
  • -
  • Specify SameSite=Strict or SameSite=Lax if the cookie should not be set by cross-site requests.
  • -
-
-`; +export const ExcludeSameSiteNoneInsecureSetCookie = [ + 'header_ExcludeSameSiteNoneInsecureSetCookie', + 'body_ExcludeSameSiteNoneInsecureSetCookie', +]; // Filename: SameSiteNoneInsecureErrorRead.md -export const ExcludeSameSiteNoneInsecureReadCookie = ` -

Mark cross-site cookies as Secure to allow them to be sent in cross-site requests

-

Cookies marked with SameSite=None must also be marked with Secure to get sent in cross-site requests. - This behavior protects user data from being sent over an insecure connection.

-

Resolve this issue by updating the attributes of the cookie:

-
    -
  • Specify SameSite=None and Secure if the cookie should be sent in cross-site requests. This enables third-party use.
  • -
  • Specify SameSite=Strict or SameSite=Lax if the cookie should not be sent in cross-site requests.
  • -
-
-`; +export const ExcludeSameSiteNoneInsecureReadCookie = [ + 'header_ExcludeSameSiteNoneInsecureReadCookie', + 'body_ExcludeSameSiteNoneInsecureReadCookie', +]; // Filename: SameSiteInvalidSameParty.md -export const ExcludeInvalidSameParty = ` -

Mark SameParty cookies as Secure and do not use SameSite=Strict for SameParty cookies

-

Cookies marked with SameParty must also be marked with Secure. In addition, cookies marked with SameParty cannot use SameSite=Strict.

-

Resolve this issue by updating the attributes of the cookie:

-
    -
  • Remove SameParty if the cookie should only be used by the same site but not the same first-party set
  • -
  • Remove SameSite=Strict and specify Secure if the cookie should be available to all sites of the same first-party set
  • -
-
-`; +export const ExcludeInvalidSameParty = [ + 'header_ExcludeInvalidSameParty', + 'body_ExcludeInvalidSameParty', +]; // Filename: SameSiteSamePartyCrossPartyContextSet.md -export const ExcludeSamePartyCrossPartyContext = ` -

Make sure a cookie is using the SameParty attribute correctly

-

Setting cross-site cookies with the SameParty attribute is only possible if both domains are a part of the same First-Party Set.

-

To allow setting cross-site cookies, try one of the following:

-
    -
  • If the domains satisfy the First-Party Set criteria, add them to the same First-Party Set.
  • -
  • If the domains don't satisfy the First-Party Set criteria, remove the SameParty attribute and specify SameSite=None.
  • -
-

If you don't have the option to do any of the above, cookies are not intended to be set in cross-site contexts.

-
-`; +export const ExcludeSamePartyCrossPartyContext = [ + 'header_ExcludeSamePartyCrossPartyContext', + 'body_ExcludeSamePartyCrossPartyContext', +]; // Filename: cookieExcludeDomainNonAscii.md -export const ExcludeDomainNonASCII = ` -

Ensure cookie Domain attribute values only contain ASCII characters

-

Domain attributes in cookies are restricted to the ASCII character set. Any cookies that contain characters outside of the ASCII range in their Domain attribute will be ignored.

-

To resolve this issue, you need to remove all non-ASCII characters from the Domain attribute of the affected cookies.

-

If your site has an internationalized domain name (IDN), you should use punycode representation for the Domain attribute instead.

-
-`; +export const ExcludeDomainNonASCII = [ + 'header_ExcludeDomainNonASCII', + 'body_1_ExcludeDomainNonASCII', + 'body_2_ExcludeDomainNonASCII', + 'body_3_ExcludeDomainNonASCII', +]; // Filename: cookieExcludeBlockedWithinFirstPartySet.md -export const ExcludeThirdPartyCookieBlockedInFirstPartySet = ` -

Third-party cookie blocked within the same First-Party Set

-

- A cookie embedded by a site in the top-level page's First-Party Set was blocked by third-party cookie blocking. -

-
-`; +export const ExcludeThirdPartyCookieBlockedInFirstPartySet = [ + 'header_ExcludeThirdPartyCookieBlockedInFirstPartySet', + 'body_ExcludeThirdPartyCookieBlockedInFirstPartySet', +]; // Filename: cookieExcludeThirdPartyPhaseoutRead.md -export const ExcludeThirdPartyPhaseoutReadCookie = ` -

Cookie is blocked when sent in cross-site context

-

- Cookies marked with SameSite=None; Secure; and not Partitioned are blocked in cross-site requests. This behavior protects user data from cross-site tracking. -

-
-`; +export const ExcludeThirdPartyPhaseoutReadCookie = [ + 'header_ExcludeThirdPartyPhaseoutReadCookie', + 'body_ExcludeThirdPartyPhaseoutReadCookie', +]; // Filename: cookieExcludeThirdPartyPhaseoutSet.md -export const ExcludeThirdPartyPhaseoutSetCookie = ` -

Cookie is blocked when set in cross-site context

-

- Cookies marked with SameSite=None; Secure and not Partitioned are blocked in cross-site contexts. This behavior protects user data from cross-site tracking. -

-
-`; +export const ExcludeThirdPartyPhaseoutSetCookie = [ + 'header_ExcludeThirdPartyPhaseoutSetCookie', + 'body_ExcludeThirdPartyPhaseoutSetCookie', +]; diff --git a/packages/common/src/data/cookieExclusionAndWarningReasons/cookieWarningReason.ts b/packages/common/src/data/cookieExclusionAndWarningReasons/cookieWarningReason.ts index d6e08980a..bdb8825cf 100644 --- a/packages/common/src/data/cookieExclusionAndWarningReasons/cookieWarningReason.ts +++ b/packages/common/src/data/cookieExclusionAndWarningReasons/cookieWarningReason.ts @@ -13,6 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/** + * External dependencies. + */ +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. */ @@ -29,34 +33,50 @@ const CookieWarningReason = { WarnSameSiteUnspecifiedCrossSiteContext: (operation: string): string => { // For SetCookie operation. if (operation === 'SetCookie') { - return CookieWarningReasonHTMLContent.WarnSameSiteUnspecifiedCrossSiteContextSetCookie; + return I18n.getFormattedMessages( + CookieWarningReasonHTMLContent.WarnSameSiteUnspecifiedCrossSiteContextSetCookie + ); } // For ReadCookie operation. - return CookieWarningReasonHTMLContent.WarnSameSiteUnspecifiedCrossSiteContextReadCookie; + return I18n.getFormattedMessages( + CookieWarningReasonHTMLContent.WarnSameSiteUnspecifiedCrossSiteContextReadCookie + ); }, WarnSameSiteNoneInsecure: (operation: string): string => { // For SetCookie operation. if (operation === 'SetCookie') { - return CookieWarningReasonHTMLContent.WarnSameSiteNoneInsecureSetCookie; + return I18n.getFormattedMessages( + CookieWarningReasonHTMLContent.WarnSameSiteNoneInsecureSetCookie + ); } // For ReadCookie operation. - return CookieWarningReasonHTMLContent.WarnSameSiteNoneInsecureReadCookie; + return I18n.getFormattedMessages( + CookieWarningReasonHTMLContent.WarnSameSiteNoneInsecureReadCookie + ); }, WarnSameSiteUnspecifiedLaxAllowUnsafe: (operation: string): string => { // For SetCookie operation. if (operation === 'SetCookie') { - return CookieWarningReasonHTMLContent.WarnSameSiteUnspecifiedLaxAllowUnsafeSetCookie; + return I18n.getFormattedMessages( + CookieWarningReasonHTMLContent.WarnSameSiteUnspecifiedLaxAllowUnsafeSetCookie + ); } // For ReadCookie operation. - return CookieWarningReasonHTMLContent.WarnSameSiteUnspecifiedLaxAllowUnsafeReadCookie; + return I18n.getFormattedMessages( + CookieWarningReasonHTMLContent.WarnSameSiteUnspecifiedLaxAllowUnsafeReadCookie + ); }, WarnSameSiteStrictLaxDowngradeStrict: (cookieURL: string): string => { // For secure URL. if (isURLSecure(cookieURL)) { - return CookieWarningReasonHTMLContent.WarnSameSiteStrictLaxDowngradeStrictSecure; + return I18n.getFormattedMessages( + CookieWarningReasonHTMLContent.WarnSameSiteStrictLaxDowngradeStrictSecure + ); } // For insecure URL. - return CookieWarningReasonHTMLContent.WarnSameSiteStrictLaxDowngradeStrictInsecure; + return I18n.getFormattedMessages( + CookieWarningReasonHTMLContent.WarnSameSiteStrictLaxDowngradeStrictInsecure + ); }, WarnSameSiteStrictCrossDowngradeStrict: ( operation: string, @@ -66,18 +86,26 @@ const CookieWarningReason = { if (operation === 'SetCookie') { // For secure URL. if (isURLSecure(cookieURL)) { - return CookieWarningReasonHTMLContent.WarnSameSiteStrictCrossDowngradeStrictSetCookieSecure; + return I18n.getFormattedMessages( + CookieWarningReasonHTMLContent.WarnSameSiteStrictCrossDowngradeStrictSetCookieSecure + ); } // For insecure URL. - return CookieWarningReasonHTMLContent.WarnSameSiteStrictCrossDowngradeStrictSetCookieInsecure; + return I18n.getFormattedMessages( + CookieWarningReasonHTMLContent.WarnSameSiteStrictCrossDowngradeStrictSetCookieInsecure + ); } // For ReadCookie operation. // For secure URL. if (isURLSecure(cookieURL)) { - return CookieWarningReasonHTMLContent.WarnSameSiteStrictCrossDowngradeStrictReadCookieSecure; + return I18n.getFormattedMessages( + CookieWarningReasonHTMLContent.WarnSameSiteStrictCrossDowngradeStrictReadCookieSecure + ); } // For insecure URL. - return CookieWarningReasonHTMLContent.WarnSameSiteStrictCrossDowngradeStrictReadCookieInsecure; + return I18n.getFormattedMessages( + CookieWarningReasonHTMLContent.WarnSameSiteStrictCrossDowngradeStrictReadCookieInsecure + ); }, WarnSameSiteStrictCrossDowngradeLax: ( operation: string, @@ -87,18 +115,26 @@ const CookieWarningReason = { if (operation === 'SetCookie') { // For secure URL. if (isURLSecure(cookieURL)) { - return CookieWarningReasonHTMLContent.WarnSameSiteStrictCrossDowngradeLaxSetCookieSecure; + return I18n.getFormattedMessages( + CookieWarningReasonHTMLContent.WarnSameSiteStrictCrossDowngradeLaxSetCookieSecure + ); } // For insecure URL. - return CookieWarningReasonHTMLContent.WarnSameSiteStrictCrossDowngradeLaxSetCookieInsecure; + return I18n.getFormattedMessages( + CookieWarningReasonHTMLContent.WarnSameSiteStrictCrossDowngradeLaxSetCookieInsecure + ); } // For ReadCookie operation. // For secure URL. if (isURLSecure(cookieURL)) { - return CookieWarningReasonHTMLContent.WarnSameSiteStrictCrossDowngradeLaxReadCookieSecure; + return I18n.getFormattedMessages( + CookieWarningReasonHTMLContent.WarnSameSiteStrictCrossDowngradeLaxReadCookieSecure + ); } // For insecure URL. - return CookieWarningReasonHTMLContent.WarnSameSiteStrictCrossDowngradeLaxReadCookieInsecure; + return I18n.getFormattedMessages( + CookieWarningReasonHTMLContent.WarnSameSiteStrictCrossDowngradeLaxReadCookieInsecure + ); }, WarnSameSiteLaxCrossDowngradeStrict: ( operation: string, @@ -108,18 +144,26 @@ const CookieWarningReason = { if (operation === 'SetCookie') { // For secure URL. if (isURLSecure(cookieURL)) { - return CookieWarningReasonHTMLContent.WarnSameSiteLaxCrossDowngradeStrictSetCookieSecure; + return I18n.getFormattedMessages( + CookieWarningReasonHTMLContent.WarnSameSiteLaxCrossDowngradeStrictSetCookieSecure + ); } // For insecure URL. - return CookieWarningReasonHTMLContent.WarnSameSiteLaxCrossDowngradeStrictSetCookieInsecure; + return I18n.getFormattedMessages( + CookieWarningReasonHTMLContent.WarnSameSiteLaxCrossDowngradeStrictSetCookieInsecure + ); } // For ReadCookie operation. // For secure URL. if (isURLSecure(cookieURL)) { - return CookieWarningReasonHTMLContent.WarnSameSiteLaxCrossDowngradeStrictReadCookieSecure; + return I18n.getFormattedMessages( + CookieWarningReasonHTMLContent.WarnSameSiteLaxCrossDowngradeStrictReadCookieSecure + ); } // For insecure URL. - return CookieWarningReasonHTMLContent.WarnSameSiteLaxCrossDowngradeStrictReadCookieInsecure; + return I18n.getFormattedMessages( + CookieWarningReasonHTMLContent.WarnSameSiteLaxCrossDowngradeStrictReadCookieInsecure + ); }, WarnSameSiteLaxCrossDowngradeLax: ( operation: string, @@ -129,35 +173,53 @@ const CookieWarningReason = { if (operation === 'SetCookie') { // For secure URL. if (isURLSecure(cookieURL)) { - return CookieWarningReasonHTMLContent.WarnSameSiteLaxCrossDowngradeLaxSetCookieSecure; + return I18n.getFormattedMessages( + CookieWarningReasonHTMLContent.WarnSameSiteLaxCrossDowngradeLaxSetCookieSecure + ); } // For insecure URL. - return CookieWarningReasonHTMLContent.WarnSameSiteLaxCrossDowngradeLaxSetCookieInsecure; + return I18n.getFormattedMessages( + CookieWarningReasonHTMLContent.WarnSameSiteLaxCrossDowngradeLaxSetCookieInsecure + ); } // For ReadCookie operation. // For secure URL. if (isURLSecure(cookieURL)) { - return CookieWarningReasonHTMLContent.WarnSameSiteLaxCrossDowngradeLaxReadCookieSecure; + return I18n.getFormattedMessages( + CookieWarningReasonHTMLContent.WarnSameSiteLaxCrossDowngradeLaxReadCookieSecure + ); } // For insecure URL. - return CookieWarningReasonHTMLContent.WarnSameSiteLaxCrossDowngradeLaxReadCookieInsecure; + return I18n.getFormattedMessages( + CookieWarningReasonHTMLContent.WarnSameSiteLaxCrossDowngradeLaxReadCookieInsecure + ); }, WarnAttributeValueExceedsMaxSize: (): string => { - return CookieWarningReasonHTMLContent.WarnAttributeValueExceedsMaxSize; + return I18n.getFormattedMessages( + CookieWarningReasonHTMLContent.WarnAttributeValueExceedsMaxSize + ); }, WarnDomainNonASCII: (): string => { - return CookieWarningReasonHTMLContent.WarnDomainNonASCII; + return I18n.getFormattedMessages( + CookieWarningReasonHTMLContent.WarnDomainNonASCII + ); }, WarnThirdPartyPhaseout: (operation: string): string => { // For SetCookie operation. if (operation === 'SetCookie') { - return CookieWarningReasonHTMLContent.WarnThirdPartyPhaseoutSetCookie; + return I18n.getFormattedMessages( + CookieWarningReasonHTMLContent.WarnThirdPartyPhaseoutSetCookie + ); } // For ReadCookie operation. - return CookieWarningReasonHTMLContent.WarnThirdPartyPhaseoutReadCookie; + return I18n.getFormattedMessages( + CookieWarningReasonHTMLContent.WarnThirdPartyPhaseoutReadCookie + ); }, WarnCrossSiteRedirectDowngradeChangesInclusion: (): string => { - return CookieWarningReasonHTMLContent.WarnCrossSiteRedirectDowngradeChangesInclusion; + return I18n.getFormattedMessages( + CookieWarningReasonHTMLContent.WarnCrossSiteRedirectDowngradeChangesInclusion + ); }, }; diff --git a/packages/common/src/data/cookieExclusionAndWarningReasons/cookieWarningReasonHTMLContent.ts b/packages/common/src/data/cookieExclusionAndWarningReasons/cookieWarningReasonHTMLContent.ts index 4ac01058c..e80761329 100644 --- a/packages/common/src/data/cookieExclusionAndWarningReasons/cookieWarningReasonHTMLContent.ts +++ b/packages/common/src/data/cookieExclusionAndWarningReasons/cookieWarningReasonHTMLContent.ts @@ -13,256 +13,197 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export const WarnSameSiteUnspecifiedCrossSiteContextReadCookie = ` -

Indicate whether to send a cookie in a cross-site request by specifying its SameSite attribute

-

Because a cookie's SameSite attribute was not set or is invalid, it defaults to SameSite=Lax, which will prevent the cookie from being sent in a cross-site request in a future version of the browser. This behavior protects user data from accidentally leaking to third parties and cross-site request forgery.

-

Resolve this issue by updating the attributes of the cookie:

-
    -
  • Specify SameSite=None and Secure if the cookie should be sent in cross-site requests. This enables third-party use.
  • -
  • Specify SameSite=Strict or SameSite=Lax if the cookie should not be sent in cross-site requests.
  • -
-
-`; +export const WarnSameSiteUnspecifiedCrossSiteContextReadCookie = [ + 'header_WarnSameSiteUnspecifiedCrossSiteContextReadCookie', + 'body_WarnSameSiteUnspecifiedCrossSiteContextReadCookie', +]; // Filename: SameSiteUnspecifiedLaxAllowUnsafeSet.md -export const WarnSameSiteUnspecifiedCrossSiteContextSetCookie = ` -

Indicate whether a cookie is intended to be set in cross-site context by specifying its SameSite attribute

-

Because a cookie's SameSite attribute was not set or is invalid, it defaults to SameSite=Lax, which will prevent the cookie from being set in a cross-site context in a future version of the browser. This behavior protects user data from accidentally leaking to third parties and cross-site request forgery.

-

Resolve this issue by updating the attributes of the cookie:

-
    -
  • Specify SameSite=None and Secure if the cookie is intended to be set in cross-site contexts. Note that only cookies sent over HTTPS may use the Secure attribute.
  • -
  • Specify SameSite=Strict or SameSite=Lax if the cookie should not be set by cross-site requests.
  • -
-
-`; +export const WarnSameSiteUnspecifiedCrossSiteContextSetCookie = [ + 'header_WarnSameSiteUnspecifiedCrossSiteContextSetCookie', + 'body_WarnSameSiteUnspecifiedCrossSiteContextSetCookie', +]; // Filename: SameSiteNoneInsecureWarnRead.md -export const WarnSameSiteNoneInsecureReadCookie = ` -

Mark cross-site cookies as Secure to allow them to be sent in cross-site requests

-

In a future version of the browser, cookies marked with SameSite=None must also be marked with Secure to get sent in cross-site requests. This behavior protects user data from being sent over an insecure connection.

-

Resolve this issue by updating the attributes of the cookie:

-
    -
  • Specify SameSite=None and Secure if the cookie should be sent in cross-site requests. This enables third-party use.
  • -
  • Specify SameSite=Strict or SameSite=Lax if the cookie should not be sent in cross-site requests.
  • -
-
-`; +export const WarnSameSiteNoneInsecureReadCookie = [ + 'header_WarnSameSiteNoneInsecureReadCookie', + 'body_WarnSameSiteNoneInsecureReadCookie', +]; // Filename: SameSiteNoneInsecureWarnSet.md -export const WarnSameSiteNoneInsecureSetCookie = ` -

Mark cross-site cookies as Secure to allow setting them in cross-site contexts

-

In a future version of the browser, cookies marked with SameSite=None must also be marked with Secure to allow setting them in a cross-site context. This behavior protects user data from being sent over an insecure connection.

-

Resolve this issue by updating the attributes of the cookie:

-
    -
  • Specify SameSite=None and Secure if the cookie is intended to be set in cross-site contexts. Note that only cookies sent over HTTPS may use the Secure attribute.
  • -
  • Specify SameSite=Strict or SameSite=Lax if the cookie should not be set by cross-site requests.
  • -
-
-`; +export const WarnSameSiteNoneInsecureSetCookie = [ + 'header_WarnSameSiteNoneInsecureSetCookie', + 'body_WarnSameSiteNoneInsecureSetCookie', +]; // Filename: SameSiteUnspecifiedLaxAllowUnsafeRead.md -export const WarnSameSiteUnspecifiedLaxAllowUnsafeReadCookie = ` -

Indicate whether to send a cookie in a cross-site request by specifying its SameSite attribute

-

Because a cookie's SameSite attribute was not set or is invalid, it defaults to SameSite=Lax, which will prevent the cookie from being sent in a cross-site request in a future version of the browser. This behavior protects user data from accidentally leaking to third parties and cross-site request forgery.

-

Resolve this issue by updating the attributes of the cookie:

-
    -
  • Specify SameSite=None and Secure if the cookie should be sent in cross-site requests. This enables third-party use.
  • -
  • Specify SameSite=Strict or SameSite=Lax if the cookie should not be sent in cross-site requests.
  • -
-
-`; +export const WarnSameSiteUnspecifiedLaxAllowUnsafeReadCookie = [ + 'header_WarnSameSiteUnspecifiedLaxAllowUnsafeReadCookie', + 'body_WarnSameSiteUnspecifiedLaxAllowUnsafeReadCookie', +]; // Filename: SameSiteUnspecifiedLaxAllowUnsafeSet.md -export const WarnSameSiteUnspecifiedLaxAllowUnsafeSetCookie = ` -

Indicate whether a cookie is intended to be set in cross-site context by specifying its SameSite attribute

-

Because a cookie's SameSite attribute was not set or is invalid, it defaults to SameSite=Lax, which will prevents the cookie from being set in a cross-site context in a future version of the browser. This behavior protects user data from accidentally leaking to third parties and cross-site request forgery.

-

Resolve this issue by updating the attributes of the cookie:

-
    -
  • Specify SameSite=None and Secure if the cookie is intended to be set in cross-site contexts. Note that only cookies sent over HTTPS may use the Secure attribute.
  • -
  • Specify SameSite=Strict or SameSite=Lax if the cookie should not be set by cross-site requests.
  • -
-
-`; +export const WarnSameSiteUnspecifiedLaxAllowUnsafeSetCookie = [ + 'header_WarnSameSiteUnspecifiedLaxAllowUnsafeSetCookie', + 'body_WarnSameSiteUnspecifiedLaxAllowUnsafeSetCookie', +]; // Filename: SameSiteWarnStrictLaxDowngradeStrict.md -export const WarnSameSiteStrictLaxDowngradeStrictSecure = ` -

Migrate entirely to HTTPS to continue having cookies sent on same-site requests

-

A cookie is being sent to a secure origin from an insecure context on a navigation. Because this cookie is being sent across schemes on the same site, it will not be sent in a future version of Chrome. This behavior enhances the SameSite attribute's protection of user data from request forgery by network attackers.

-

Resolve this issue by migrating your site (as defined by the eTLD+1) entirely to HTTPS. It is also recommended to mark the cookie with the Secure attribute if that is not already the case.

-
-`; +export const WarnSameSiteStrictLaxDowngradeStrictSecure = [ + 'header_WarnSameSiteStrictLaxDowngradeStrictSecure', + 'body_1_WarnSameSiteStrictLaxDowngradeStrictSecure', + 'body_2_WarnSameSiteStrictLaxDowngradeStrictSecure', +]; // Filename: SameSiteWarnStrictLaxDowngradeStrict.md -export const WarnSameSiteStrictLaxDowngradeStrictInsecure = ` -

Migrate entirely to HTTPS to continue having cookies sent on same-site requests

-

A cookie is being sent to an insecure origin from a secure context on a navigation. Because this cookie is being sent across schemes on the same site, it will not be sent in a future version of Chrome. This behavior enhances the SameSite attribute's protection of user data from request forgery by network attackers.

-

Resolve this issue by migrating your site (as defined by the eTLD+1) entirely to HTTPS. It is also recommended to mark the cookie with the Secure attribute if that is not already the case.

-
-`; +export const WarnSameSiteStrictLaxDowngradeStrictInsecure = [ + 'header_WarnSameSiteStrictLaxDowngradeStrictInsecure', + 'body_1_WarnSameSiteStrictLaxDowngradeStrictInsecure', + 'body_2_WarnSameSiteStrictLaxDowngradeStrictInsecure', +]; // Filename: SameSiteWarnCrossDowngradeRead.md -export const WarnSameSiteStrictCrossDowngradeStrictReadCookieSecure = ` -

Migrate entirely to HTTPS to continue having cookies sent to same-site subresources

-

A cookie is being sent to a secure origin from an insecure context. Because this cookie is being sent across schemes on the same site, it will not be sent in a future version of Chrome. This behavior enhances the SameSite attribute's protection of user data from request forgery by network attackers.

-

Resolve this issue by migrating your site (as defined by the eTLD+1) entirely to HTTPS. It is also recommended to mark the cookie with the Secure attribute if that is not already the case.

-
-`; +export const WarnSameSiteStrictCrossDowngradeStrictReadCookieSecure = [ + 'header_WarnSameSiteStrictCrossDowngradeStrictReadCookieSecure', + 'body_1_WarnSameSiteStrictCrossDowngradeStrictReadCookieSecure', + 'body_2_WarnSameSiteStrictCrossDowngradeStrictReadCookieSecure', +]; // Filename: SameSiteWarnCrossDowngradeRead.md -export const WarnSameSiteStrictCrossDowngradeStrictReadCookieInsecure = ` -

Migrate entirely to HTTPS to continue having cookies sent to same-site subresources

-

A cookie is being sent to an insecure origin from a secure context. Because this cookie is being sent across schemes on the same site, it will not be sent in a future version of Chrome. This behavior enhances the SameSite attribute's protection of user data from request forgery by network attackers.

-

Resolve this issue by migrating your site (as defined by the eTLD+1) entirely to HTTPS. It is also recommended to mark the cookie with the Secure attribute if that is not already the case.

-
-`; +export const WarnSameSiteStrictCrossDowngradeStrictReadCookieInsecure = [ + 'header_WarnSameSiteStrictCrossDowngradeStrictReadCookieInsecure', + 'body_1_WarnSameSiteStrictCrossDowngradeStrictReadCookieInsecure', + 'body_2_WarnSameSiteStrictCrossDowngradeStrictReadCookieInsecure', +]; // Filename: SameSiteWarnCrossDowngradeSet.md -export const WarnSameSiteStrictCrossDowngradeStrictSetCookieSecure = ` -

Migrate entirely to HTTPS to continue allowing cookies to be set by same-site subresources

-

A cookie is being set by a secure origin in an insecure context. Because this cookie is being set across schemes on the same site, it will be blocked in a future version of Chrome. This behavior enhances the SameSite attribute's protection of user data from request forgery by network attackers.

-

Resolve this issue by migrating your site (as defined by the eTLD+1) entirely to HTTPS. It is also recommended to mark the cookie with the Secure attribute if that is not already the case.

-
-`; +export const WarnSameSiteStrictCrossDowngradeStrictSetCookieSecure = [ + 'header_WarnSameSiteStrictCrossDowngradeStrictSetCookieSecure', + 'body_1_WarnSameSiteStrictCrossDowngradeStrictSetCookieSecure', + 'body_2_WarnSameSiteStrictCrossDowngradeStrictSetCookieSecure', +]; // Filename: SameSiteWarnCrossDowngradeSet.md -export const WarnSameSiteStrictCrossDowngradeStrictSetCookieInsecure = ` -

Migrate entirely to HTTPS to continue allowing cookies to be set by same-site subresources

-

A cookie is being set by an insecure origin in a secure context. Because this cookie is being set across schemes on the same site, it will be blocked in a future version of Chrome. This behavior enhances the SameSite attribute's protection of user data from request forgery by network attackers.

-

Resolve this issue by migrating your site (as defined by the eTLD+1) entirely to HTTPS. It is also recommended to mark the cookie with the Secure attribute if that is not already the case.

-
-`; +export const WarnSameSiteStrictCrossDowngradeStrictSetCookieInsecure = [ + 'header_WarnSameSiteStrictCrossDowngradeStrictSetCookieInsecure', + 'body_1_WarnSameSiteStrictCrossDowngradeStrictSetCookieInsecure', + 'body_2_WarnSameSiteStrictCrossDowngradeStrictSetCookieInsecure', +]; // Filename: SameSiteWarnCrossDowngradeRead.md -export const WarnSameSiteStrictCrossDowngradeLaxReadCookieSecure = ` -

Migrate entirely to HTTPS to continue having cookies sent to same-site subresources

-

A cookie is being sent to a secure origin from an insecure context. Because this cookie is being sent across schemes on the same site, it will not be sent in a future version of Chrome. This behavior enhances the SameSite attribute's protection of user data from request forgery by network attackers.

-

Resolve this issue by migrating your site (as defined by the eTLD+1) entirely to HTTPS. It is also recommended to mark the cookie with the Secure attribute if that is not already the case.

-
-`; +export const WarnSameSiteStrictCrossDowngradeLaxReadCookieSecure = [ + 'header_WarnSameSiteStrictCrossDowngradeLaxReadCookieSecure', + 'body_1_WarnSameSiteStrictCrossDowngradeLaxReadCookieSecure', + 'body_2_WarnSameSiteStrictCrossDowngradeLaxReadCookieSecure', +]; // Filename: SameSiteWarnCrossDowngradeRead.md -export const WarnSameSiteStrictCrossDowngradeLaxReadCookieInsecure = ` -

Migrate entirely to HTTPS to continue having cookies sent to same-site subresources

-

A cookie is being sent to an insecure origin from a secure context. Because this cookie is being sent across schemes on the same site, it will not be sent in a future version of Chrome. This behavior enhances the SameSite attribute's protection of user data from request forgery by network attackers.

-

Resolve this issue by migrating your site (as defined by the eTLD+1) entirely to HTTPS. It is also recommended to mark the cookie with the Secure attribute if that is not already the case.

-
-`; +export const WarnSameSiteStrictCrossDowngradeLaxReadCookieInsecure = [ + 'header_WarnSameSiteStrictCrossDowngradeLaxReadCookieInsecure', + 'body_1_WarnSameSiteStrictCrossDowngradeLaxReadCookieInsecure', + 'body_2_WarnSameSiteStrictCrossDowngradeLaxReadCookieInsecure', +]; // Filename: SameSiteWarnCrossDowngradeSet.md -export const WarnSameSiteStrictCrossDowngradeLaxSetCookieSecure = ` -

Migrate entirely to HTTPS to continue allowing cookies to be set by same-site subresources

-

A cookie is being set by a secure origin in an insecure context. Because this cookie is being set across schemes on the same site, it will be blocked in a future version of Chrome. This behavior enhances the SameSite attribute's protection of user data from request forgery by network attackers.

-

Resolve this issue by migrating your site (as defined by the eTLD+1) entirely to HTTPS. It is also recommended to mark the cookie with the Secure attribute if that is not already the case.

-
-`; +export const WarnSameSiteStrictCrossDowngradeLaxSetCookieSecure = [ + 'header_WarnSameSiteStrictCrossDowngradeLaxSetCookieSecure', + 'body_1_WarnSameSiteStrictCrossDowngradeLaxSetCookieSecure', + 'body_2_WarnSameSiteStrictCrossDowngradeLaxSetCookieSecure', +]; // Filename: SameSiteWarnCrossDowngradeSet.md -export const WarnSameSiteStrictCrossDowngradeLaxSetCookieInsecure = ` -

Migrate entirely to HTTPS to continue allowing cookies to be set by same-site subresources

-

A cookie is being set by an insecure origin in a secure context. Because this cookie is being set across schemes on the same site, it will be blocked in a future version of Chrome. This behavior enhances the SameSite attribute's protection of user data from request forgery by network attackers.

-

Resolve this issue by migrating your site (as defined by the eTLD+1) entirely to HTTPS. It is also recommended to mark the cookie with the Secure attribute if that is not already the case.

-
-`; +export const WarnSameSiteStrictCrossDowngradeLaxSetCookieInsecure = [ + 'header_WarnSameSiteStrictCrossDowngradeLaxSetCookieInsecure', + 'body_1_WarnSameSiteStrictCrossDowngradeLaxSetCookieInsecure', + 'body_2_WarnSameSiteStrictCrossDowngradeLaxSetCookieInsecure', +]; // Filename: SameSiteWarnCrossDowngradeRead.md -export const WarnSameSiteLaxCrossDowngradeStrictReadCookieSecure = ` -

Migrate entirely to HTTPS to continue having cookies sent to same-site subresources

-

A cookie is being sent to a secure origin from an insecure context. Because this cookie is being sent across schemes on the same site, it will not be sent in a future version of Chrome. This behavior enhances the SameSite attribute's protection of user data from request forgery by network attackers.

-

Resolve this issue by migrating your site (as defined by the eTLD+1) entirely to HTTPS. It is also recommended to mark the cookie with the Secure attribute if that is not already the case.

-
-`; +export const WarnSameSiteLaxCrossDowngradeStrictReadCookieSecure = [ + 'header_WarnSameSiteLaxCrossDowngradeStrictReadCookieSecure', + 'body_1_WarnSameSiteLaxCrossDowngradeStrictReadCookieSecure', + 'body_2_WarnSameSiteLaxCrossDowngradeStrictReadCookieSecure', +]; // Filename: SameSiteWarnCrossDowngradeRead.md -export const WarnSameSiteLaxCrossDowngradeStrictReadCookieInsecure = ` -

Migrate entirely to HTTPS to continue having cookies sent to same-site subresources

-

A cookie is being sent to an insecure origin from a secure context. Because this cookie is being sent across schemes on the same site, it will not be sent in a future version of Chrome. This behavior enhances the SameSite attribute's protection of user data from request forgery by network attackers.

-

Resolve this issue by migrating your site (as defined by the eTLD+1) entirely to HTTPS. It is also recommended to mark the cookie with the Secure attribute if that is not already the case.

-
-`; +export const WarnSameSiteLaxCrossDowngradeStrictReadCookieInsecure = [ + 'header_WarnSameSiteLaxCrossDowngradeStrictReadCookieInsecure', + 'body_1_WarnSameSiteLaxCrossDowngradeStrictReadCookieInsecure', + 'body_2_WarnSameSiteLaxCrossDowngradeStrictReadCookieInsecure', +]; // Filename: SameSiteWarnCrossDowngradeSet.md -export const WarnSameSiteLaxCrossDowngradeStrictSetCookieSecure = ` -

Migrate entirely to HTTPS to continue allowing cookies to be set by same-site subresources

-

A cookie is being set by a secure origin in an insecure context. Because this cookie is being set across schemes on the same site, it will be blocked in a future version of Chrome. This behavior enhances the SameSite attribute's protection of user data from request forgery by network attackers.

-

Resolve this issue by migrating your site (as defined by the eTLD+1) entirely to HTTPS. It is also recommended to mark the cookie with the Secure attribute if that is not already the case.

-
-`; +export const WarnSameSiteLaxCrossDowngradeStrictSetCookieSecure = [ + 'header_WarnSameSiteLaxCrossDowngradeStrictSetCookieSecure', + 'body_1_WarnSameSiteLaxCrossDowngradeStrictSetCookieSecure', + 'body_2_WarnSameSiteLaxCrossDowngradeStrictSetCookieSecure', +]; // Filename: SameSiteWarnCrossDowngradeSet.md -export const WarnSameSiteLaxCrossDowngradeStrictSetCookieInsecure = ` -

Migrate entirely to HTTPS to continue allowing cookies to be set by same-site subresources

-

A cookie is being set by an insecure origin in a secure context. Because this cookie is being set across schemes on the same site, it will be blocked in a future version of Chrome. This behavior enhances the SameSite attribute's protection of user data from request forgery by network attackers.

-

Resolve this issue by migrating your site (as defined by the eTLD+1) entirely to HTTPS. It is also recommended to mark the cookie with the Secure attribute if that is not already the case.

-
-`; +export const WarnSameSiteLaxCrossDowngradeStrictSetCookieInsecure = [ + 'header_WarnSameSiteLaxCrossDowngradeStrictSetCookieInsecure', + 'body_1_WarnSameSiteLaxCrossDowngradeStrictSetCookieInsecure', + 'body_2_WarnSameSiteLaxCrossDowngradeStrictSetCookieInsecure', +]; // Filename: SameSiteWarnCrossDowngradeRead.md -export const WarnSameSiteLaxCrossDowngradeLaxReadCookieSecure = ` -

Migrate entirely to HTTPS to continue having cookies sent to same-site subresources

-

A cookie is being sent to a secure origin from an insecure context. Because this cookie is being sent across schemes on the same site, it will not be sent in a future version of Chrome. This behavior enhances the SameSite attribute's protection of user data from request forgery by network attackers.

-

Resolve this issue by migrating your site (as defined by the eTLD+1) entirely to HTTPS. It is also recommended to mark the cookie with the Secure attribute if that is not already the case.

-
-`; +export const WarnSameSiteLaxCrossDowngradeLaxReadCookieSecure = [ + 'header_WarnSameSiteLaxCrossDowngradeLaxReadCookieSecure', + 'body_1_WarnSameSiteLaxCrossDowngradeLaxReadCookieSecure', + 'body_2_WarnSameSiteLaxCrossDowngradeLaxReadCookieSecure', +]; // Filename: SameSiteWarnCrossDowngradeRead.md -export const WarnSameSiteLaxCrossDowngradeLaxReadCookieInsecure = ` -

Migrate entirely to HTTPS to continue having cookies sent to same-site subresources

-

A cookie is being sent to an insecure origin from a secure context. Because this cookie is being sent across schemes on the same site, it will not be sent in a future version of Chrome. This behavior enhances the SameSite attribute's protection of user data from request forgery by network attackers.

-

Resolve this issue by migrating your site (as defined by the eTLD+1) entirely to HTTPS. It is also recommended to mark the cookie with the Secure attribute if that is not already the case.

-
-`; +export const WarnSameSiteLaxCrossDowngradeLaxReadCookieInsecure = [ + 'header_WarnSameSiteLaxCrossDowngradeLaxReadCookieInsecure', + 'body_1_WarnSameSiteLaxCrossDowngradeLaxReadCookieInsecure', + 'body_2_WarnSameSiteLaxCrossDowngradeLaxReadCookieInsecure', +]; // Filename: SameSiteWarnCrossDowngradeSet.md -export const WarnSameSiteLaxCrossDowngradeLaxSetCookieSecure = ` -

Migrate entirely to HTTPS to continue allowing cookies to be set by same-site subresources

-

A cookie is being set by a secure origin in an insecure context. Because this cookie is being set across schemes on the same site, it will be blocked in a future version of Chrome. This behavior enhances the SameSite attribute's protection of user data from request forgery by network attackers.

-

Resolve this issue by migrating your site (as defined by the eTLD+1) entirely to HTTPS. It is also recommended to mark the cookie with the Secure attribute if that is not already the case.

-
-`; +export const WarnSameSiteLaxCrossDowngradeLaxSetCookieSecure = [ + 'header_WarnSameSiteLaxCrossDowngradeLaxSetCookieSecure', + 'body_1_WarnSameSiteLaxCrossDowngradeLaxSetCookieSecure', + 'body_2_WarnSameSiteLaxCrossDowngradeLaxSetCookieSecure', +]; // Filename: SameSiteWarnCrossDowngradeSet.md -export const WarnSameSiteLaxCrossDowngradeLaxSetCookieInsecure = ` -

Migrate entirely to HTTPS to continue allowing cookies to be set by same-site subresources

-

A cookie is being set by an insecure origin in a secure context. Because this cookie is being set across schemes on the same site, it will be blocked in a future version of Chrome. This behavior enhances the SameSite attribute's protection of user data from request forgery by network attackers.

-

Resolve this issue by migrating your site (as defined by the eTLD+1) entirely to HTTPS. It is also recommended to mark the cookie with the Secure attribute if that is not already the case.

-
-`; +export const WarnSameSiteLaxCrossDowngradeLaxSetCookieInsecure = [ + 'header_WarnSameSiteLaxCrossDowngradeLaxSetCookieInsecure', + 'body_1_WarnSameSiteLaxCrossDowngradeLaxSetCookieInsecure', + 'body_2_WarnSameSiteLaxCrossDowngradeLaxSetCookieInsecure', +]; // Filename: CookieAttributeValueExceedsMaxSize.md -export const WarnAttributeValueExceedsMaxSize = ` -

Ensure cookie attribute values don't exceed 1024 characters

-

Cookie attribute values exceeding 1024 characters in size will result in the attribute being ignored. This could lead to unexpected behavior since the cookie will be processed as if the offending attribute / attribute value pair were not present.

-

Resolve this issue by ensuring that cookie attribute values don't exceed 1024 characters.

-
-`; +export const WarnAttributeValueExceedsMaxSize = [ + 'header_WarnAttributeValueExceedsMaxSize', + 'body_1_WarnAttributeValueExceedsMaxSize', + 'body_2_WarnAttributeValueExceedsMaxSize', +]; // Filename: cookieWarnDomainNonAscii.md -export const WarnDomainNonASCII = ` -

Ensure cookie Domain attribute values only contain ASCII characters

-

Domain attributes in cookies are restricted to the ASCII character set. Any cookies that contain characters outside of the ASCII range in their Domain attribute will be ignored in the future.

-

To resolve this issue, you need to remove all non-ASCII characters from the Domain attribute of the affected cookies.

-

If your site has an internationalized domain name (IDN), you should use punycode representation for the Domain attribute instead.

-
-`; +export const WarnDomainNonASCII = [ + 'header_WarnDomainNonASCII', + 'body_1_WarnDomainNonASCII', + 'body_2_WarnDomainNonASCII', + 'body_3_WarnDomainNonASCII', +]; // Filename: cookieWarnThirdPartyPhaseoutRead.md -export const WarnThirdPartyPhaseoutReadCookie = ` -

Cookie sent in cross-site context will be blocked in future Chrome versions

-

In a future version of the browser, cookies marked with SameSite=None; Secure and not Partitioned will be blocked in cross-site requests. This behavior protects user data from cross-site tracking.

-
-`; +export const WarnThirdPartyPhaseoutReadCookie = [ + 'header_WarnThirdPartyPhaseoutReadCookie', + 'body_WarnThirdPartyPhaseoutReadCookie', +]; // Filename: cookieWarnThirdPartyPhaseoutSet.md -export const WarnThirdPartyPhaseoutSetCookie = ` -

Cookie set in cross-site context will be blocked in future Chrome versions

-

In a future version of the browser, cookies marked with SameSite=None; Secure and not Partitioned will be blocked in cross-site context. This behavior protects user data from cross-site tracking.

-
-`; +export const WarnThirdPartyPhaseoutSetCookie = [ + 'header_WarnThirdPartyPhaseoutSetCookie', + 'body_WarnThirdPartyPhaseoutSetCookie', +]; // Filename: cookieCrossSiteRedirectDowngrade.md -export const WarnCrossSiteRedirectDowngradeChangesInclusion = ` -

Cookie is blocked due to a cross-site redirect chain

-

The cookie was blocked because the URL redirect chain was not fully same-site, meaning the final request was treated as a cross-site request. Like other cross-site requests, this blocks cookies with SameSite=Lax or SameSite=Strict.

-

For example: If site A redirects to site B which then redirects back to site A, the final request to site A will be a cross-site request.

-
-`; +export const WarnCrossSiteRedirectDowngradeChangesInclusion = [ + 'header_WarnCrossSiteRedirectDowngradeChangesInclusion', + 'body_1_WarnCrossSiteRedirectDowngradeChangesInclusion', + 'body_2_WarnCrossSiteRedirectDowngradeChangesInclusion', +]; diff --git a/packages/common/src/data/cookieExemptionReason/exemptionReasons.ts b/packages/common/src/data/cookieExemptionReason/exemptionReasons.ts index a53e27ad9..27a7f0d34 100644 --- a/packages/common/src/data/cookieExemptionReason/exemptionReasons.ts +++ b/packages/common/src/data/cookieExemptionReason/exemptionReasons.ts @@ -13,20 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import { I18n } from '@google-psat/i18n'; + //For source see https://source.chromium.org/chromium/chromium/src/+/main:third_party/devtools-frontend/src/front_end/core/sdk/NetworkRequest.ts const CookieExemptionReason = { - UserSetting: 'This cookie is allowed by user preference.', - TPCDMetadata: - 'This cookie is allowed by a third-party cookie deprecation trial grace period.', - TPCDDeprecationTrial: - 'This cookie is allowed by third-party cookie phaseout deprecation trial.', - TPCDHeuristics: - 'This cookie is allowed by third-party cookie phaseout heuristics.', - EnterprisePolicy: 'This cookie is allowed by Chrome Enterprise policy.', - StorageAccessAPI: 'This cookie is allowed by the Storage Access API.', - TopLevelStorageAccessAPI: - 'This cookie is allowed by the top-level Storage Access API.', - CorsOptIn: 'This cookie is allowed by CORS opt-in', + UserSetting: () => I18n.getMessage('exemptionReasonUserSetting'), + TPCDMetadata: () => I18n.getMessage('exemptionReasonTPCDMetadata'), + TPCDDeprecationTrial: () => + I18n.getMessage('exemptionReasonTPCDDeprecationTrial'), + TPCDHeuristics: () => I18n.getMessage('exemptionReasonTPCDHeuristics'), + EnterprisePolicy: () => I18n.getMessage('exemptionReasonEnterprisePolicy'), + StorageAccessAPI: () => I18n.getMessage('exemptionReasonStorageAccessAPI'), + TopLevelStorageAccessAPI: () => + I18n.getMessage('exemptionReasonTopLevelStorageAccessAPI'), + CorsOptIn: () => I18n.getMessage('exemptionReasonCorsOptIn'), }; export default CookieExemptionReason; diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index 3bae5d459..a5182684a 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -36,9 +36,13 @@ export { default as getDevToolWorker } from './worker/devToolWorker'; export { default as executeTaskInDevToolWorker } from './worker/executeTaskInDevToolWorker'; export { default as getValueByKey } from './utils/getValueByKey'; export * from './utils/contextSelector'; -export { default as addUTMParams } from './utils/addUTMParams'; +export { default as addUTMParams, type MediumType } from './utils/addUTMParams'; +export { default as delay } from './utils/delay'; +export { default as mergeDeep } from './utils/mergeDeep'; +export { default as resolveWithTimeout } from './utils/resolveWithTimeout'; +export { default as deriveBlockingStatus } from './utils/deriveBlockingStatus'; +export { default as getLegendDescription } from './utils/getLegendDescription'; export * from './worker/enums'; export * from './utils/generateReports'; export * from './cookies.types'; export * from './libraryDetection.types'; -export * from './constants'; diff --git a/packages/common/src/libraryDetection.types.ts b/packages/common/src/libraryDetection.types.ts index 10f4157ce..10fe74881 100644 --- a/packages/common/src/libraryDetection.types.ts +++ b/packages/common/src/libraryDetection.types.ts @@ -39,3 +39,39 @@ export type LibraryData = { domQuerymatches?: [string] | null; }; }; + +export type ScriptTagUnderCheck = { + origin: string | null; + content: string; + type?: string; +}; + +export type DetectionFunctions = ( + arg0: ScriptTagUnderCheck, + arg1: DetectedSignature[], + arg2: number, + arg3?: number +) => { + signatureMatches: number; + matches: DetectedSignature[]; + moduleMatch?: number; +}; + +export type LibraryMatchers = { + name?: string; + component: React.FC; + signatures?: { + strongMatches: { + signature: string; + helpUrl: string; + }[]; + weakMatches: { + signature: string; + helpUrl: string; + }[]; + }; + domainsToSkip?: string[]; + helpUrl: string; + detectionFunction?: DetectionFunctions; + domQueryFunction?: (externalDocument?: Document | null) => string[]; +}; diff --git a/packages/common/src/utils/addUTMParams.ts b/packages/common/src/utils/addUTMParams.ts index 0cc0fbe30..17d4b9d59 100644 --- a/packages/common/src/utils/addUTMParams.ts +++ b/packages/common/src/utils/addUTMParams.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -type MediumType = 'extension' | 'cli'; +export type MediumType = 'extension' | 'cli'; const addUTMParams = ( url: string, @@ -24,12 +24,22 @@ const addUTMParams = ( return url; } + let calculatedMedium = medium; + if ( + //@ts-ignore + !globalThis?.WorkerNavigator && + !globalThis?.chrome?.devtools?.inspectedWindow?.tabId + ) { + //@ts-ignore + calculatedMedium = globalThis?.PSAT_DATA?.source ?? 'cli'; + } + const urlParts = url.split('#'); const base = urlParts[0]; const hash = urlParts[1] ? `#${urlParts[1]}` : ''; const separator = base.includes('?') ? '&' : '?'; - return `${base}${separator}utm_source=psat&utm_medium=${medium}${hash}`; + return `${base}${separator}utm_source=psat&utm_medium=${calculatedMedium}${hash}`; }; export default addUTMParams; diff --git a/packages/cli/src/utils/delay.ts b/packages/common/src/utils/delay.ts similarity index 100% rename from packages/cli/src/utils/delay.ts rename to packages/common/src/utils/delay.ts diff --git a/packages/extension/src/store/utils/deriveBlockingStatus.ts b/packages/common/src/utils/deriveBlockingStatus.ts similarity index 92% rename from packages/extension/src/store/utils/deriveBlockingStatus.ts rename to packages/common/src/utils/deriveBlockingStatus.ts index edc45b5b7..265fac854 100644 --- a/packages/extension/src/store/utils/deriveBlockingStatus.ts +++ b/packages/common/src/utils/deriveBlockingStatus.ts @@ -13,16 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - /** - * External dependencies. + * Internal dependencies. */ import { - type CookieData, - type requestEvent, - type responsEvent, BLOCK_STATUS, -} from '@ps-analysis-tool/common'; + CookieData, + responseEvent, + requestEvent, +} from '../cookies.types'; /** * Derives if a cookie was blocked in response header. @@ -32,7 +31,7 @@ import { * 'null' - The cookie wa blocked in atleast one or all of the response headers * 'true' - The cookie was blocked in all of the response headers. */ -function deriveInboundBlocking(respEvents: responsEvent[]): BLOCK_STATUS { +function deriveInboundBlocking(respEvents: responseEvent[]): BLOCK_STATUS { // if there are not response events the cookie must be stored in a previous visit // Or there is a javascript not used in the request header. if (respEvents.length === 0) { @@ -83,11 +82,10 @@ function deriveOutboundBlocking(reqEvents: requestEvent[]): BLOCK_STATUS { } /** - * * @param networkEvents Cookie network events * @returns blocking status object */ -export function deriveBlockingStatus( +export default function deriveBlockingStatus( networkEvents: CookieData['networkEvents'] ): CookieData['blockingStatus'] { if (!networkEvents) { diff --git a/packages/common/src/utils/filterCookiesByFrame.ts b/packages/common/src/utils/filterCookiesByFrame.ts index 458b15731..ec4a66d18 100644 --- a/packages/common/src/utils/filterCookiesByFrame.ts +++ b/packages/common/src/utils/filterCookiesByFrame.ts @@ -18,7 +18,7 @@ */ import { TabCookies } from '../cookies.types'; interface TabFrames { - [key: string]: { frameIds: number[] }; + [key: string]: { frameIds: string[] }; } const filterCookiesByFrame = ( @@ -31,65 +31,11 @@ const filterCookiesByFrame = ( return Object.values(frameFilteredCookies); } - let tabFramesIDMap: number[] = []; - - Object.keys(tabFrames)?.forEach((url) => { - const frameIds = tabFrames[url].frameIds; - - if (frameIds) { - tabFramesIDMap = [...new Set([...frameIds, ...tabFramesIDMap])]; - } - }); - Object.entries(cookies).forEach(([key, cookie]) => { tabFrames[frameUrl].frameIds?.forEach((frameId) => { - if (cookie.frameIdList?.includes(frameId)) { + if (frameId && cookie.frameIdList?.includes(frameId)) { frameFilteredCookies[key] = cookie; } - - //For orphaned/unmapped cookies - let hasFrame = false; - cookie.frameIdList?.forEach((cookieFrameId) => { - if (tabFramesIDMap.includes(cookieFrameId as number)) { - hasFrame = true; - } - }); - - if (!hasFrame && cookie?.frameIdList) { - //For UnMapped Cookies - if ( - cookie.frameIdList && - cookie.frameIdList.length === 0 && - cookie.parsedCookie.domain - ) { - const domainToCheck = cookie.parsedCookie.domain.startsWith('.') - ? cookie.parsedCookie.domain.slice(1) - : cookie.parsedCookie.domain; - - if (frameUrl.includes(domainToCheck)) { - frameFilteredCookies[key] = cookie; - } - if ( - Object.keys(tabFrames).filter((frameKey) => - frameKey.includes(domainToCheck) - ).length === 0 && - tabFrames[frameUrl].frameIds.includes(0) - ) { - frameFilteredCookies[key] = cookie; - } - } - - //For Orphaned Cookies - if ( - cookie.frameIdList && - cookie.frameIdList.length > 0 && - cookie.parsedCookie.domain - ) { - if (tabFrames[frameUrl].frameIds.includes(0)) { - frameFilteredCookies[key] = cookie; - } - } - } }); }); diff --git a/packages/common/src/utils/findAnalyticsMatch.ts b/packages/common/src/utils/findAnalyticsMatch.ts index 1be1f19cf..0f8cbc229 100644 --- a/packages/common/src/utils/findAnalyticsMatch.ts +++ b/packages/common/src/utils/findAnalyticsMatch.ts @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - /** * Internal dependencies. */ diff --git a/packages/common/src/utils/generateReports/generateAllCookiesCSV.ts b/packages/common/src/utils/generateReports/generateAllCookiesCSV.ts index 59bbae22c..e4039e9c3 100644 --- a/packages/common/src/utils/generateReports/generateAllCookiesCSV.ts +++ b/packages/common/src/utils/generateReports/generateAllCookiesCSV.ts @@ -18,6 +18,7 @@ * External dependencies */ import sanitizeCsvRecord from '../sanitizeCsvRecord'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies */ @@ -28,20 +29,20 @@ import { import calculateEffectiveExpiryDate from '../calculateEffectiveExpiryDate'; export const COOKIES_DATA_HEADER = [ - 'Name', - 'Scope', - 'Domain', - 'Partition Key', - 'Same Site', - 'Category', - 'Platform', - 'Http Only', - 'Secure', - 'Value', - 'Path', - 'Expires', - 'Issues', - 'GDPRPortal', + () => I18n.getMessage('name'), + () => I18n.getMessage('scope'), + () => I18n.getMessage('domain'), + () => I18n.getMessage('partitionKey'), + () => I18n.getMessage('sameSite'), + () => I18n.getMessage('category'), + () => I18n.getMessage('platform'), + () => I18n.getMessage('httpOnly'), + () => I18n.getMessage('secure'), + () => I18n.getMessage('value'), + () => I18n.getMessage('path'), + () => I18n.getMessage('expires'), + () => I18n.getMessage('issues'), + () => I18n.getMessage('gDPR'), ]; const generateAllCookiesCSV = (siteAnalysisData: CompleteJson): string => { @@ -62,25 +63,37 @@ const generateAllCookiesCSV = (siteAnalysisData: CompleteJson): string => { //This should be in the same order as cookieDataHeader const recordsArray = [ cookie.parsedCookie.name, - cookie.isFirstParty ? 'First Party' : 'Third Party', + cookie.isFirstParty + ? I18n.getMessage('firstParty') + : I18n.getMessage('thirdParty'), cookie.parsedCookie.domain || ' ', cookie.parsedCookie.partitionKey || ' ', cookie.parsedCookie.sameSite, - cookie.analytics.category, + I18n.getMessage( + cookie.analytics?.category?.toLowerCase() || 'uncategorized' + ), cookie.analytics.platform, - cookie.parsedCookie.httpOnly ? 'Yes' : 'No', - cookie.parsedCookie.secure ? 'Yes' : 'No', + cookie.parsedCookie.httpOnly + ? I18n.getMessage('yes') + : I18n.getMessage('no'), + cookie.parsedCookie.secure + ? I18n.getMessage('yes') + : I18n.getMessage('no'), cookie.parsedCookie.value, cookie.parsedCookie.path, calculateEffectiveExpiryDate(cookie.parsedCookie.expires), - cookie.isBlocked ? 'Yes' : 'No', + cookie.isBlocked ? I18n.getMessage('yes') : I18n.getMessage('no'), cookie.analytics.GDPR || 'NA', ].map(sanitizeCsvRecord); cookieRecords += recordsArray.join(',') + '\r\n'; } - return COOKIES_DATA_HEADER.join(',') + '\r\n' + cookieRecords; + return ( + COOKIES_DATA_HEADER.map((header) => header()).join(',') + + '\r\n' + + cookieRecords + ); }; export default generateAllCookiesCSV; diff --git a/packages/common/src/utils/generateReports/generateCookiesWithIssuesCSV.ts b/packages/common/src/utils/generateReports/generateCookiesWithIssuesCSV.ts index a1ec91685..908aed99c 100644 --- a/packages/common/src/utils/generateReports/generateCookiesWithIssuesCSV.ts +++ b/packages/common/src/utils/generateReports/generateCookiesWithIssuesCSV.ts @@ -13,7 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +/** + * External dependencies. + */ +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies */ @@ -22,19 +25,19 @@ import calculateEffectiveExpiryDate from '../calculateEffectiveExpiryDate'; import sanitizeCsvRecord from '../sanitizeCsvRecord'; export const COOKIES_WITH_ISSUES_DATA_HEADERS = [ - 'Name', - 'Scope', - 'Domain', - 'Partition Key', - 'Same Site', - 'Category', - 'Platform', - 'Http Only', - 'Secure', - 'Value', - 'Path', - 'Expires', - 'GDPRPortal', + () => I18n.getMessage('name'), + () => I18n.getMessage('scope'), + () => I18n.getMessage('domain'), + () => I18n.getMessage('partitionKey'), + () => I18n.getMessage('sameSite'), + () => I18n.getMessage('category'), + () => I18n.getMessage('platform'), + () => I18n.getMessage('httpOnly'), + () => I18n.getMessage('secure'), + () => I18n.getMessage('value'), + () => I18n.getMessage('path'), + () => I18n.getMessage('expires'), + () => I18n.getMessage('gDPR'), ]; const generateCookiesWithIssuesCSV = ( @@ -59,14 +62,22 @@ const generateCookiesWithIssuesCSV = ( //This should be in the same order as cookieDataHeader const recordsArray = [ cookie.parsedCookie.name, - cookie.isFirstParty ? 'First Party' : 'Third Party', + cookie.isFirstParty + ? I18n.getMessage('firstParty') + : I18n.getMessage('thirdParty'), cookie.parsedCookie.domain || ' ', cookie.parsedCookie.partitionKey || ' ', cookie.parsedCookie.sameSite, - cookie.analytics.category, + I18n.getMessage( + cookie.analytics?.category?.toLowerCase() || 'uncategorized' + ), cookie.analytics.platform, - cookie.parsedCookie.httpOnly ? 'Yes' : 'No', - cookie.parsedCookie.secure ? 'Yes' : 'No', + cookie.parsedCookie.httpOnly + ? I18n.getMessage('yes') + : I18n.getMessage('no'), + cookie.parsedCookie.secure + ? I18n.getMessage('yes') + : I18n.getMessage('no'), cookie.parsedCookie.value, cookie.parsedCookie.path, calculateEffectiveExpiryDate(cookie.parsedCookie.expires), @@ -76,7 +87,11 @@ const generateCookiesWithIssuesCSV = ( cookieRecords += recordsArray.join(',') + '\r\n'; } - return COOKIES_WITH_ISSUES_DATA_HEADERS.join(',') + '\r\n' + cookieRecords; + return ( + COOKIES_WITH_ISSUES_DATA_HEADERS.map((header) => header()).join(',') + + '\r\n' + + cookieRecords + ); }; export default generateCookiesWithIssuesCSV; diff --git a/packages/common/src/utils/generateReports/generateSummaryDataCSV.ts b/packages/common/src/utils/generateReports/generateSummaryDataCSV.ts index 218bbba32..6ddf4ea3d 100644 --- a/packages/common/src/utils/generateReports/generateSummaryDataCSV.ts +++ b/packages/common/src/utils/generateReports/generateSummaryDataCSV.ts @@ -13,7 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +/** + * External dependencies. + */ +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies */ @@ -85,18 +88,20 @@ const generateSummaryDataCSV = (siteAnalysisData: CompleteJson): string => { } const summary = { - 'Total Cookies': cookieMap.size, - 'Total First Party Cookies': totalFirstPartyCookies, - 'Total Third Party Cookies': totalThirdPartyCookies, - 'Analytics Cookies': analyticsCookies, - 'Functional Cookies': functionalCookies, - 'Marketing Cookies': marketingCookies, - 'Uncategorized Cookies': uncategorisedCookies, - 'Cookies With Issues': cookiesWithIssues, - 'Analytics Cookies With Issues': analyticsCookiesWithIssues, - 'Functional Cookies With Issues': functionalCookiesWithIssues, - 'Marketing Cookies With Issues': marketingCookiesWithIssues, - 'Uncategorized Cookies With Issues': uncategorisedCookiesWithIssues, + [I18n.getMessage('totalCookies')]: cookieMap.size, + [I18n.getMessage('totalFirstPartyCookies')]: totalFirstPartyCookies, + [I18n.getMessage('totalThirdPartyCookies')]: totalThirdPartyCookies, + [I18n.getMessage('analyticsCookies')]: analyticsCookies, + [I18n.getMessage('functionalCookies')]: functionalCookies, + [I18n.getMessage('marketingCookies')]: marketingCookies, + [I18n.getMessage('uncategorizedCookies')]: uncategorisedCookies, + [I18n.getMessage('cookiesWithIssues')]: cookiesWithIssues, + [I18n.getMessage('analyticsCookiesWithIssues')]: analyticsCookiesWithIssues, + [I18n.getMessage('functionalCookiesWithIssues')]: + functionalCookiesWithIssues, + [I18n.getMessage('marketingCookiesWithIssues')]: marketingCookiesWithIssues, + [I18n.getMessage('uncategorizedCookiesWithIssues')]: + uncategorisedCookiesWithIssues, }; const CSVString = Object.entries(summary).reduce( diff --git a/packages/common/src/utils/generateReports/generateTechnologyCSV.ts b/packages/common/src/utils/generateReports/generateTechnologyCSV.ts index cc48bdbfb..f8fd486f1 100644 --- a/packages/common/src/utils/generateReports/generateTechnologyCSV.ts +++ b/packages/common/src/utils/generateReports/generateTechnologyCSV.ts @@ -13,7 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +/** + * External dependencies + */ +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies */ @@ -21,11 +24,11 @@ import sanitizeCsvRecord from '../sanitizeCsvRecord'; import { CompleteJson } from '../../cookies.types'; const TECHNOLOGIES_DATA_HEADER = [ - 'Name', - 'Description', - 'Confidence', - 'Website', - 'Categories', + () => I18n.getMessage('name'), + () => I18n.getMessage('description'), + () => I18n.getMessage('confidence'), + () => I18n.getMessage('website'), + () => I18n.getMessage('categories'), ]; const generateTechnologyCSV = (siteAnalysisData: CompleteJson): string => { @@ -52,7 +55,11 @@ const generateTechnologyCSV = (siteAnalysisData: CompleteJson): string => { technologyRecords += recordsArray.join(',') + '\r\n'; } - return TECHNOLOGIES_DATA_HEADER.join(',') + '\r\n' + technologyRecords; + return ( + TECHNOLOGIES_DATA_HEADER.map((header) => header()).join(',') + + '\r\n' + + technologyRecords + ); }; export default generateTechnologyCSV; diff --git a/packages/common/src/utils/generateReports/tests/generateSummaryData.ts b/packages/common/src/utils/generateReports/tests/generateSummaryData.ts index 8c49c3356..3ae3370d9 100644 --- a/packages/common/src/utils/generateReports/tests/generateSummaryData.ts +++ b/packages/common/src/utils/generateReports/tests/generateSummaryData.ts @@ -13,6 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/** + * External dependencies + */ +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies @@ -21,9 +25,54 @@ import generateSummaryDataCSV from '../generateSummaryDataCSV'; import { mockData1 } from './data.mock'; describe('generateSummaryDataCSV', () => { + beforeAll(() => { + globalThis.chrome.i18n = null; + + I18n.initMessages({ + totalCookies: { + message: 'Total Cookies', + }, + totalFirstPartyCookies: { + message: 'Total First Party Cookies', + }, + totalThirdPartyCookies: { + message: 'Total Third Party Cookies', + }, + analyticsCookies: { + message: 'Analytics Cookies', + }, + functionalCookies: { + message: 'Functional Cookies', + }, + marketingCookies: { + message: 'Marketing Cookies', + }, + uncategorizedCookies: { + message: 'Uncategorised Cookies', + }, + cookiesWithIssues: { + message: 'Cookies With Issues', + }, + analyticsCookiesWithIssues: { + message: 'Analytics Cookies With Issues', + }, + functionalCookiesWithIssues: { + message: 'Functional Cookies With Issues', + }, + marketingCookiesWithIssues: { + message: 'Marketing Cookies With Issues', + }, + uncategorisedCookiesWithIssues: { + message: 'Uncategorised Cookies With Issues', + }, + }); + }); + it('should create CSV string for summary data', () => { const CSVString = generateSummaryDataCSV(mockData1); + console.log(CSVString); + expect(CSVString.split('\r\n').filter((str) => str).length).toBe(12); }); diff --git a/packages/common/src/utils/getLegendDescription.ts b/packages/common/src/utils/getLegendDescription.ts new file mode 100644 index 000000000..2255046ab --- /dev/null +++ b/packages/common/src/utils/getLegendDescription.ts @@ -0,0 +1,29 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import { I18n } from '@google-psat/i18n'; + +const getLegendDescription = (legendDescription: string | string[]) => { + return typeof legendDescription === 'string' + ? legendDescription.includes(' ') + ? legendDescription + : I18n.getMessage(legendDescription) + : I18n.getFormattedMessages(legendDescription); +}; + +export default getLegendDescription; diff --git a/packages/common/src/utils/mergeDeep.ts b/packages/common/src/utils/mergeDeep.ts new file mode 100644 index 000000000..a015a63f8 --- /dev/null +++ b/packages/common/src/utils/mergeDeep.ts @@ -0,0 +1,36 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const isObject = (item: any) => { + return item && typeof item === 'object' && !Array.isArray(item); +}; + +const mergeDeep = (target: any, source: any) => { + if (isObject(target) && isObject(source)) { + for (const key in source) { + if (isObject(source[key])) { + if (!target[key]) { + Object.assign(target, { [key]: {} }); + } + mergeDeep(target[key], source[key]); + } else { + Object.assign(target, { [key]: source[key] }); + } + } + } + return target; +}; + +export default mergeDeep; diff --git a/packages/common/src/utils/parseRequestWillBeSentExtraInfo.ts b/packages/common/src/utils/parseRequestWillBeSentExtraInfo.ts index a599b60be..e3ddbd989 100644 --- a/packages/common/src/utils/parseRequestWillBeSentExtraInfo.ts +++ b/packages/common/src/utils/parseRequestWillBeSentExtraInfo.ts @@ -17,6 +17,7 @@ * External dependencies */ import type { Protocol } from 'devtools-protocol'; + /** * Internal dependencies. */ @@ -33,16 +34,18 @@ import isFirstParty from './isFirstParty'; * Parses Network.requestWillBeSentExtraInfo to get extra information about a cookie. * @param {object} associatedCookies Cookies associated with the request being parsed. * @param {object} cookieDB Cookie database to find analytics from. - * @param {object} requestMap An object for requestId to url. + * @param {string} requestUrl The request url. * @param {string} tabUrl - The top-level URL (URL in the tab's address bar). + * @param {string[]} frameIds The request to which the frame is associated to. * @param {string} requestId - The requestId of the request being processed * @returns {object} parsed cookies. */ export default function parseRequestWillBeSentExtraInfo( associatedCookies: Protocol.Network.RequestWillBeSentExtraInfoEvent['associatedCookies'], cookieDB: CookieDatabase, - requestMap: { [requestId: string]: string }, + requestUrl: string, tabUrl: string, + frameIds: string[], requestId: string ) { const cookies: CookieData[] = []; @@ -52,17 +55,12 @@ export default function parseRequestWillBeSentExtraInfo( cookie.expires ); - let domain, - url = ''; - - if (requestMap && requestMap[requestId]) { - url = requestMap[requestId] ?? ''; - } + let domain; if (cookie?.domain) { domain = cookie?.domain; - } else if (!cookie?.domain && url) { - domain = new URL(url).hostname; + } else if (!cookie?.domain && requestUrl) { + domain = new URL(requestUrl).hostname; } const singleCookie: CookieData = { @@ -72,13 +70,18 @@ export default function parseRequestWillBeSentExtraInfo( expires: effectiveExpirationDate, samesite: cookie.sameSite?.toLowerCase() ?? '', domain, + partitionKey: + cookie?.partitionKey && typeof cookie?.partitionKey === 'string' + ? cookie?.partitionKey + : //@ts-ignore This is to handle both stable and canary version of Chrome. + cookie?.partitionKey?.topLevelSite, }, networkEvents: { requestEvents: [ { type: REQUEST_EVENT.CDP_REQUEST_WILL_BE_SENT_EXTRA_INFO, requestId, - url: url, + url: requestUrl, blocked: blockedReasons.length !== 0, timeStamp: Date.now(), }, @@ -86,14 +89,22 @@ export default function parseRequestWillBeSentExtraInfo( responseEvents: [], }, blockedReasons, - analytics: cookieDB ? findAnalyticsMatch(cookie.name, cookieDB) : null, // In case CDP gets cookie first. - url, + analytics: cookieDB ? findAnalyticsMatch(cookie.name, cookieDB) : null, + url: requestUrl, headerType: 'request' as CookieData['headerType'], isFirstParty: isFirstParty(domain, tabUrl), frameIdList: [], - exemptionReason: exemptionReason ? exemptionReason : undefined, + exemptionReason: + exemptionReason && exemptionReason !== 'None' + ? exemptionReason + : undefined, }; + //Sometimes frameId comes empty so it shows data in other frames where cookie should not be shown. + if (frameIds.length > 0) { + singleCookie.frameIdList = [...frameIds]; + } + cookies.push(singleCookie); }); diff --git a/packages/common/src/utils/parseResponseReceivedExtraInfo.ts b/packages/common/src/utils/parseResponseReceivedExtraInfo.ts index a43d6d706..d55318c82 100644 --- a/packages/common/src/utils/parseResponseReceivedExtraInfo.ts +++ b/packages/common/src/utils/parseResponseReceivedExtraInfo.ts @@ -37,9 +37,10 @@ import isFirstParty from './isFirstParty'; * @param {object} blockedCookies Blocked Cookies associated with the response being parsed. * @param {object} exemptedCookies Blocked Cookies associated with the response being parsed. * @param {string|undefined} cookiePartitionKey Partittion key for the response. - * @param {object} requestMap An object for requestId to url. + * @param {string} requestUrl The associated request URL. * @param {string} tabUrl - The top-level URL (URL in the tab's address bar). * @param {object} cookieDB Cookie database to find analytics from. + * @param {string[]} frameIds - The frameId the following cookies are associated to. * @param {string} requestId - The requestId of the request being processed * @returns {object} parsed cookies. */ @@ -48,9 +49,10 @@ export default function parseResponseReceivedExtraInfo( blockedCookies: Protocol.Network.ResponseReceivedExtraInfoEvent['blockedCookies'], exemptedCookies: Protocol.Network.ResponseReceivedExtraInfoEvent['exemptedCookies'], cookiePartitionKey: Protocol.Network.ResponseReceivedExtraInfoEvent['cookiePartitionKey'], - requestMap: { [requestId: string]: string }, + requestUrl: string, tabUrl: string, cookieDB: CookieDatabase, + frameIds: string[], requestId: string ) { const cookies: CookieData[] = []; @@ -82,21 +84,20 @@ export default function parseResponseReceivedExtraInfo( if (headerLine.toLowerCase().includes('partitioned')) { parsedCookie = { ...parsedCookie, - partitionKey: cookiePartitionKey, + partitionKey: + cookiePartitionKey && typeof cookiePartitionKey === 'string' + ? cookiePartitionKey + : //@ts-ignore This is to handle both stable and canary version of Chrome. + cookiePartitionKey?.topLevelSite, }; } - let domain, - url = ''; - - if (requestMap && requestMap[requestId]) { - url = requestMap[requestId] ?? ''; - } + let domain; if (parsedCookie?.domain) { domain = parsedCookie?.domain; - } else if (!parsedCookie?.domain && url) { - domain = new URL(url).hostname; + } else if (!parsedCookie?.domain && requestUrl) { + domain = new URL(requestUrl).hostname; } const singleCookie: CookieData = { @@ -113,8 +114,8 @@ export default function parseResponseReceivedExtraInfo( responseEvents: [ { type: RESPONSE_EVENT.CDP_RESPONSE_RECEIVED_EXTRA_INFO, - requestId, - url: url, + requestId: requestId, + url: requestUrl, blocked: blockedCookie ? true : false, timeStamp: Date.now(), }, @@ -123,13 +124,22 @@ export default function parseResponseReceivedExtraInfo( analytics: cookieDB ? findAnalyticsMatch(parsedCookie.name, cookieDB) : null, - url, + url: requestUrl, isFirstParty: isFirstParty(domain, tabUrl), headerType: 'response' as CookieData['headerType'], frameIdList: [], - exemptionReason: exemptedCookie?.exemptionReason, + exemptionReason: + exemptedCookie?.exemptionReason && + exemptedCookie?.exemptionReason !== 'None' + ? exemptedCookie?.exemptionReason + : undefined, }; + //Sometimes frameId comes empty so it shows data in other frames where cookie should not be shown. + if (frameIds.length > 0) { + singleCookie.frameIdList = [...frameIds]; + } + cookies.push(singleCookie); }); diff --git a/packages/common/src/utils/resolveWithTimeout.ts b/packages/common/src/utils/resolveWithTimeout.ts new file mode 100644 index 000000000..c9d59d1a6 --- /dev/null +++ b/packages/common/src/utils/resolveWithTimeout.ts @@ -0,0 +1,40 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Time a promise. + * @param {Promise} promise - A promise that need to be timed. + * @param resolveInto + * @param {number} timeout - Time for the timeout in milliseconds. + * @returns {Promose} - The parsed URL object or null if the URL is invalid. + */ +export default function resolveWithTimeout( + promise: Promise, + resolveInto: T, + timeout: number +): Promise { + return new Promise((resolve, reject) => { + // Create a timeout promise that resolves after the specified time + const timeoutPromise = new Promise((resolveTimeout) => { + setTimeout(() => { + resolveTimeout(resolveInto); + }, timeout); + }); + + // Use Promise.race to resolve with the first promise that completes + Promise.race([promise, timeoutPromise]).then(resolve).catch(reject); + }); +} diff --git a/packages/common/src/utils/tests/addUTMParams.ts b/packages/common/src/utils/tests/addUTMParams.ts index 9f61b8aca..e2ab01114 100644 --- a/packages/common/src/utils/tests/addUTMParams.ts +++ b/packages/common/src/utils/tests/addUTMParams.ts @@ -21,7 +21,7 @@ import addUTMParams from '../addUTMParams'; describe('addUTMParams', () => { it('should add UTM parameters with default medium', () => { expect(addUTMParams('http://example.com')).toBe( - 'http://example.com?utm_source=psat&utm_medium=extension' + 'http://example.com?utm_source=psat&utm_medium=cli' ); }); diff --git a/packages/extension/src/store/tests/deriveBlockingStatus.ts b/packages/common/src/utils/tests/deriveBlockingStatus.ts similarity index 94% rename from packages/extension/src/store/tests/deriveBlockingStatus.ts rename to packages/common/src/utils/tests/deriveBlockingStatus.ts index 4206b0572..8cdc07a6f 100644 --- a/packages/extension/src/store/tests/deriveBlockingStatus.ts +++ b/packages/common/src/utils/tests/deriveBlockingStatus.ts @@ -15,22 +15,18 @@ */ /** - * External dependencies. + * Internal dependencies. */ import { + BLOCK_STATUS, RESPONSE_EVENT, REQUEST_EVENT, - type responsEvent, - type requestEvent, - BLOCK_STATUS, -} from '@ps-analysis-tool/common'; - -/** - * Internal dependencies. - */ -import { deriveBlockingStatus } from '../utils/deriveBlockingStatus'; + responseEvent, + requestEvent, +} from '../../cookies.types'; +import deriveBlockingStatus from '../deriveBlockingStatus'; -const mockRespArray: responsEvent[] = [ +const mockRespArray: responseEvent[] = [ { type: RESPONSE_EVENT.CDP_RESPONSE_RECEIVED_EXTRA_INFO, blocked: false, diff --git a/packages/common/src/utils/tests/mergeDeep.ts b/packages/common/src/utils/tests/mergeDeep.ts new file mode 100644 index 000000000..6ee3e006f --- /dev/null +++ b/packages/common/src/utils/tests/mergeDeep.ts @@ -0,0 +1,77 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import mergeDeep from '../mergeDeep'; + +describe('mergeDeep', () => { + it('merges two objects deeply', () => { + const target = { + a: 1, + b: 2, + c: { + d: 3, + e: 4, + }, + }; + const source = { + a: 2, + c: { + e: 5, + f: 6, + }, + }; + const result = { + a: 2, + b: 2, + c: { + d: 3, + e: 5, + f: 6, + }, + }; + + expect(mergeDeep(target, source)).toEqual(result); + }); + + it('merges two objects deeply with nested arrays', () => { + const target = { + a: 1, + b: 2, + c: { + d: 3, + e: 4, + f: [1, 2, 3], + }, + }; + const source = { + a: 2, + c: { + e: 5, + f: [4, 5, 6], + }, + }; + const result = { + a: 2, + b: 2, + c: { + d: 3, + e: 5, + f: [4, 5, 6], + }, + }; + + expect(mergeDeep(target, source)).toEqual(result); + }); +}); diff --git a/packages/common/src/utils/tests/resolveWithTimeout.ts b/packages/common/src/utils/tests/resolveWithTimeout.ts new file mode 100644 index 000000000..79ea4f13f --- /dev/null +++ b/packages/common/src/utils/tests/resolveWithTimeout.ts @@ -0,0 +1,51 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Internal dependencies. + */ +import resolveWithTimeout from '../resolveWithTimeout'; + +describe('resolveWithTimeout', () => { + it('should resolve Promise before the timeout', async () => { + const PROMISE_RESOLVE_TIME = 1000; + const TIMEOUT = 2000; + + const myPromise = new Promise((resolve) => { + setTimeout(() => { + resolve('Promise Resolved'); + }, PROMISE_RESOLVE_TIME); + }); + + const result = await resolveWithTimeout(myPromise, null, TIMEOUT); + + expect(result).toBe('Promise Resolved'); + }); + + it('should not resolve Promise after the timeout', async () => { + const PROMISE_RESOLVE_TIME = 2000; + const TIMEOUT = 1000; + + const myPromise = new Promise((resolve) => { + setTimeout(() => { + resolve('Promise Resolved'); + }, PROMISE_RESOLVE_TIME); + }); + + const result = await resolveWithTimeout(myPromise, null, TIMEOUT); + + expect(result).not.toBe('Promise Resolved'); + }); +}); diff --git a/packages/common/tsconfig.json b/packages/common/tsconfig.json index 6ca0c6b71..ac50a7bbd 100644 --- a/packages/common/tsconfig.json +++ b/packages/common/tsconfig.json @@ -12,5 +12,6 @@ "esModuleInterop": true, "moduleResolution": "node" }, - "exclude": ["**/tests/**/*.ts", "dist/**", "dist-types/**"] + "exclude": ["**/tests/**/*.ts", "dist/**", "dist-types/**"], + "references": [{ "path": "../i18n" }] } diff --git a/packages/design-system/.npmignore b/packages/design-system/.npmignore new file mode 100644 index 000000000..9c3741a56 --- /dev/null +++ b/packages/design-system/.npmignore @@ -0,0 +1,2 @@ +__tests__ +src/ \ No newline at end of file diff --git a/packages/design-system/package.json b/packages/design-system/package.json index df157ec22..587fe6475 100644 --- a/packages/design-system/package.json +++ b/packages/design-system/package.json @@ -1,9 +1,10 @@ { - "name": "@ps-analysis-tool/design-system", - "version": "0.8.0", + "name": "@google-psat/design-system", + "version": "0.9.0-2", "description": "A package for presentational components that are being used in multiple packages", - "main": "./src/index.ts", - "source":"./src/index.ts", + "main": "dist/index.js", + "types": "dist-types/index.d.ts", + "source": "src/index.ts", "customExports": { ".": { "default": "./src/index.ts" @@ -11,20 +12,32 @@ }, "scripts": { "build": "tsc", - "dev": "tsc-watch" + "dev": "tsc-watch", + "build:remove": "rimraf dist dist-types tsconfig.tsbuildinfo", + "publish:local": "npm publish --registry=http://localhost:4873", + "publish:remote": "npm publish --access=public --registry=https://registry.npmjs.org", + "unpublish:local": "npm unpublish --registry=http://localhost:4873 --force", + "unpublish:remote": "npm unpublish --registry=https://registry.npmjs.org --force" }, "repository": { "type": "git", - "url": "git+https://github.com/GoogleChromeLabs/ps-analysis-tool" + "url": "https://github.com/GoogleChromeLabs/ps-analysis-tool", + "directory": "packages/design-system" + }, + "publishConfig": { + "access": "public" + }, + "author": { + "name": "Google" }, - "author": "", "license": "Apache-2.0", "bugs": { "url": "https://github.com/GoogleChromeLabs/ps-analysis-tool/issues" }, "homepage": "https://github.com/GoogleChromeLabs/ps-analysis-tool", "dependencies": { - "@ps-analysis-tool/common": "*", + "@google-psat/common": "*", + "@google-psat/i18n": "*", "p-queue": "^7.3.4", "use-context-selector": "^1.4.1" }, diff --git a/packages/design-system/src/components/bulletList/index.tsx b/packages/design-system/src/components/bulletList/index.tsx index 0c6368089..d8b4b1658 100644 --- a/packages/design-system/src/components/bulletList/index.tsx +++ b/packages/design-system/src/components/bulletList/index.tsx @@ -18,6 +18,7 @@ * External dependencies. */ import React from 'react'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. @@ -42,7 +43,9 @@ const BulletList = ({ rows, heading }: BulletListProps) => { )}
{rows.length === 0 ? ( -

No News

+

+ {I18n.getMessage('noNews')} +

) : ( <> {rows.map((rowItem) => ( diff --git a/packages/design-system/src/components/circlePieChart/index.tsx b/packages/design-system/src/components/circlePieChart/index.tsx index 43107ca6c..403617cf2 100644 --- a/packages/design-system/src/components/circlePieChart/index.tsx +++ b/packages/design-system/src/components/circlePieChart/index.tsx @@ -24,7 +24,6 @@ import classNames from 'classnames'; * Internal dependencies. */ import EmptyCirclePieChart from './emptyCirclePieChart'; -import { InfoIcon } from '@ps-analysis-tool/design-system'; interface CirclePieChartProps { centerCount: number; @@ -42,7 +41,6 @@ const CirclePieChart = ({ centerCount, data, title, - infoIconClassName = '', centerTitleExtraClasses = '', pieChartExtraClasses = '', }: CirclePieChartProps) => { @@ -80,14 +78,6 @@ const CirclePieChart = ({

{title}

- {title === '3rd Party Cookies' && ( -

- -

- )}
)} diff --git a/packages/design-system/src/components/cookieDetails/details.tsx b/packages/design-system/src/components/cookieDetails/details.tsx index 8087c99ce..7e62b505a 100644 --- a/packages/design-system/src/components/cookieDetails/details.tsx +++ b/packages/design-system/src/components/cookieDetails/details.tsx @@ -17,7 +17,7 @@ /** * External dependencies. */ -import React, { useState } from 'react'; +import React, { useMemo, useState } from 'react'; import classNames from 'classnames'; import { BLOCK_STATUS, @@ -25,7 +25,8 @@ import { cookieExemptionReason, cookieIssueDetails, type CookieTableData, -} from '@ps-analysis-tool/common'; +} from '@google-psat/common'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. @@ -55,10 +56,22 @@ const Details = ({ selectedCookie, isUsingCDP }: DetailsProps) => { const hasValidBlockedReason = selectedCookie.blockedReasons && selectedCookie.blockedReasons.length !== 0; + const selectedCookieExemptionReason = + selectedCookie?.exemptionReason ?? 'None'; + selectedCookie?.blockedReasons?.forEach((reason) => { const cookieExclusionReason = + // @ts-ignore cookieIssueDetails.CookieExclusionReason[reason]; - const cookieBlockedReason = cookieIssueDetails.CookieBlockedReason[reason]; + const cookieBlockedReason = + //@ts-ignore + typeof cookieIssueDetails.CookieBlockedReason[reason] === 'string' + ? //@ts-ignore + cookieIssueDetails.CookieBlockedReason[reason] + : I18n.getFormattedMessages( + // @ts-ignore + cookieIssueDetails.CookieBlockedReason[reason] + ); if (cookieBlockedReason) { blockedReasons = blockedReasons + cookieBlockedReason; @@ -87,46 +100,56 @@ const Details = ({ selectedCookie, isUsingCDP }: DetailsProps) => { return reason; }); + const canBeDecoded = useMemo(() => { + let errorOnDecode = true; + + try { + decodeURIComponent(selectedCookie.parsedCookie.value); + } catch (error) { + errorOnDecode = false; + } + + return errorOnDecode; + }, [selectedCookie.parsedCookie.value]); + return (
- {selectedCookie.exemptionReason && - selectedCookie.exemptionReason.toLowerCase() !== 'none' && ( + {selectedCookieExemptionReason && + selectedCookieExemptionReason.toLowerCase() !== 'none' && (

- Exemption Reason + {I18n.getMessage('exemptionReason')}

- { - cookieExemptionReason[ - selectedCookie.exemptionReason as CookieData['exemptionReason'] - ] - } + {cookieExemptionReason[ + //@ts-ignore + selectedCookieExemptionReason as CookieData['exemptionReason'] + ]()}

)} {selectedCookie.isDomainInAllowList && (

- Allow Listed + {I18n.getMessage('allowListed')}

- The cookie domain was added to the allow-list for this session, - however the browser may still block these cookies for various - reasons, such as invalid attributes. You can check the allowed - domains under chrome://settings/content/siteData. + {I18n.getMessage('allowListedNote', [ + 'chrome://settings/content/siteData', + ])}

)} {hasValidBlockedReason && isUsingCDP && ( - <> +

- Blocked Reason + {I18n.getMessage('blockedReason')}

- +

)} {selectedCookie?.blockingStatus?.inboundBlock === @@ -137,7 +160,7 @@ const Details = ({ selectedCookie, isUsingCDP }: DetailsProps) => {

- This cookie was blocked in at least one of the responses. + {I18n.getMessage('blockedInAtLeastOne', ['responses'])}


@@ -151,7 +174,7 @@ const Details = ({ selectedCookie, isUsingCDP }: DetailsProps) => {

- This cookie was blocked in all responses. + {I18n.getMessage('blockedInAll', ['responses'])}


@@ -165,7 +188,7 @@ const Details = ({ selectedCookie, isUsingCDP }: DetailsProps) => {

- This cookie was blocked in at least one of the requests. + {I18n.getMessage('blockedInAtLeastOne', ['requests'])}


@@ -179,7 +202,7 @@ const Details = ({ selectedCookie, isUsingCDP }: DetailsProps) => {

- This cookie was blocked in all requests. + {I18n.getMessage('blockedInAll', ['requests'])}


@@ -194,7 +217,7 @@ const Details = ({ selectedCookie, isUsingCDP }: DetailsProps) => {

- This cookie was blocked in all of the requests and responses. + {I18n.getMessage('blockedInAllRequestResponse')}


@@ -209,8 +232,7 @@ const Details = ({ selectedCookie, isUsingCDP }: DetailsProps) => {

- This cookie was blocked in at least one of the requests and at - least one of the responses. + {I18n.getMessage('blockedInSomeRequestResponse')}


@@ -225,8 +247,10 @@ const Details = ({ selectedCookie, isUsingCDP }: DetailsProps) => {

- This cookie was blocked in all requests and at least one of the - responses. + {I18n.getMessage('blockedinSomeAndAll', [ + 'requests', + 'responses', + ])}


@@ -241,8 +265,10 @@ const Details = ({ selectedCookie, isUsingCDP }: DetailsProps) => {

- This cookie was blocked in at least one of the requests and all of - the responses. + {I18n.getMessage('blockedinSomeAndAll', [ + 'responses', + 'requests', + ])}


@@ -250,18 +276,19 @@ const Details = ({ selectedCookie, isUsingCDP }: DetailsProps) => { {selectedCookie?.warningReasons && selectedCookie?.warningReasons?.length > 0 && ( - <> +

- Warnings + {I18n.getMessage('warnings')}

- +

)} +

- Cookie Value + {I18n.getMessage('cookieValue')}

- {showUrlDecoded + {showUrlDecoded && canBeDecoded ? decodeURIComponent(selectedCookie.parsedCookie.value) : selectedCookie.parsedCookie.value}

- Description + {I18n.getMessage('description')}

- {selectedCookie.analytics?.description || 'No description available.'} + {I18n.getMessage( + selectedCookie.analytics?.description || 'noDescription' + )}

); diff --git a/packages/design-system/src/components/cookieDetails/index.tsx b/packages/design-system/src/components/cookieDetails/index.tsx index 36b7bbce1..178e51a4f 100644 --- a/packages/design-system/src/components/cookieDetails/index.tsx +++ b/packages/design-system/src/components/cookieDetails/index.tsx @@ -18,14 +18,15 @@ * External dependencies. */ import React from 'react'; +import type { CookieTableData } from '@google-psat/common'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. */ -import type { CookieTableData } from '@ps-analysis-tool/common'; import Details from './details'; -interface CookieDetailsProps { +export interface CookieDetailsProps { isUsingCDP: boolean; selectedFrameCookie: { [frame: string]: CookieTableData | null; @@ -48,7 +49,7 @@ const CookieDetails = ({ ) : (

- Select cookies to preview its value + {I18n.getMessage('selectCookie')}

)} diff --git a/packages/design-system/src/components/cookieDetails/stories/cookieDetails.stories.ts b/packages/design-system/src/components/cookieDetails/stories/cookieDetails.stories.ts index e8a5a23ff..4d54d6049 100644 --- a/packages/design-system/src/components/cookieDetails/stories/cookieDetails.stories.ts +++ b/packages/design-system/src/components/cookieDetails/stories/cookieDetails.stories.ts @@ -17,6 +17,7 @@ * External dependencies. */ import type { Meta, StoryObj } from '@storybook/react'; +import { CookieTableData } from '@google-psat/common'; /** * Internal dependencies. @@ -34,5 +35,15 @@ export default meta; type Story = StoryObj; export const Primary: Story = { - args: {}, + args: { + selectedFrameCookie: { + testFrame: { + parsedCookie: { + name: 'test', + value: 'testVal', + }, + } as CookieTableData, + }, + isUsingCDP: false, + }, }; diff --git a/packages/design-system/src/components/cookieDetails/stories/details.stories.ts b/packages/design-system/src/components/cookieDetails/stories/details.stories.ts index c3998384a..eb9e0a583 100644 --- a/packages/design-system/src/components/cookieDetails/stories/details.stories.ts +++ b/packages/design-system/src/components/cookieDetails/stories/details.stories.ts @@ -17,7 +17,7 @@ * External dependencies. */ import type { Meta, StoryObj } from '@storybook/react'; -import type { CookieTableData } from '@ps-analysis-tool/common'; +import type { CookieTableData } from '@google-psat/common'; /** * Internal dependencies. diff --git a/packages/design-system/src/components/cookieTable/index.tsx b/packages/design-system/src/components/cookieTable/index.tsx index 92465b00f..b38890296 100644 --- a/packages/design-system/src/components/cookieTable/index.tsx +++ b/packages/design-system/src/components/cookieTable/index.tsx @@ -24,7 +24,7 @@ import React, { useMemo, useReducer, } from 'react'; -import { CookieTableData, getCookieKey } from '@ps-analysis-tool/common'; +import { CookieTableData, getCookieKey } from '@google-psat/common'; /** * Internal dependencies. @@ -56,6 +56,8 @@ interface CookieTableProps { row: TableRow ) => void; hideExport?: boolean; + isCLI?: boolean; + hostname: string; } const CookieTable = forwardRef< @@ -78,6 +80,8 @@ const CookieTable = forwardRef< extraInterfaceToTopBar, onRowContextMenu, hideExport, + isCLI = false, + hostname, }: CookieTableProps, ref ) { @@ -170,9 +174,9 @@ const CookieTable = forwardRef< ); useEffect(() => { - window.addEventListener('resize', () => forceUpdate()); + globalThis?.addEventListener('resize', () => forceUpdate()); return () => { - window.removeEventListener('resize', () => forceUpdate()); + globalThis?.removeEventListener('resize', () => forceUpdate()); }; }, []); @@ -189,7 +193,13 @@ const CookieTable = forwardRef< onRowContextMenu={onRowContextMenuHandler} getRowObjectKey={getRowObjectKey} conditionalTableRowClassesHandler={_conditionalTableRowClassesHandler} - exportTableData={!hideExport ? exportCookies : undefined} + exportTableData={ + !hideExport + ? (rows: TableRow[]) => { + exportCookies(isCLI, rows, hostname); + } + : undefined + } hasVerticalBar={hasVerticalBar} isRowSelected={isRowSelected} > diff --git a/packages/design-system/src/components/cookieTable/tests/index.tsx b/packages/design-system/src/components/cookieTable/tests/index.tsx index 808609d49..bfc10bf25 100644 --- a/packages/design-system/src/components/cookieTable/tests/index.tsx +++ b/packages/design-system/src/components/cookieTable/tests/index.tsx @@ -20,7 +20,8 @@ import React, { act } from 'react'; import '@testing-library/jest-dom'; import { fireEvent, render, screen, waitFor } from '@testing-library/react'; -import { CookieTableData } from '@ps-analysis-tool/common'; +import { CookieTableData } from '@google-psat/common'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. @@ -120,9 +121,32 @@ describe('CookieTable', () => { }, } as CookieTableData, }, + hostname: '', setSelectedFrameCookie: jest.fn(), }; + beforeAll(() => { + globalThis.chrome.i18n = null; + + I18n.initMessages({ + openFilterOptions: { + message: 'Open filter options', + }, + clearAll: { + message: 'Clear all', + }, + search: { + message: 'Search', + }, + toggleAll: { + message: 'Toggle All', + }, + clearSearch: { + message: 'Clear Search', + }, + }); + }); + it('should render the table', async () => { render(); diff --git a/packages/design-system/src/components/cookieTable/utils/conditionalTableRowClassesHandler.ts b/packages/design-system/src/components/cookieTable/utils/conditionalTableRowClassesHandler.ts index 71821b47d..573939c0e 100644 --- a/packages/design-system/src/components/cookieTable/utils/conditionalTableRowClassesHandler.ts +++ b/packages/design-system/src/components/cookieTable/utils/conditionalTableRowClassesHandler.ts @@ -18,7 +18,7 @@ import { BLOCK_STATUS, CookieTableData, getCookieKey, -} from '@ps-analysis-tool/common'; +} from '@google-psat/common'; import classnames from 'classnames'; import { TableRow } from '../../table'; diff --git a/packages/design-system/src/components/cookieTable/utils/exportCookies.ts b/packages/design-system/src/components/cookieTable/utils/exportCookies.ts index 2c88458b8..944bd9943 100644 --- a/packages/design-system/src/components/cookieTable/utils/exportCookies.ts +++ b/packages/design-system/src/components/cookieTable/utils/exportCookies.ts @@ -17,20 +17,29 @@ /** * External dependencies */ -import { CookieTableData } from '@ps-analysis-tool/common'; +import { CookieTableData } from '@google-psat/common'; import { saveAs } from 'file-saver'; /** * Internal dependencies */ import { TableRow } from '../../table'; -import { generateCookieTableCSV } from '../../table/utils'; +import { + generateCLICookieTableCSV, + generateExtensionCookieTableCSV, +} from '../../table/utils'; -const exportCookies = (rows: TableRow[]) => { +const exportCookies = (isCLI = false, rows: TableRow[], hostname: string) => { const _cookies = rows.map(({ originalData }) => originalData); + + const generateCookieTableCSV = isCLI + ? generateCLICookieTableCSV + : generateExtensionCookieTableCSV; + if (_cookies.length > 0 && 'parsedCookie' in _cookies[0]) { const csvTextBlob = generateCookieTableCSV(_cookies as CookieTableData[]); - saveAs(csvTextBlob, 'Cookies Report.csv'); + const fileName = hostname.split('.').join('-'); + saveAs(csvTextBlob, `${fileName}-report.csv`); } }; diff --git a/packages/design-system/src/components/cookiesLanding/cookiesLandingWrapper.tsx b/packages/design-system/src/components/cookiesLanding/cookiesLandingWrapper.tsx index 631662f19..d08847de9 100644 --- a/packages/design-system/src/components/cookiesLanding/cookiesLandingWrapper.tsx +++ b/packages/design-system/src/components/cookiesLanding/cookiesLandingWrapper.tsx @@ -17,11 +17,11 @@ * External dependencies. */ import React from 'react'; - +import { type DataMapping } from '@google-psat/common'; /** * Internal dependencies. */ -import LandingHeader, { type DataMapping } from './landingHeader'; +import LandingHeader from './landingHeader'; import { InfoIcon } from '../../icons'; export interface CookiesLandingWrapperProps { diff --git a/packages/design-system/src/components/cookiesLanding/cookiesMatrix/index.tsx b/packages/design-system/src/components/cookiesLanding/cookiesMatrix/index.tsx index 88247388c..9aaa1c844 100644 --- a/packages/design-system/src/components/cookiesLanding/cookiesMatrix/index.tsx +++ b/packages/design-system/src/components/cookiesLanding/cookiesMatrix/index.tsx @@ -22,7 +22,9 @@ import { type TabFrames, Legend, filterFramesWithCookies, -} from '@ps-analysis-tool/common'; + getLegendDescription, +} from '@google-psat/common'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies */ @@ -52,7 +54,7 @@ const CookiesMatrix = ({ tabCookies, tabFrames, componentData = [], - title = 'Categories', + title = I18n.getMessage('categories'), description = '', showHorizontalMatrix = true, showMatrix = true, @@ -67,12 +69,12 @@ const CookiesMatrix = ({ const dataComponents: MatrixComponentProps[] = []; componentData.forEach((component) => { - const legendDescription = LEGEND_DESCRIPTION[component.label] || ''; + const legendDescription = + LEGEND_DESCRIPTION[component.descriptionKey || component.label] || ''; dataComponents.push({ ...component, - description: legendDescription, + description: getLegendDescription(legendDescription), title: component.label, - containerClasses: '', }); }); @@ -83,14 +85,15 @@ const CookiesMatrix = ({ ? matrixHorizontalData : [ { - title: 'Number of Frames', - description: 'Number of unique frames found across the page(s).', + title: I18n.getMessage('numberOfFrames'), + description: I18n.getMessage('numberOfFramesNote'), count: totalFrames, }, { - title: 'Number of Frames with Associated Cookies', - description: - 'Unique frames across the page(s) that have cookies associated with them.', + title: I18n.getMessage('numberOfFramesWithAssociatedCookies'), + description: I18n.getMessage( + 'numberOfFramesWithAssociatedCookiesNote' + ), count: associatedCookiesCount ? associatedCookiesCount : framesWithCookies diff --git a/packages/design-system/src/components/cookiesLanding/cookiesMatrix/stories/cookiesMatrix.stories.tsx b/packages/design-system/src/components/cookiesLanding/cookiesMatrix/stories/cookiesMatrix.stories.tsx index d301b0265..7b26c917f 100644 --- a/packages/design-system/src/components/cookiesLanding/cookiesMatrix/stories/cookiesMatrix.stories.tsx +++ b/packages/design-system/src/components/cookiesLanding/cookiesMatrix/stories/cookiesMatrix.stories.tsx @@ -65,10 +65,10 @@ export const Primary: StoryObj = { cookiesStatsComponents: cookiesStatsComponents, tabFrames: { 'https://example.com': { - frameIds: [0, 0], + frameIds: ['0', '0'], }, 'https://www.google.com': { - frameIds: [6169, 6172, 6169, 6172], + frameIds: ['6169', '6172', '6169', '6172'], }, }, }, diff --git a/packages/design-system/src/components/cookiesLanding/landingHeader/index.tsx b/packages/design-system/src/components/cookiesLanding/landingHeader/index.tsx index d09fa7ee2..3e747e8e7 100644 --- a/packages/design-system/src/components/cookiesLanding/landingHeader/index.tsx +++ b/packages/design-system/src/components/cookiesLanding/landingHeader/index.tsx @@ -17,10 +17,13 @@ * External dependencies. */ import React from 'react'; -import { CirclePieChart } from '@ps-analysis-tool/design-system'; -import { DataMapping } from '@ps-analysis-tool/common'; +import { DataMapping } from '@google-psat/common'; import classnames from 'classnames'; +/** + * Internal depenencies. + */ +import CirclePieChart from '../../circlePieChart'; interface LandingHeaderProps { dataMapping?: DataMapping[]; } diff --git a/packages/design-system/src/components/cookiesLanding/landingHeader/stories/LandingHeader.stories.tsx b/packages/design-system/src/components/cookiesLanding/landingHeader/stories/LandingHeader.stories.tsx index 1fffa1c72..9eee1af33 100644 --- a/packages/design-system/src/components/cookiesLanding/landingHeader/stories/LandingHeader.stories.tsx +++ b/packages/design-system/src/components/cookiesLanding/landingHeader/stories/LandingHeader.stories.tsx @@ -45,11 +45,28 @@ const meta: Meta = { component: LandingHeader, }; +const dataMapping = [ + { + title: 'Total Cookies', + count: cookieStats.total, + data: [...cookiesStatsComponents.legend], + }, + { + title: 'First Party Cookies', + count: cookieStats.firstParty.total, + data: [...cookiesStatsComponents.firstParty], + }, + { + title: 'Third Party Cookies', + count: cookieStats.thirdParty.total, + data: [...cookiesStatsComponents.thirdParty], + }, +]; + export default meta; export const Primary: StoryObj = { args: { - cookieStats, - cookiesStatsComponents, + dataMapping, }, }; diff --git a/packages/design-system/src/components/cookiesLanding/tests/useFiltersMapping.tsx b/packages/design-system/src/components/cookiesLanding/tests/useFiltersMapping.tsx index e78b3c582..72b58e3cd 100644 --- a/packages/design-system/src/components/cookiesLanding/tests/useFiltersMapping.tsx +++ b/packages/design-system/src/components/cookiesLanding/tests/useFiltersMapping.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { TabFrames } from '@ps-analysis-tool/common'; +import { TabFrames } from '@google-psat/common'; import { renderHook } from '@testing-library/react'; import useFiltersMapping from '../useFiltersMapping'; import { useSidebar } from '../../sidebar/useSidebar'; diff --git a/packages/design-system/src/components/cookiesLanding/useFiltersMapping.tsx b/packages/design-system/src/components/cookiesLanding/useFiltersMapping.tsx index 0dfc8aad9..80da8dd2b 100644 --- a/packages/design-system/src/components/cookiesLanding/useFiltersMapping.tsx +++ b/packages/design-system/src/components/cookiesLanding/useFiltersMapping.tsx @@ -18,8 +18,11 @@ * External dependencies. */ import { useCallback, useMemo } from 'react'; -import { useSidebar } from '@ps-analysis-tool/design-system'; -import type { TabFrames } from '@ps-analysis-tool/common'; +import type { TabFrames } from '@google-psat/common'; +/** + * Internal dependencies. + */ +import { useSidebar } from '../sidebar'; /** * Hook to expose the selectedItemUpdater function. diff --git a/packages/design-system/src/components/errorFallback/extensionReloadNotification.tsx b/packages/design-system/src/components/errorFallback/extensionReloadNotification.tsx index d5bcf19d9..e783cc17c 100644 --- a/packages/design-system/src/components/errorFallback/extensionReloadNotification.tsx +++ b/packages/design-system/src/components/errorFallback/extensionReloadNotification.tsx @@ -24,20 +24,23 @@ import React from 'react'; import Button from '../button'; interface ExtensionReloadNotificationProps { tabId?: number; + texts: { + displayText: string; + buttonText: string; + }; } const ExtensionReloadNotification = ({ tabId, + texts, }: ExtensionReloadNotificationProps) => { return (
-

- Looks like extension has been updated since devtool was open. -

+

{texts.displayText}

diff --git a/packages/design-system/src/components/errorFallback/index.tsx b/packages/design-system/src/components/errorFallback/index.tsx index 9df07c58e..fd1f21408 100644 --- a/packages/design-system/src/components/errorFallback/index.tsx +++ b/packages/design-system/src/components/errorFallback/index.tsx @@ -17,6 +17,7 @@ * External dependencies. */ import React from 'react'; +import { I18n } from '@google-psat/i18n'; interface ErrorFallbackProps { error: Record; @@ -27,10 +28,10 @@ const ErrorFallback = ({ error }: ErrorFallbackProps) => { return (
-

Something went wrong!

-

Please try opening this page in a new tab.

+

{I18n.getMessage('wentWrong')}

+

{I18n.getMessage('openInNewTab')}

-        {error?.message} Please try opening this page in a new tab.
+        {error?.message} {I18n.getMessage('openInNewTab')}
       
); diff --git a/packages/design-system/src/components/index.ts b/packages/design-system/src/components/index.ts index 97c832540..6bdbdb7a8 100644 --- a/packages/design-system/src/components/index.ts +++ b/packages/design-system/src/components/index.ts @@ -28,7 +28,6 @@ export { default as MatrixComponentHorizontal } from './matrix/matrixComponent/m export type { MatrixComponentProps } from './matrix/matrixComponent'; export { default as CookiesLanding } from './cookiesLanding'; export * from './cookiesLanding'; -export type { DataMapping } from './cookiesLanding/landingHeader'; export { default as CookiesMatrix } from './cookiesLanding/cookiesMatrix'; export { default as CookieDetails } from './cookieDetails'; export { default as Details } from './cookieDetails/details'; diff --git a/packages/design-system/src/components/inspectButton/index.tsx b/packages/design-system/src/components/inspectButton/index.tsx index ae33c2db3..d4f3c73c8 100644 --- a/packages/design-system/src/components/inspectButton/index.tsx +++ b/packages/design-system/src/components/inspectButton/index.tsx @@ -17,6 +17,7 @@ * External dependencies. */ import React from 'react'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. @@ -38,7 +39,7 @@ const InspectButton = ({ return ( { setIsInspecting(!isInspecting); }} diff --git a/packages/design-system/src/components/landingPage/constants.ts b/packages/design-system/src/components/landingPage/constants.ts index 550396e02..d70b94883 100644 --- a/packages/design-system/src/components/landingPage/constants.ts +++ b/packages/design-system/src/components/landingPage/constants.ts @@ -16,27 +16,27 @@ /** * External dependencies. */ -import { addUTMParams } from '@ps-analysis-tool/common'; +import { addUTMParams } from '@google-psat/common'; export const QUICK_LINKS = [ { - title: 'Learn more about PSAT', + title: 'learnMore', link: 'https://github.com/GoogleChromeLabs/ps-analysis-tool/wiki', }, { - title: 'Join the discussions', + title: 'joinDiscussions', link: 'https://github.com/GoogleChromeLabs/ps-analysis-tool/discussions', }, { - title: 'Report a bug', + title: 'reportBug', link: 'https://github.com/GoogleChromeLabs/ps-analysis-tool/issues/new?assignees=&labels=&projects=&template=bug_report.md&title=', }, { - title: 'Report a breakage', + title: 'reportBreakage', link: 'https://goo.gle/report-3pc-broken', }, { - title: 'Request Additional Migration Time', + title: 'requestMigrationTime', link: addUTMParams( 'https://developer.chrome.com/origintrials/#/view_trial/3315212275698106369' ), diff --git a/packages/design-system/src/components/landingPage/contentPanel.tsx b/packages/design-system/src/components/landingPage/contentPanel.tsx index 30f0e5300..dc8f8f5f4 100644 --- a/packages/design-system/src/components/landingPage/contentPanel.tsx +++ b/packages/design-system/src/components/landingPage/contentPanel.tsx @@ -18,7 +18,7 @@ * External dependencies. */ import React from 'react'; -import { addUTMParams } from '@ps-analysis-tool/common'; +import { addUTMParams } from '@google-psat/common'; interface ContentPanelProps { title: string; diff --git a/packages/design-system/src/components/landingPage/infoCard/fetchPSInfo.ts b/packages/design-system/src/components/landingPage/infoCard/fetchPSInfo.ts index b80a09770..d0625c6a5 100644 --- a/packages/design-system/src/components/landingPage/infoCard/fetchPSInfo.ts +++ b/packages/design-system/src/components/landingPage/infoCard/fetchPSInfo.ts @@ -16,11 +16,12 @@ /** * External dependencies. */ -import { fetchLocalData } from '@ps-analysis-tool/common'; +import { fetchLocalData } from '@google-psat/common'; export enum PSInfoKey { 'Topics' = 'topics', 'AttributionReporting' = 'attribution-reporting', + 'ProtectedAudience' = 'protected-audience', 'BounceTracking' = 'bounce-tracking', 'UserAgentReduction' = 'user-agent-reduction', 'RelatedWebsiteSets' = 'related-website-sets', @@ -32,7 +33,9 @@ export type PSInfoKeyType = (typeof PSInfoKey)[keyof typeof PSInfoKey]; export type PSInfo = { name: string; description: string; + useI18n: boolean; proposal: string; + publicExplainer: string; publicDiscussion: string; videoOverview: string; devDocumentation: string; diff --git a/packages/design-system/src/components/landingPage/infoCard/index.tsx b/packages/design-system/src/components/landingPage/infoCard/index.tsx index 76d5ddc4c..d061ee64c 100644 --- a/packages/design-system/src/components/landingPage/infoCard/index.tsx +++ b/packages/design-system/src/components/landingPage/infoCard/index.tsx @@ -28,6 +28,7 @@ import { type PSInfo as PSInfoType, type PSInfoKeyType, } from './fetchPSInfo'; +import { I18n } from '@google-psat/i18n'; interface InfoCardProps { infoKey: PSInfoKeyType; @@ -51,7 +52,12 @@ const InfoCard = ({ infoKey, className }: InfoCardProps) => {

diff --git a/packages/design-system/src/components/landingPage/infoCard/learnMoreDropdown.tsx b/packages/design-system/src/components/landingPage/infoCard/learnMoreDropdown.tsx index 450c57a4d..618cfa5c8 100644 --- a/packages/design-system/src/components/landingPage/infoCard/learnMoreDropdown.tsx +++ b/packages/design-system/src/components/landingPage/infoCard/learnMoreDropdown.tsx @@ -18,7 +18,8 @@ * External dependencies. */ import React from 'react'; -import { addUTMParams } from '@ps-analysis-tool/common'; +import { addUTMParams } from '@google-psat/common'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. @@ -33,20 +34,29 @@ import RenderLink from './renderLink'; */ const LABELS = [ { - label: 'Proposal', - linkLabel: 'Public explanation for the proposed solution (Chrome)', + label: 'proposal', + linkLabel: 'proposalNote', + useI18n: true, }, { - label: 'Public Discussion', - linkLabel: 'Public questions and feedback about the proposal', + label: 'Public Explainer', + linkLabel: 'Implementation explainer', + useI18n: false, }, { - label: 'Video Overview', - linkLabel: 'Short summary video', + label: 'publicDiscussion', + linkLabel: 'publicDiscussionNote', + useI18n: true, }, { - label: 'Dev Documentation', - linkLabel: 'Developer documentation', + label: 'videoOverview', + linkLabel: 'videoOverviewNote', + useI18n: true, + }, + { + label: 'devDocumentation', + linkLabel: 'devDocumentationNote', + useI18n: true, }, ]; @@ -56,7 +66,13 @@ interface LearnMoreDropdownProps { } const LearnMoreDropdown = ({ - PSInfo: { proposal, publicDiscussion, videoOverview, devDocumentation }, + PSInfo: { + proposal, + publicExplainer, + publicDiscussion, + videoOverview, + devDocumentation, + }, }: LearnMoreDropdownProps) => { return ( <> @@ -65,22 +81,40 @@ const LearnMoreDropdown = ({ role="list" className="divide-y divide-gray-200 dark:divide-gray-500" > - {[proposal, publicDiscussion, videoOverview, devDocumentation].map( - (value, index) => ( + {[ + proposal, + publicExplainer, + publicDiscussion, + videoOverview, + devDocumentation, + ].map((value, index) => { + if (!value) { + return null; + } + + const label = LABELS[index].useI18n + ? I18n.getMessage(LABELS[index].label) + : LABELS[index].label; + + const linkLabel = LABELS[index].useI18n + ? I18n.getMessage(LABELS[index].linkLabel) + : LABELS[index].linkLabel; + + return ( - ) - )} + ); + })} diff --git a/packages/design-system/src/components/landingPage/infoCard/tests/infoCard.test.tsx b/packages/design-system/src/components/landingPage/infoCard/tests/infoCard.test.tsx index ae12e9969..1a4eec6d3 100644 --- a/packages/design-system/src/components/landingPage/infoCard/tests/infoCard.test.tsx +++ b/packages/design-system/src/components/landingPage/infoCard/tests/infoCard.test.tsx @@ -21,7 +21,8 @@ import React from 'react'; import '@testing-library/jest-dom'; import SinonChrome from 'sinon-chrome'; import { render, screen } from '@testing-library/react'; -import { addUTMParams } from '@ps-analysis-tool/common'; +import { addUTMParams } from '@google-psat/common'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. @@ -42,6 +43,23 @@ describe('should match the json file data with the component', () => { beforeAll(() => { globalThis.chrome = SinonChrome as unknown as typeof chrome; + globalThis.chrome.i18n = null; + + I18n.initMessages({ + proposal: { + message: 'Proposal', + }, + publicDiscussion: { + message: 'Public Discussion', + }, + videoOverview: { + message: 'Video Overview', + }, + devDocumentation: { + message: 'Dev Documentation', + }, + }); + jest.spyOn(console, 'warn').mockImplementation(() => undefined); globalThis.fetch = function () { return Promise.resolve({ diff --git a/packages/design-system/src/components/landingPage/quickLinksList.tsx b/packages/design-system/src/components/landingPage/quickLinksList.tsx index c3e44f2fd..d7f829ff9 100644 --- a/packages/design-system/src/components/landingPage/quickLinksList.tsx +++ b/packages/design-system/src/components/landingPage/quickLinksList.tsx @@ -19,7 +19,8 @@ */ import React, { useEffect, useState } from 'react'; import { XMLParser } from 'fast-xml-parser'; -import { addUTMParams } from '@ps-analysis-tool/common'; +import { addUTMParams } from '@google-psat/common'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. @@ -59,14 +60,19 @@ const QuickLinksList = () => { })(); }, []); + const quickLinks = QUICK_LINKS.map((link) => ({ + ...link, + title: I18n.getMessage(link.title), + })); + return (
- +

- Latest News + {I18n.getMessage('latestNews')}


@@ -79,7 +85,7 @@ const QuickLinksList = () => { rel="noreferrer" className="leading-6 text-sm text-bright-navy-blue dark:text-jordy-blue font-semibold px-3 border border-american-silver dark:border-quartz rounded inline-flex gap-2 items-center" > - View More + {I18n.getMessage('viewMore')}
diff --git a/packages/design-system/src/components/matrix/matrixComponent/index.tsx b/packages/design-system/src/components/matrix/matrixComponent/index.tsx index bd93af98b..544d40492 100644 --- a/packages/design-system/src/components/matrix/matrixComponent/index.tsx +++ b/packages/design-system/src/components/matrix/matrixComponent/index.tsx @@ -32,6 +32,7 @@ export interface MatrixComponentProps { count: number; isExpanded?: boolean; countClassName: string; + containerClasses?: string; } const MatrixComponent = ({ diff --git a/packages/design-system/src/components/matrixContainer/index.tsx b/packages/design-system/src/components/matrixContainer/index.tsx index 088af620a..2d717fccd 100644 --- a/packages/design-system/src/components/matrixContainer/index.tsx +++ b/packages/design-system/src/components/matrixContainer/index.tsx @@ -18,6 +18,7 @@ */ import React, { useState } from 'react'; import classname from 'classnames'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies @@ -45,7 +46,7 @@ interface MatrixContainerProps { const MatrixContainer = ({ matrixData = [], horizontalMatrixData = [], - title = 'Categories', + title = I18n.getMessage('categories'), description = '', showMatrix = true, count = null, @@ -65,7 +66,11 @@ const MatrixContainer = ({ diff --git a/packages/design-system/src/components/sidebar/sidebar.tsx b/packages/design-system/src/components/sidebar/sidebar.tsx index 6faa920fd..70dc1b07e 100644 --- a/packages/design-system/src/components/sidebar/sidebar.tsx +++ b/packages/design-system/src/components/sidebar/sidebar.tsx @@ -50,10 +50,10 @@ const Sidebar = ({ visibleWidth }: SidebarProps) => { } }; - document.addEventListener('click', handleClickOutside); + globalThis?.document?.addEventListener('click', handleClickOutside); return () => { - document.removeEventListener('click', handleClickOutside); + globalThis?.document?.removeEventListener('click', handleClickOutside); }; }, [setIsSidebarFocused]); diff --git a/packages/design-system/src/components/sidebar/sidebarChild.tsx b/packages/design-system/src/components/sidebar/sidebarChild.tsx index 243213228..52d638609 100644 --- a/packages/design-system/src/components/sidebar/sidebarChild.tsx +++ b/packages/design-system/src/components/sidebar/sidebarChild.tsx @@ -17,18 +17,11 @@ * External dependencies. */ import React, { useEffect, useRef } from 'react'; -import { - ArrowDown, - ArrowDownWhite, - InfoIcon, - SidebarItemValue, - useSidebar, -} from '@ps-analysis-tool/design-system'; - /** * Internal dependencies. */ - +import { ArrowDown, ArrowDownWhite, InfoIcon } from '../../icons'; +import { useSidebar, SidebarItemValue } from './useSidebar'; interface SidebarItemProps { didUserInteract: boolean; setDidUserInteract: (didUserInteract: boolean) => void; @@ -98,7 +91,11 @@ const SidebarChild = ({ setIsSidebarFocused(true); }} onKeyDown={(event) => { - onKeyNavigation(event, itemKey); + if (event.key === 'Enter' || event.key === ' ') { + toggleDropdown(!sidebarItem.dropdownOpen, itemKey); + } else { + onKeyNavigation(event, itemKey); + } setIsSidebarFocused(true); }} className={`relative w-full flex items-center py-0.5 outline-0 text-xs dark:text-bright-gray ${ @@ -133,7 +130,7 @@ const SidebarChild = ({ )} {sidebarItem.icon && sidebarItem.selectedIcon && ( -
+
{isKeySelected(itemKey) && isSidebarFocused ? SelectedIcon && ( @@ -142,7 +139,10 @@ const SidebarChild = ({
)}

- {sidebarItem.title} + {typeof sidebarItem.title === 'function' + ? sidebarItem.title() + : sidebarItem.title} + {sidebarItem.infoIconDescription ? ( { expect.any(Object), 'item1' ); + + // Act + fireEvent.keyDown(sidebar, { key: 'Enter' }); + + // Assert + expect(initialState.toggleDropdown).toHaveBeenCalled(); + expect(initialState.toggleDropdown).toHaveBeenCalledWith(true, 'item1'); + + // Act + fireEvent.keyDown(sidebar, { key: ' ' }); + + // Assert + expect(initialState.toggleDropdown).toHaveBeenCalled(); + expect(initialState.toggleDropdown).toHaveBeenCalledWith(true, 'item1'); }); }); diff --git a/packages/design-system/src/components/sidebar/useSidebar/constants.ts b/packages/design-system/src/components/sidebar/useSidebar/constants.ts index b86fb4cb1..9d46503e3 100644 --- a/packages/design-system/src/components/sidebar/useSidebar/constants.ts +++ b/packages/design-system/src/components/sidebar/useSidebar/constants.ts @@ -25,6 +25,7 @@ export enum SIDEBAR_ITEMS_KEYS { PRIVATE_ADVERTISING = 'private-advertising', TOPICS = 'topics', ATTRIBUTION = 'attribution', + PROTECTED_AUDIENCE = 'protected-audience', ANTI_COVERT_TRACKING = 'anti-covert-tracking', BOUNCE_TRACKING = 'bounce-tracking', FINGERPRINTING = 'fingerprinting', diff --git a/packages/design-system/src/components/sidebar/useSidebar/context.ts b/packages/design-system/src/components/sidebar/useSidebar/context.ts index 46d190b3a..765a067d5 100644 --- a/packages/design-system/src/components/sidebar/useSidebar/context.ts +++ b/packages/design-system/src/components/sidebar/useSidebar/context.ts @@ -17,7 +17,7 @@ /** * External dependencies. */ -import { createContext, noop } from '@ps-analysis-tool/common'; +import { createContext, noop } from '@google-psat/common'; /** * Internal dependencies. diff --git a/packages/design-system/src/components/sidebar/useSidebar/types.ts b/packages/design-system/src/components/sidebar/useSidebar/types.ts index 3d0a1ab4d..787de4e26 100644 --- a/packages/design-system/src/components/sidebar/useSidebar/types.ts +++ b/packages/design-system/src/components/sidebar/useSidebar/types.ts @@ -20,7 +20,7 @@ export type SidebarComponent = { }; export type SidebarItemValue = { - title: string; + title: (() => string) | string; children: SidebarItems; popupTitle?: string; infoIconDescription?: string; diff --git a/packages/design-system/src/components/sidebar/useSidebar/useSidebar.tsx b/packages/design-system/src/components/sidebar/useSidebar/useSidebar.tsx index 7602c7538..a22819cfe 100644 --- a/packages/design-system/src/components/sidebar/useSidebar/useSidebar.tsx +++ b/packages/design-system/src/components/sidebar/useSidebar/useSidebar.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { useContextSelector } from '@ps-analysis-tool/common'; +import { useContextSelector } from '@google-psat/common'; import { SidebarContext, SidebarStoreContext } from './context'; export function useSidebar(): SidebarStoreContext; diff --git a/packages/design-system/src/components/table/components/columnMenu/columnListItem.tsx b/packages/design-system/src/components/table/components/columnMenu/columnListItem.tsx index 812b80cca..cf82f4b7a 100644 --- a/packages/design-system/src/components/table/components/columnMenu/columnListItem.tsx +++ b/packages/design-system/src/components/table/components/columnMenu/columnListItem.tsx @@ -41,7 +41,7 @@ const ColumnListItem = ({ return (

  • { > - Clear all + {I18n.getMessage('clearAll')}
    diff --git a/packages/design-system/src/components/table/components/filtersSidebar/index.tsx b/packages/design-system/src/components/table/components/filtersSidebar/index.tsx index 2c587f2ea..8a2facf89 100644 --- a/packages/design-system/src/components/table/components/filtersSidebar/index.tsx +++ b/packages/design-system/src/components/table/components/filtersSidebar/index.tsx @@ -17,6 +17,7 @@ * External dependencies. */ import React, { useCallback, useMemo, useRef, useState } from 'react'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. @@ -88,7 +89,9 @@ const FiltersSidebar = () => { href="#" onClick={handleExpandAllClick} > - {expandAll ? 'Collapse All' : 'Expand All'} + {expandAll + ? I18n.getMessage('collapseAll') + : I18n.getMessage('expandAll')}
      {Object.entries(filters).map(([filterKey, filter]) => ( diff --git a/packages/design-system/src/components/table/components/filtersSidebar/listItem.tsx b/packages/design-system/src/components/table/components/filtersSidebar/listItem.tsx index 0bba0c264..1d9e459c1 100644 --- a/packages/design-system/src/components/table/components/filtersSidebar/listItem.tsx +++ b/packages/design-system/src/components/table/components/filtersSidebar/listItem.tsx @@ -17,13 +17,14 @@ * External dependencies. */ import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import { ArrowDown, InfoIcon } from '@ps-analysis-tool/design-system'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. */ import SubList from './subList'; import { TableFilter } from '../../useTable'; +import { ArrowDown, InfoIcon } from '../../../../icons'; interface ListItemProps { filter: TableFilter[keyof TableFilter]; @@ -128,7 +129,7 @@ const ListItem = ({ className="text-link ml-2 mt-1 block text-royal-blue dark:text-medium-persian-blue" href="#" > - {isExpanded ? 'Show Less' : 'Show More'} + {I18n.getMessage(isExpanded ? 'showLess' : 'showMore')} )} diff --git a/packages/design-system/src/components/table/components/filtersSidebar/subList.tsx b/packages/design-system/src/components/table/components/filtersSidebar/subList.tsx index 065957f83..21aaefdf1 100644 --- a/packages/design-system/src/components/table/components/filtersSidebar/subList.tsx +++ b/packages/design-system/src/components/table/components/filtersSidebar/subList.tsx @@ -17,6 +17,7 @@ * External dependencies. */ import React, { useMemo } from 'react'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. @@ -63,7 +64,7 @@ const SubList = ({ {isSelectAllFilterEnabled && (
    ); diff --git a/packages/design-system/src/components/table/persistentSettingsStore/index.tsx b/packages/design-system/src/components/table/persistentSettingsStore/index.tsx index 86b10ecef..086ea1ec9 100644 --- a/packages/design-system/src/components/table/persistentSettingsStore/index.tsx +++ b/packages/design-system/src/components/table/persistentSettingsStore/index.tsx @@ -25,7 +25,7 @@ import React, { useRef, } from 'react'; import PQueue from 'p-queue'; -import { useContextSelector, createContext } from '@ps-analysis-tool/common'; +import { useContextSelector, createContext } from '@google-psat/common'; /** * Internal dependencies @@ -67,7 +67,7 @@ export const Provider = ({ children }: PropsWithChildren) => { const PROMISE_QUEUE = useMemo(() => new PQueue({ concurrency: 1 }), []); const isChromeExtension = useMemo(() => { - return window.location.protocol === 'chrome-extension:'; + return globalThis?.location.protocol === 'chrome-extension:'; }, []); const preferences = useRef( diff --git a/packages/design-system/src/components/table/persistentSettingsStore/utils/extractStorage.ts b/packages/design-system/src/components/table/persistentSettingsStore/utils/extractStorage.ts index 09ea92c6c..cc5df5a3d 100644 --- a/packages/design-system/src/components/table/persistentSettingsStore/utils/extractStorage.ts +++ b/packages/design-system/src/components/table/persistentSettingsStore/utils/extractStorage.ts @@ -24,16 +24,16 @@ const extractStorage = async ( isChromeExtension: boolean ): Promise => { const options = isChromeExtension - ? extractChromeStorage(persistenceKey) - : await extractLocalStorage(persistenceKey); + ? await extractChromeStorage(persistenceKey) + : extractSessionStorage(persistenceKey); return options; }; -const extractLocalStorage = ( +const extractSessionStorage = ( persistenceKey: string ): TablePersistentSettingsStoreContext['state'] => { - const data = localStorage.getItem(persistenceKey); + const data = sessionStorage.getItem(persistenceKey); if (data) { return JSON.parse(data); } diff --git a/packages/design-system/src/components/table/persistentSettingsStore/utils/updateStorage.ts b/packages/design-system/src/components/table/persistentSettingsStore/utils/updateStorage.ts index 3d1e35436..95dc55a2d 100644 --- a/packages/design-system/src/components/table/persistentSettingsStore/utils/updateStorage.ts +++ b/packages/design-system/src/components/table/persistentSettingsStore/utils/updateStorage.ts @@ -22,6 +22,7 @@ import { TABLE_PERSISTENT_SETTINGS_STORE_KEY, TablePersistentSettingsStoreContext, } from '..'; +import { mergeDeep } from '@google-psat/common'; const updateStorage = async ( persistenceKey: string, @@ -34,14 +35,14 @@ const updateStorage = async ( return result; } - return updateLocalStorage(persistenceKey, options); + return updateSessionStorage(persistenceKey, options); }; -const updateLocalStorage = ( +const updateSessionStorage = ( persistenceKey: string, storageData: PersistentStorageData ) => { - const data = localStorage.getItem(TABLE_PERSISTENT_SETTINGS_STORE_KEY); + const data = sessionStorage.getItem(TABLE_PERSISTENT_SETTINGS_STORE_KEY); if (data) { const allData = JSON.parse( @@ -64,7 +65,7 @@ const updateLocalStorage = ( ...newData, } as TablePersistentSettingsStoreContext['state']; - localStorage.setItem( + sessionStorage.setItem( TABLE_PERSISTENT_SETTINGS_STORE_KEY, JSON.stringify(updatedData) ); @@ -72,7 +73,7 @@ const updateLocalStorage = ( return updatedData; } - localStorage.setItem( + sessionStorage.setItem( TABLE_PERSISTENT_SETTINGS_STORE_KEY, JSON.stringify({ [persistenceKey]: storageData, @@ -100,10 +101,7 @@ const updateChromeStorage = async ( let requiredData = tableData?.[persistenceKey]; if (requiredData) { - requiredData = { - ...requiredData, - ...storageData, - }; + requiredData = mergeDeep(requiredData, storageData); } else { requiredData = storageData; } diff --git a/packages/design-system/src/components/table/useTable/context.ts b/packages/design-system/src/components/table/useTable/context.ts index b0d493340..71614f039 100644 --- a/packages/design-system/src/components/table/useTable/context.ts +++ b/packages/design-system/src/components/table/useTable/context.ts @@ -16,7 +16,7 @@ /** * External dependencies. */ -import { createContext, noop } from '@ps-analysis-tool/common'; +import { createContext, noop } from '@google-psat/common'; /** * Internal dependencies. diff --git a/packages/design-system/src/components/table/useTable/provider.tsx b/packages/design-system/src/components/table/useTable/provider.tsx index 0ecdcd5b8..5586d4d2e 100644 --- a/packages/design-system/src/components/table/useTable/provider.tsx +++ b/packages/design-system/src/components/table/useTable/provider.tsx @@ -18,7 +18,7 @@ * External dependencies. */ import React, { PropsWithChildren, useEffect, useMemo, useState } from 'react'; -import { getValueByKey } from '@ps-analysis-tool/common'; +import { getValueByKey } from '@google-psat/common'; /** * Internal dependencies. */ diff --git a/packages/design-system/src/components/table/useTable/types.ts b/packages/design-system/src/components/table/useTable/types.ts index aef022f07..205513773 100644 --- a/packages/design-system/src/components/table/useTable/types.ts +++ b/packages/design-system/src/components/table/useTable/types.ts @@ -16,7 +16,7 @@ /** * External dependencies. */ -import { CookieTableData, TechnologyData } from '@ps-analysis-tool/common'; +import { CookieTableData, TechnologyData } from '@google-psat/common'; export type TableData = (CookieTableData | TechnologyData) & { highlighted?: boolean; @@ -57,7 +57,7 @@ export type TableFilter = { isSelectAllOptionSelected?: boolean; filterValues?: { [filterValue: string]: { - selected: boolean; + selected: boolean | null; description?: string; }; }; diff --git a/packages/design-system/src/components/table/useTable/useColumnResizing/index.tsx b/packages/design-system/src/components/table/useTable/useColumnResizing/index.tsx index 7cb70b58c..ed81b3d1f 100644 --- a/packages/design-system/src/components/table/useTable/useColumnResizing/index.tsx +++ b/packages/design-system/src/components/table/useTable/useColumnResizing/index.tsx @@ -84,9 +84,9 @@ const useColumnResizing = ( useEffect(() => { const _setColumnsCallback = () => setColumnsCallback(); - window.addEventListener('resize', _setColumnsCallback); + globalThis?.addEventListener('resize', _setColumnsCallback); return () => { - window.removeEventListener('resize', _setColumnsCallback); + globalThis?.removeEventListener('resize', _setColumnsCallback); }; }, [setColumnsCallback]); @@ -151,13 +151,22 @@ const useColumnResizing = ( moveHandler: (e: MouseEvent) => onMove(e.clientX), upHandler: () => { setTimeout(() => setIsResizing(false), 100); - document.removeEventListener('mousemove', mouseEvents.moveHandler); - document.removeEventListener('mouseup', mouseEvents.upHandler); + globalThis?.document?.removeEventListener( + 'mousemove', + mouseEvents.moveHandler + ); + globalThis?.document?.removeEventListener( + 'mouseup', + mouseEvents.upHandler + ); }, }; - document.addEventListener('mousemove', mouseEvents.moveHandler); - document.addEventListener('mouseup', mouseEvents.upHandler); + globalThis?.document?.addEventListener( + 'mousemove', + mouseEvents.moveHandler + ); + globalThis?.document?.addEventListener('mouseup', mouseEvents.upHandler); }, [] ); diff --git a/packages/design-system/src/components/table/useTable/useColumnSorting/index.tsx b/packages/design-system/src/components/table/useTable/useColumnSorting/index.tsx index 8dea36fc1..3ac6e7ef2 100644 --- a/packages/design-system/src/components/table/useTable/useColumnSorting/index.tsx +++ b/packages/design-system/src/components/table/useTable/useColumnSorting/index.tsx @@ -18,7 +18,7 @@ * External dependencies. */ import { useState, useCallback, useMemo, useEffect } from 'react'; -import { getValueByKey } from '@ps-analysis-tool/common'; +import { getValueByKey } from '@google-psat/common'; /** * Internal dependencies. diff --git a/packages/design-system/src/components/table/useTable/useFiltering/hooks/index.ts b/packages/design-system/src/components/table/useTable/useFiltering/hooks/index.ts new file mode 100644 index 000000000..928b57061 --- /dev/null +++ b/packages/design-system/src/components/table/useTable/useFiltering/hooks/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { default as useFiltersPersistence } from './useFiltersPersistence'; +export { default as useFiltersOptions } from './useFiltersOptions'; +export { default as useFiltersExtraction } from './useFiltersExtraction'; diff --git a/packages/design-system/src/components/table/useTable/useFiltering/hooks/tests/useFiltersExtraction.tsx b/packages/design-system/src/components/table/useTable/useFiltering/hooks/tests/useFiltersExtraction.tsx new file mode 100644 index 000000000..d6bac9292 --- /dev/null +++ b/packages/design-system/src/components/table/useTable/useFiltering/hooks/tests/useFiltersExtraction.tsx @@ -0,0 +1,190 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { renderHook } from '@testing-library/react'; +import useFiltersExtraction from '../useFiltersExtraction'; +import React from 'react'; + +describe('useFiltersExtraction', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterAll(() => { + jest.restoreAllMocks(); + }); + + const data = [ + { + parsedCookie: { + name: 'cookie1', + value: 'value1', + sameSite: 'Lax', + }, + }, + { + parsedCookie: { + name: 'cookie2', + value: 'value2', + sameSite: 'None', + }, + }, + ]; + + const tableFilterData = { + 'parsedCookie.name': { + title: 'Cookie Name', + description: 'Name of the cookie', + enableSelectAllOption: true, + }, + 'parsedCookie.value': { + title: 'Cookie Value', + description: 'Value of the cookie', + hasStaticFilterValues: true, + filterValues: { + value1: { + selected: false, + }, + value2: { + selected: false, + }, + }, + }, + 'parsedCookie.sameSite': { + title: 'Same Site', + description: 'Same site of the cookie', + hasStaticFilterValues: true, + hasPrecalculatedFilterValues: true, + filterValues: { + Lax: { + selected: false, + }, + None: { + selected: false, + }, + }, + }, + }; + + const options = { + current: { + 'parsedCookie.value': { + value1: { + selected: true, + }, + value2: { + selected: true, + }, + }, + 'parsedCookie.sameSite': { + None: { + selected: true, + }, + }, + }, + }; + + const selectAllFilterSelection = { + 'parsedCookie.name': { + selected: true, + }, + }; + + const expectedFilters = { + 'parsedCookie.name': { + title: 'Cookie Name', + description: 'Name of the cookie', + enableSelectAllOption: true, + filterValues: { + cookie1: { + selected: true, + }, + cookie2: { + selected: true, + }, + }, + }, + 'parsedCookie.value': { + title: 'Cookie Value', + description: 'Value of the cookie', + hasStaticFilterValues: true, + filterValues: { + value1: { + selected: true, + }, + value2: { + selected: true, + }, + }, + }, + 'parsedCookie.sameSite': { + title: 'Same Site', + description: 'Same site of the cookie', + hasStaticFilterValues: true, + hasPrecalculatedFilterValues: true, + filterValues: { + Lax: { + selected: null, + }, + None: { + selected: true, + }, + }, + }, + }; + + const expectedSelectAllFilterSelection = { + 'parsedCookie.name': { + selected: true, + }, + }; + + it('should extract filters from data with options', () => { + const setFilters = jest.fn(); + const useFiltersSpy = jest.spyOn(React, 'useState'); + useFiltersSpy.mockImplementation(() => [{}, setFilters]); + + const setSelectAllFilterSelection = jest.fn(); + const useSelectAllFilterSelectionSpy = jest.spyOn(React, 'useState'); + useSelectAllFilterSelectionSpy.mockImplementation(() => [ + {}, + setSelectAllFilterSelection, + ]); + + renderHook(() => + useFiltersExtraction( + // @ts-ignore + data, + tableFilterData, + options, + selectAllFilterSelection, + setFilters, + setSelectAllFilterSelection + ) + ); + + expect(setFilters).toHaveBeenCalledWith(expect.any(Function)); + + const setFiltersFn = setFilters.mock.calls[0][0]; + const setSelectAllFilterSelectionFn = + setSelectAllFilterSelection.mock.calls[0][0]; + + expect(setFiltersFn(tableFilterData)).toEqual(expectedFilters); + expect(setSelectAllFilterSelectionFn(selectAllFilterSelection)).toEqual( + expectedSelectAllFilterSelection + ); + }); +}); diff --git a/packages/design-system/src/components/table/useTable/useFiltering/hooks/tests/useFiltersOptions.tsx b/packages/design-system/src/components/table/useTable/useFiltering/hooks/tests/useFiltersOptions.tsx new file mode 100644 index 000000000..b68189feb --- /dev/null +++ b/packages/design-system/src/components/table/useTable/useFiltering/hooks/tests/useFiltersOptions.tsx @@ -0,0 +1,166 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { renderHook } from '@testing-library/react'; +import useFiltersOptions from '../useFiltersOptions'; +import React from 'react'; + +describe('useFiltersOptions', () => { + const tableFilterData = { + 'parsedCookie.name': { + title: 'Cookie Name', + description: 'Name of the cookie', + enableSelectAllOption: true, + filterValues: { + cookie1: { + selected: false, + }, + cookie2: { + selected: false, + }, + }, + }, + 'parsedCookie.value': { + title: 'Cookie Value', + description: 'Value of the cookie', + hasStaticFilterValues: true, + filterValues: { + value1: { + selected: false, + }, + value2: { + selected: false, + }, + }, + }, + 'parsedCookie.sameSite': { + title: 'Same Site', + description: 'Same site of the cookie', + hasStaticFilterValues: true, + hasPrecalculatedFilterValues: true, + filterValues: { + Lax: { + selected: false, + }, + None: { + selected: false, + }, + }, + }, + }; + + const options = { + current: { + 'parsedCookie.name': { + All: { + selected: true, + }, + }, + 'parsedCookie.value': { + value1: { + selected: true, + }, + value2: { + selected: true, + }, + }, + 'parsedCookie.sameSite': { + None: { + selected: true, + }, + }, + }, + }; + + const expectedFilters = { + 'parsedCookie.name': { + title: 'Cookie Name', + description: 'Name of the cookie', + enableSelectAllOption: true, + filterValues: { + cookie1: { + selected: false, + }, + cookie2: { + selected: false, + }, + }, + }, + 'parsedCookie.value': { + title: 'Cookie Value', + description: 'Value of the cookie', + hasStaticFilterValues: true, + filterValues: { + value1: { + selected: true, + }, + value2: { + selected: true, + }, + }, + }, + 'parsedCookie.sameSite': { + title: 'Same Site', + description: 'Same site of the cookie', + hasStaticFilterValues: true, + hasPrecalculatedFilterValues: true, + filterValues: { + Lax: { + selected: false, + }, + None: { + selected: true, + }, + }, + }, + }; + + const expectedSelectAllFilterSelection = { + 'parsedCookie.name': { + selected: true, + }, + }; + + it('should run options on select filters', () => { + const setSelectAllFilterSelection = jest.fn(); + const useSelectAllFilterSelectionSpy = jest.spyOn(React, 'useState'); + useSelectAllFilterSelectionSpy.mockImplementation(() => [ + {}, + setSelectAllFilterSelection, + ]); + + const setFilters = jest.fn(); + const useFiltersSpy = jest.spyOn(React, 'useState'); + useFiltersSpy.mockImplementation(() => [{}, setFilters]); + + renderHook(() => + useFiltersOptions(setSelectAllFilterSelection, setFilters, options, false) + ); + + const setFiltersFn = setFilters.mock.calls[0][0]; + const setSelectAllFilterSelectionFn = + setSelectAllFilterSelection.mock.calls[0][0]; + + expect(setFiltersFn(tableFilterData)).toEqual(expectedFilters); + expect( + setSelectAllFilterSelectionFn({ + 'parsedCookie.name': { + selected: false, + }, + }) + ).toEqual(expectedSelectAllFilterSelection); + }); +}); diff --git a/packages/design-system/src/components/table/useTable/useFiltering/hooks/useFiltersExtraction.tsx b/packages/design-system/src/components/table/useTable/useFiltering/hooks/useFiltersExtraction.tsx new file mode 100644 index 000000000..d48f4e694 --- /dev/null +++ b/packages/design-system/src/components/table/useTable/useFiltering/hooks/useFiltersExtraction.tsx @@ -0,0 +1,241 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { useCallback, useEffect } from 'react'; +import { PersistentStorageData, TableData, TableFilter } from '../../types'; +import { getValueByKey } from '@google-psat/common'; + +/** + * External dependencies. + */ + +const useFiltersExtraction = ( + data: TableData[], + tableFilterData: TableFilter | undefined, + options: React.MutableRefObject, + selectAllFilterSelection: { + [filterKey: string]: { selected: boolean | null }; + }, + setFilters: React.Dispatch>, + setSelectAllFilterSelection: React.Dispatch< + React.SetStateAction<{ [filterKey: string]: { selected: boolean | null } }> + > +) => { + const handleStaticFilters = useCallback( + (filter: TableFilter[keyof TableFilter], filterKey: string) => { + const filterValues = { ...(filter.filterValues || {}) }; + + const updatedFilterValues = Object.fromEntries( + Object.keys(filterValues).map((filterValue) => { + const _filterValue = filterValue.trim(); + return [ + _filterValue, + { + ...filterValues[_filterValue], + selected: + options.current?.[filterKey]?.[_filterValue]?.selected || null, + }, + ]; + }) + ); + + return [filterKey, { ...filter, filterValues: updatedFilterValues }]; + }, + [options] + ); + + const calculateDynamicFilterValues = useCallback( + (filterKey: string, filter: TableFilter[keyof TableFilter]) => { + const filterValues = { ...(filter.filterValues || {}) }; + + const mappedValues = data + .map((row) => { + let value = getValueByKey(filterKey, row); + + if (filter.calculateFilterValues) { + value = filter.calculateFilterValues(value); + } + + return value; + }) + .filter((value) => value && value.toString().trim()); + + const newFilterValues = mappedValues.reduce((acc, value) => { + const val = value.toString().trim(); + + // if selectAll is selected, then select all filter values + if (val && !acc[val]) { + const isSelectAllFilterSelected = + selectAllFilterSelection?.[filterKey]?.selected; + + if (filterValues[val]) { + acc[val] = { + ...filterValues[val], + selected: + isSelectAllFilterSelected || + filterValues[val].selected || + options.current?.[filterKey]?.[val]?.selected || + null, + }; + } else { + acc[val] = { + selected: + isSelectAllFilterSelected || + options.current?.[filterKey]?.[val]?.selected || + null, + }; + } + } + + return acc; + }, {}); + + return newFilterValues; + }, + [data, options, selectAllFilterSelection] + ); + + // extract filters from data + useEffect(() => { + setFilters((prevFilters) => + Object.fromEntries( + Object.entries(prevFilters).map(([filterKey, filter]) => { + if (filter.hasStaticFilterValues) { + return handleStaticFilters(filter, filterKey); + } + + const filterValues = calculateDynamicFilterValues( + filterKey, + filter as TableFilter[keyof TableFilter] + ); + + return [filterKey, { ...filter, filterValues }]; + }) + ) + ); + }, [calculateDynamicFilterValues, handleStaticFilters, setFilters]); + + const extractPrecalculatedFilterKeys = useCallback(() => { + return Object.fromEntries( + Object.entries(tableFilterData || {}) + .filter(([, filter]) => filter.hasPrecalculatedFilterValues) + .map(([key, filter]) => { + return [key, filter.filterValues]; + }) + ); + }, [tableFilterData]); + + // extract filters from precalculated filter values + useEffect(() => { + const filtersByKey = extractPrecalculatedFilterKeys(); + + setFilters((prevFilters) => { + const newFilters = { ...prevFilters }; + + Object.entries(newFilters).forEach(([key, filter]) => { + if (!filtersByKey[key]) { + return; + } + + const filterValues: TableFilter[keyof TableFilter]['filterValues'] = {}; + + Object.entries(filtersByKey[key] || {}).forEach( + ([filterValue, filterValueData]) => { + const _filterValue = filterValue.trim(); + const isSelectAllFilterSelected = + selectAllFilterSelection?.[key]?.selected; + + if (!filter.filterValues) { + filter.filterValues = {}; + } + + if (!filter.filterValues?.[_filterValue]) { + filterValues[_filterValue] = { + ...filterValueData, + selected: + isSelectAllFilterSelected || + filterValueData.selected || + options.current?.[key]?.[_filterValue]?.selected || + null, + }; + } else { + filterValues[_filterValue] = { + ...filterValueData, + selected: + isSelectAllFilterSelected || + filter.filterValues[_filterValue].selected || + filterValueData.selected || + options.current?.[key]?.[_filterValue]?.selected || + null, + }; + } + } + ); + + filter.filterValues = filterValues; + }); + + return newFilters; + }); + }, [ + extractPrecalculatedFilterKeys, + options, + selectAllFilterSelection, + setFilters, + ]); + + // extract filterKeys with selectAllFilter enabled + useEffect(() => { + const filtersWithSelectAllFilterEnabled = Object.entries( + tableFilterData || {} + ) + .filter(([, filter]) => filter.enableSelectAllOption) + .reduce>( + (acc, [filterKey, filterValueData]) => { + acc[filterKey] = { + selected: + filterValueData.isSelectAllOptionSelected || + options.current?.[filterKey]?.All?.selected || + null, + }; + + return acc; + }, + {} + ); + + setSelectAllFilterSelection((prev) => { + const newSelectAllFilterSelection = { ...prev }; + + Object.entries(filtersWithSelectAllFilterEnabled).forEach( + ([filterKey, filterValueData]) => { + if (!newSelectAllFilterSelection[filterKey]) { + newSelectAllFilterSelection[filterKey] = { + selected: + newSelectAllFilterSelection[filterKey]?.selected || + filterValueData.selected || + null, + }; + } + } + ); + + return newSelectAllFilterSelection; + }); + }, [options, setSelectAllFilterSelection, tableFilterData]); +}; + +export default useFiltersExtraction; diff --git a/packages/design-system/src/components/table/useTable/useFiltering/hooks/useFiltersOptions.tsx b/packages/design-system/src/components/table/useTable/useFiltering/hooks/useFiltersOptions.tsx new file mode 100644 index 000000000..8d00b1dd2 --- /dev/null +++ b/packages/design-system/src/components/table/useTable/useFiltering/hooks/useFiltersOptions.tsx @@ -0,0 +1,94 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * External dependencies. + */ +import { useCallback, useEffect } from 'react'; + +/** + * Internal dependencies. + */ +import { PersistentStorageData, TableFilter } from '../../types'; + +const useFiltersOptions = ( + setSelectAllFilterSelection: React.Dispatch< + React.SetStateAction<{ [filterKey: string]: { selected: boolean | null } }> + >, + setFilters: React.Dispatch>, + options: React.MutableRefObject, + isDataLoading: boolean +) => { + const runOptionsOnSelectAllFilters = useCallback(() => { + setSelectAllFilterSelection((prev) => { + const newSelectAllFilterSelection = { ...prev }; + + Object.keys(newSelectAllFilterSelection).forEach((filterKey) => { + const savedFilters = options.current?.[filterKey]; + const isSelectAllSelected = savedFilters?.All?.selected; + + if (!isSelectAllSelected) { + return; + } + + newSelectAllFilterSelection[filterKey].selected = true; + }); + + return newSelectAllFilterSelection; + }); + }, [options, setSelectAllFilterSelection]); + + const runOptionsOnFilters = useCallback(() => { + setFilters((prevFilters) => + Object.fromEntries( + Object.entries(prevFilters).map(([filterKey, filter]) => { + const savedFilterValues = options.current?.[filterKey]; + const filterValues = filter.filterValues || {}; + + if (!Object.keys(savedFilterValues || {}).length) { + return [filterKey, { ...filter, filterValues }]; + } + + Object.entries(savedFilterValues || {}).forEach( + ([filterValue, filterValueData]) => { + if (filterValue === 'All') { + return; + } + + filterValues[filterValue] = { + ...filterValueData, + }; + } + ); + + return [filterKey, { ...filter, filterValues }]; + }) + ) + ); + }, [options, setFilters]); + + // use stored filters to set selected state + useEffect(() => { + if (isDataLoading) { + return; + } + + runOptionsOnSelectAllFilters(); + runOptionsOnFilters(); + }, [isDataLoading, runOptionsOnFilters, runOptionsOnSelectAllFilters]); +}; + +export default useFiltersOptions; diff --git a/packages/design-system/src/components/table/useTable/useFiltering/useFiltersPersistence.tsx b/packages/design-system/src/components/table/useTable/useFiltering/hooks/useFiltersPersistence.tsx similarity index 79% rename from packages/design-system/src/components/table/useTable/useFiltering/useFiltersPersistence.tsx rename to packages/design-system/src/components/table/useTable/useFiltering/hooks/useFiltersPersistence.tsx index b099268ba..58d469219 100644 --- a/packages/design-system/src/components/table/useTable/useFiltering/useFiltersPersistence.tsx +++ b/packages/design-system/src/components/table/useTable/useFiltering/hooks/useFiltersPersistence.tsx @@ -22,17 +22,13 @@ import { useCallback, useEffect, useMemo, useRef } from 'react'; /** * Internal dependencies. */ -import { useTablePersistentSettingsStore } from '../../persistentSettingsStore'; -import { PersistentStorageData, TableFilter } from '../types'; +import { useTablePersistentSettingsStore } from '../../../persistentSettingsStore'; +import { PersistentStorageData, TableFilter } from '../../types'; const useFiltersPersistence = ( filters: TableFilter | undefined, - selectAllFilters: { [filterKey: string]: { selected: boolean } }, - setOptions: React.Dispatch< - React.SetStateAction<{ - [filterKey: string]: TableFilter[keyof TableFilter]['filterValues']; - }> - >, + options: React.MutableRefObject, + selectAllFilters: { [filterKey: string]: { selected: boolean | null } }, setIsDataLoading: React.Dispatch>, specificTablePersistentSettingsKey?: string, genericTablePersistentSettingsKey?: string @@ -45,13 +41,13 @@ const useFiltersPersistence = ( ); const computeAndUpdateOptions = useCallback( - (options: PersistentStorageData['selectedFilters']) => { - setOptions((prevOptions) => ({ - ...prevOptions, - ...(options || {}), - })); + (newOptions: PersistentStorageData['selectedFilters']) => { + options.current = { + ...options.current, + ...newOptions, + }; }, - [setOptions] + [options] ); const genericFilterOptionsRef = useRef< @@ -59,6 +55,8 @@ const useFiltersPersistence = ( >({}); useEffect(() => { + setIsDataLoading(true); + if (specificTablePersistentSettingsKey) { const data = getPreferences( specificTablePersistentSettingsKey, @@ -75,20 +73,21 @@ const useFiltersPersistence = ( setIsDataLoading(false); return () => { - setOptions(() => ({ + options.current = { ...genericFilterOptionsRef.current, - })); + }; }; }, [ computeAndUpdateOptions, getPreferences, + options, setIsDataLoading, - setOptions, specificTablePersistentSettingsKey, ]); useEffect(() => { setIsDataLoading(true); + if (genericTablePersistentSettingsKey) { const data = getPreferences( genericTablePersistentSettingsKey, @@ -105,14 +104,14 @@ const useFiltersPersistence = ( setIsDataLoading(false); return () => { - setOptions(() => ({})); + options.current = {}; }; }, [ computeAndUpdateOptions, genericTablePersistentSettingsKey, getPreferences, + options, setIsDataLoading, - setOptions, ]); const saveFilters = useCallback( @@ -154,27 +153,23 @@ const useFiltersPersistence = ( ...filter.filterValues, }; - if (selected) { - accumulator.genericSelectedFilters[filterKey] = { - ...accumulator.genericSelectedFilters[filterKey], - All: { - selected: true, - }, - }; - } + accumulator.genericSelectedFilters[filterKey] = { + ...accumulator.genericSelectedFilters[filterKey], + All: { + selected, + }, + }; } else { accumulator.specificSelectedFilters[filterKey] = { ...filter.filterValues, }; - if (selected) { - accumulator.specificSelectedFilters[filterKey] = { - ...accumulator.specificSelectedFilters[filterKey], - All: { - selected: true, - }, - }; - } + accumulator.specificSelectedFilters[filterKey] = { + ...accumulator.specificSelectedFilters[filterKey], + All: { + selected, + }, + }; } }); diff --git a/packages/design-system/src/components/table/useTable/useFiltering/index.tsx b/packages/design-system/src/components/table/useTable/useFiltering/index.tsx index afbc83d29..eff58f7d6 100644 --- a/packages/design-system/src/components/table/useTable/useFiltering/index.tsx +++ b/packages/design-system/src/components/table/useTable/useFiltering/index.tsx @@ -17,14 +17,18 @@ /** * External dependencies. */ -import { useCallback, useEffect, useMemo, useState } from 'react'; -import { getValueByKey } from '@ps-analysis-tool/common'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { getValueByKey } from '@google-psat/common'; /** * Internal dependencies. */ -import type { TableData, TableFilter } from '../types'; -import useFiltersPersistence from './useFiltersPersistence'; +import type { PersistentStorageData, TableData, TableFilter } from '../types'; +import { + useFiltersExtraction, + useFiltersOptions, + useFiltersPersistence, +} from './hooks'; export type TableFilteringOutput = { filters: TableFilter; @@ -53,168 +57,43 @@ const useFiltering = ( const [filters, setFilters] = useState({ ...(tableFilterData || {}), }); + const options = useRef({}); const [selectAllFilterSelection, setSelectAllFilterSelection] = useState<{ - [accessorKey: string]: { selected: boolean }; - }>({}); - const [options, setOptions] = useState<{ - [filterKey: string]: TableFilter[keyof TableFilter]['filterValues']; + [accessorKey: string]: { selected: boolean | null }; }>({}); const [isDataLoading, setIsDataLoading] = useState(true); - useEffect(() => { - setIsDataLoading(true); - }, [specificTablePersistentSettingsKey]); - - // extract filters from data - useEffect(() => { - setFilters((prevFilters) => - Object.fromEntries( - Object.entries(prevFilters).map(([filterKey, filter]) => { - let filterValues = { ...(filter.filterValues || {}) }; - - if (filter.hasStaticFilterValues) { - return [filterKey, { ...filter, filterValues }]; - } - - filterValues = data - .map((row) => { - let value = getValueByKey(filterKey, row); - - if (filter.calculateFilterValues) { - value = filter.calculateFilterValues(value); - } - - return value; - }) - .filter((value) => value && value.toString().trim()) - .reduce((acc, value) => { - const val = value.toString().trim(); - - // if selectAll is selected, then select all filter values - if (val && !acc[val]) { - const isSelectAllFilterSelected = - selectAllFilterSelection?.[filterKey]?.selected; - - if (filterValues[val]) { - acc[val] = { - ...filterValues[val], - selected: - isSelectAllFilterSelected || filterValues[val].selected, - }; - } else { - acc[val] = { - selected: isSelectAllFilterSelected || false, - }; - } - } - - return acc; - }, {}); - - return [filterKey, { ...filter, filterValues }]; - }) - ) - ); - }, [data, selectAllFilterSelection]); - - // extract filters from precalculated filter values - useEffect(() => { - const filtersByKey = Object.fromEntries( - Object.entries(tableFilterData || {}) - .filter(([, filter]) => filter.hasPrecalculatedFilterValues) - .map(([key, filter]) => { - return [key, filter.filterValues]; - }) - ); - - setFilters((prevFilters) => { - const newFilters = { ...prevFilters }; - - Object.entries(newFilters).forEach(([key, filter]) => { - if (!filtersByKey[key]) { - return; - } - - const filterValues: TableFilter[keyof TableFilter]['filterValues'] = {}; - - Object.entries(filtersByKey[key] || {}).forEach( - ([filterValue, filterValueData]) => { - const _filterValue = filterValue.trim(); - const isSelectAllFilterSelected = - selectAllFilterSelection?.[key]?.selected; - - if (!filter.filterValues) { - filter.filterValues = {}; - } - - if (!filter.filterValues?.[_filterValue]) { - filterValues[_filterValue] = { - ...filterValueData, - selected: - isSelectAllFilterSelected || - filterValueData.selected || - false, - }; - } else { - filterValues[_filterValue] = { - ...filterValueData, - selected: - isSelectAllFilterSelected || - filter.filterValues[_filterValue].selected || - filterValueData.selected || - false, - }; - } - } - ); - - filter.filterValues = filterValues; - }); - - return newFilters; - }); - }, [selectAllFilterSelection, tableFilterData]); - - // extract filterKeys with selectAllFilter enabled - useEffect(() => { - const filtersWithSelectAllFilterEnabled = Object.entries( - tableFilterData || {} - ) - .filter(([, filter]) => filter.enableSelectAllOption) - .reduce>( - (acc, [filterKey, filterValueData]) => { - acc[filterKey] = { - selected: filterValueData.isSelectAllOptionSelected || false, - }; - - return acc; - }, - {} - ); - - setSelectAllFilterSelection((prev) => { - const newSelectAllFilterSelection = { ...prev }; + // extract saved filters from persistent storage + useFiltersPersistence( + filters, + options, + selectAllFilterSelection, + setIsDataLoading, + specificTablePersistentSettingsKey, + genericTablePersistentSettingsKey + ); - Object.entries(filtersWithSelectAllFilterEnabled).forEach( - ([filterKey, filterValueData]) => { - if (!newSelectAllFilterSelection[filterKey]) { - newSelectAllFilterSelection[filterKey] = { - selected: - newSelectAllFilterSelection[filterKey]?.selected || - filterValueData.selected || - false, - }; - } - } - ); + // run options on filters to update filter values + useFiltersOptions( + setSelectAllFilterSelection, + setFilters, + options, + isDataLoading + ); - return newSelectAllFilterSelection; - }); - }, [tableFilterData]); + // extract filters from data + useFiltersExtraction( + data, + tableFilterData, + options, + selectAllFilterSelection, + setFilters, + setSelectAllFilterSelection + ); const isSelectAllFilterSelected = useCallback( (filterKey: string) => { - return selectAllFilterSelection?.[filterKey]?.selected; + return Boolean(selectAllFilterSelection?.[filterKey]?.selected); }, [selectAllFilterSelection] ); @@ -229,7 +108,7 @@ const useFiltering = ( selected: !newSelectAllFilterSelection[filterKey].selected, }; - valueToSet = newSelectAllFilterSelection[filterKey].selected; + valueToSet = Boolean(newSelectAllFilterSelection[filterKey].selected); return newSelectAllFilterSelection; }); @@ -357,85 +236,96 @@ const useFiltering = ( }); }, [data, isDataLoading, selectedFilters]); + const isFiltering = useMemo( + () => + Object.values(selectedFilters).some((filter) => + Object.values(filter.filterValues).some( + (filterValue) => filterValue.selected + ) + ), + [selectedFilters] + ); + useEffect(() => { - setSelectAllFilterSelection((prev) => { - const newSelectAllFilterSelection = { ...prev }; + if (isDataLoading) { + return; + } - Object.keys(newSelectAllFilterSelection).forEach((filterKey) => { - const savedFilters = options?.[filterKey]; - const isSelectAllSelected = savedFilters?.All?.selected; + // loop options to check if any filter value has changed since last time + options.current = Object.entries(options.current || {}).reduce< + PersistentStorageData['selectedFilters'] + >((acc, [filterKey, filterValues]) => { + if (!acc) { + acc = {}; + } - if (!isSelectAllSelected) { - return; - } + acc[filterKey] = Object.fromEntries( + Object.entries(filterValues || {}).map( + ([filterValue, filterValueData]) => { + const isSelected = + filters[filterKey]?.filterValues?.[filterValue]?.selected; - newSelectAllFilterSelection[filterKey].selected = true; - }); + if (isSelected === undefined) { + return [filterValue, filterValueData]; + } - return newSelectAllFilterSelection; - }); + return [ + filterValue, + { + ...filterValueData, + selected: isSelected || null, + }, + ]; + } + ) + ); - setFilters((prevFilters) => - Object.fromEntries( - Object.entries(prevFilters).map(([filterKey, filter]) => { - const savedFilterValues = options?.[filterKey]; - const filterValues = filter.filterValues || {}; + return acc; + }, {}); - Object.entries(filterValues).forEach( - ([filterValue, filterValueData]) => { - filterValues[filterValue] = { - ...filterValueData, - selected: false, - }; - } - ); + // add new filters to options if they are not present + Object.entries(filters).forEach(([filterKey, filter]) => { + Object.entries(filter.filterValues || {}).forEach( + ([filterValue, filterData]) => { + if (!options.current) { + options.current = {}; + } - if (!Object.keys(savedFilterValues || {}).length) { - return [filterKey, { ...filter, filterValues }]; + if (!options.current?.[filterKey]) { + options.current[filterKey] = {}; } - Object.entries(savedFilterValues || {}).forEach( - ([filterValue, filterValueData]) => { - if (filterValue === 'All') { - return; - } + if ( + !options.current?.[filterKey]?.[filterValue] && + filterData.selected !== null + ) { + // @ts-ignore + options.current[filterKey][filterValue] = { + ...filterData, + }; + } + } + ); + }); - if (!filterValues?.[filterValue]) { - filterValues[filterValue] = { - selected: false, - }; - } + // add selectAll filter values to options if they are not present + Object.entries(selectAllFilterSelection).forEach( + ([filterKey, filterValueData]) => { + if (!options.current) { + options.current = {}; + } - filterValues[filterValue] = { - ...filterValueData, - }; - } - ); + if (!options.current?.[filterKey]) { + options.current[filterKey] = {}; + } - return [filterKey, { ...filter, filterValues }]; - }) - ) + // @ts-ignore + options.current[filterKey].All = { + ...filterValueData, + }; + } ); - }, [options]); - - const isFiltering = useMemo( - () => - Object.values(selectedFilters).some((filter) => - Object.values(filter.filterValues).some( - (filterValue) => filterValue.selected - ) - ), - [selectedFilters] - ); - - useFiltersPersistence( - filters, - selectAllFilterSelection, - setOptions, - setIsDataLoading, - specificTablePersistentSettingsKey, - genericTablePersistentSettingsKey - ); + }, [filters, isDataLoading, selectAllFilterSelection]); return { filters, diff --git a/packages/design-system/src/components/table/useTable/useSearch/index.tsx b/packages/design-system/src/components/table/useTable/useSearch/index.tsx index df4b856cb..c3e128f57 100644 --- a/packages/design-system/src/components/table/useTable/useSearch/index.tsx +++ b/packages/design-system/src/components/table/useTable/useSearch/index.tsx @@ -18,7 +18,7 @@ * External dependencies. */ import { useEffect, useMemo, useState } from 'react'; -import { getValueByKey } from '@ps-analysis-tool/common'; +import { getValueByKey } from '@google-psat/common'; /** * Internal dependencies. diff --git a/packages/design-system/src/components/table/useTable/useTable.tsx b/packages/design-system/src/components/table/useTable/useTable.tsx index a9c9a906f..9d17e1696 100644 --- a/packages/design-system/src/components/table/useTable/useTable.tsx +++ b/packages/design-system/src/components/table/useTable/useTable.tsx @@ -16,7 +16,7 @@ /** * External dependencies. */ -import { useContextSelector } from '@ps-analysis-tool/common'; +import { useContextSelector } from '@google-psat/common'; /** * Internal dependencies. diff --git a/packages/design-system/src/components/table/utils/generateCookieTableCSV.ts b/packages/design-system/src/components/table/utils/generatExtensionCookietableCSV.ts similarity index 54% rename from packages/design-system/src/components/table/utils/generateCookieTableCSV.ts rename to packages/design-system/src/components/table/utils/generatExtensionCookietableCSV.ts index b44315226..aaeb1a31d 100644 --- a/packages/design-system/src/components/table/utils/generateCookieTableCSV.ts +++ b/packages/design-system/src/components/table/utils/generatExtensionCookietableCSV.ts @@ -20,29 +20,30 @@ import { BLOCK_STATUS, CookieTableData, sanitizeCsvRecord, -} from '@ps-analysis-tool/common'; +} from '@google-psat/common'; +import { I18n } from '@google-psat/i18n'; -const COOKIES_TABLE_DATA_HEADER = [ - 'Name', - 'Scope', - 'Domain', - 'Partition Key', - 'Same Site', - 'Category', - 'Platform', - 'Http Only', - 'Secure', - 'Value', - 'Path', - 'Expires', - 'Issues', - 'GDPRPortal', - 'Priority', - 'Size', - 'Blocking Status', +const COOKIES_TABLE_DATA_HEADER_EXTENSION = [ + () => I18n.getMessage('name'), + () => I18n.getMessage('scope'), + () => I18n.getMessage('domain'), + () => I18n.getMessage('partitionKey'), + () => I18n.getMessage('sameSite'), + () => I18n.getMessage('category'), + () => I18n.getMessage('platform'), + () => I18n.getMessage('httpOnly'), + () => I18n.getMessage('secure'), + () => I18n.getMessage('value'), + () => I18n.getMessage('path'), + () => I18n.getMessage('expires'), + () => I18n.getMessage('issues'), + () => I18n.getMessage('gdpr'), + () => I18n.getMessage('priority'), + () => I18n.getMessage('size'), + () => I18n.getMessage('blockingStatus'), ]; -const generateCookieTableCSV = (cookies: CookieTableData[]): Blob => { +const generateExtensionCookieTableCSV = (cookies: CookieTableData[]): Blob => { let cookieRecords = ''; for (const cookie of cookies) { @@ -56,24 +57,32 @@ const generateCookieTableCSV = (cookies: CookieTableData[]): Blob => { let status = ''; if ((isInboundBlocked || isOutboundBlocked) && !hasValidBlockedReason) { - status = 'Undetermined'; + status = I18n.getMessage('undetermined'); } //This should be in the same order as cookieDataHeader const recordsArray = [ cookie.parsedCookie.name, - cookie.isFirstParty ? 'First Party' : 'Third Party', + cookie.isFirstParty + ? I18n.getMessage('firstParty') + : I18n.getMessage('thirdParty'), cookie.parsedCookie.domain || ' ', cookie.parsedCookie.partitionKey || ' ', cookie.parsedCookie.samesite, - cookie.analytics?.category, + I18n.getMessage( + cookie.analytics?.category?.toLowerCase() || 'uncategorized' + ), cookie.analytics?.platform, - cookie.parsedCookie.httponly ? 'Yes' : 'No', - cookie.parsedCookie.secure ? 'Yes' : 'No', + cookie.parsedCookie.httponly + ? I18n.getMessage('yes') + : I18n.getMessage('no'), + cookie.parsedCookie.secure + ? I18n.getMessage('yes') + : I18n.getMessage('no'), cookie.parsedCookie.value, cookie.parsedCookie.path, cookie.parsedCookie.expires, - cookie.isBlocked ? 'Yes' : 'No', + cookie.isBlocked ? I18n.getMessage('yes') : I18n.getMessage('no'), cookie.analytics?.gdprUrl || 'NA', cookie.parsedCookie.priority || ' ', cookie.parsedCookie.size?.toString(), @@ -84,8 +93,10 @@ const generateCookieTableCSV = (cookies: CookieTableData[]): Blob => { } return new Blob([ - COOKIES_TABLE_DATA_HEADER.join(',') + '\r\n' + cookieRecords, + COOKIES_TABLE_DATA_HEADER_EXTENSION.map((header) => header()).join(',') + + '\r\n' + + cookieRecords, ]); }; -export default generateCookieTableCSV; +export default generateExtensionCookieTableCSV; diff --git a/packages/design-system/src/components/table/utils/generateCLICookieTableCSV.ts b/packages/design-system/src/components/table/utils/generateCLICookieTableCSV.ts new file mode 100644 index 000000000..13bf0538a --- /dev/null +++ b/packages/design-system/src/components/table/utils/generateCLICookieTableCSV.ts @@ -0,0 +1,80 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import { CookieTableData, sanitizeCsvRecord } from '@google-psat/common'; +import { I18n } from '@google-psat/i18n'; + +const COOKIES_TABLE_DATA_HEADER_CLI = [ + () => I18n.getMessage('name'), + () => I18n.getMessage('scope'), + () => I18n.getMessage('domain'), + () => I18n.getMessage('partitionKey'), + () => I18n.getMessage('sameSite'), + () => I18n.getMessage('category'), + () => I18n.getMessage('platform'), + () => I18n.getMessage('httpOnly'), + () => I18n.getMessage('secure'), + () => I18n.getMessage('value'), + () => I18n.getMessage('path'), + () => I18n.getMessage('expires'), + () => I18n.getMessage('issues'), + () => I18n.getMessage('gdpr'), +]; + +const generateCLICookieTableCSV = (cookies: CookieTableData[]): Blob => { + let cookieRecords = ''; + + for (const cookie of cookies) { + //This should be in the same order as cookieDataHeader + const recordsArray = [ + cookie.parsedCookie.name, + cookie.isFirstParty + ? I18n.getMessage('firstParty') + : I18n.getMessage('thirdParty'), + cookie.parsedCookie.domain || ' ', + cookie.parsedCookie.partitionKey || ' ', + //@ts-ignore + cookie.parsedCookie?.sameSite ?? cookie.parsedCookie?.samesite, + I18n.getMessage( + cookie.analytics?.category?.toLowerCase() || 'uncategorized' + ), + cookie.analytics?.platform, + cookie.parsedCookie.httponly + ? I18n.getMessage('yes') + : I18n.getMessage('no'), + cookie.parsedCookie.secure + ? I18n.getMessage('yes') + : I18n.getMessage('no'), + cookie.parsedCookie.value, + cookie.parsedCookie.path, + cookie.parsedCookie.expires, + cookie.isBlocked ? I18n.getMessage('yes') : I18n.getMessage('no'), + cookie.analytics?.gdprUrl || 'NA', + ].map(sanitizeCsvRecord); + + cookieRecords += recordsArray.join(',') + '\r\n'; + } + + return new Blob([ + COOKIES_TABLE_DATA_HEADER_CLI.map((header) => header()).join(',') + + '\r\n' + + cookieRecords, + ]); +}; + +export default generateCLICookieTableCSV; diff --git a/packages/design-system/src/components/table/utils/index.ts b/packages/design-system/src/components/table/utils/index.ts index 2dfe9fc89..fab72218b 100644 --- a/packages/design-system/src/components/table/utils/index.ts +++ b/packages/design-system/src/components/table/utils/index.ts @@ -14,5 +14,6 @@ * limitations under the License. */ -export { default as generateCookieTableCSV } from './generateCookieTableCSV'; +export { default as generateCLICookieTableCSV } from './generateCLICookieTableCSV'; +export { default as generateExtensionCookieTableCSV } from './generatExtensionCookietableCSV'; export * from './precalculatedFiltersUtils'; diff --git a/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/calculateBlockedReasonsFilterValues.ts b/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/calculateBlockedReasonsFilterValues.ts index b4b622fd6..b7e39dd11 100644 --- a/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/calculateBlockedReasonsFilterValues.ts +++ b/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/calculateBlockedReasonsFilterValues.ts @@ -17,7 +17,7 @@ /** * External dependencies */ -import { getValueByKey, type CookieTableData } from '@ps-analysis-tool/common'; +import { getValueByKey, type CookieTableData } from '@google-psat/common'; /** * Internal dependencies diff --git a/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/calculateDynamicFilterValues.ts b/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/calculateDynamicFilterValues.ts index 7eee03c29..45b108d92 100644 --- a/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/calculateDynamicFilterValues.ts +++ b/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/calculateDynamicFilterValues.ts @@ -16,7 +16,8 @@ /** * External dependencies */ -import { getValueByKey, type CookieTableData } from '@ps-analysis-tool/common'; +import { getValueByKey, type CookieTableData } from '@google-psat/common'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies @@ -30,18 +31,26 @@ import { TableFilter } from '../../useTable'; * @param tabCookies Cookies to calculate the filter values from. * @param options Options to preselect the filter values. * @param clearQuery Function to clear the query(when options are provided) to avoid conflicts. + * @param runTranslation Boolean to check if the translation should be run. * @returns Filters for the provider key. */ const calculateDynamicFilterValues = ( key: string, tabCookies: CookieTableData[], options: string[], - clearQuery?: () => void + clearQuery?: () => void, + runTranslation?: boolean ): TableFilter[keyof TableFilter]['filterValues'] => { const filters = tabCookies.reduce< TableFilter[keyof TableFilter]['filterValues'] >((acc, cookie) => { - const value = getValueByKey(key, cookie); + let value = getValueByKey(key, cookie); + + if (runTranslation) { + value = I18n.getMessage( + (value as string).toLowerCase() || 'uncategorized' + ); + } if (!acc) { acc = {}; diff --git a/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/calculateExemptionReasons.ts b/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/calculateExemptionReasons.ts index 254639cdd..8e3286a38 100644 --- a/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/calculateExemptionReasons.ts +++ b/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/calculateExemptionReasons.ts @@ -16,7 +16,7 @@ /* * External dependencies */ -import { CookieTableData } from '@ps-analysis-tool/common'; +import { CookieTableData } from '@google-psat/common'; /* * Internal dependencies */ diff --git a/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/evaluateSelectAllOption.ts b/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/evaluateSelectAllOption.ts index d52ddc71e..7b660ae3d 100644 --- a/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/evaluateSelectAllOption.ts +++ b/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/evaluateSelectAllOption.ts @@ -13,6 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/** + * External dependencies. + */ +import { I18n } from '@google-psat/i18n'; /** * Evaluate if the 'All' option is present in parsedQuery. @@ -28,7 +32,7 @@ const evaluateSelectAllOption = ( ) => { const options: string[] = parsedQuery?.filter?.[filterKey]; - if (options?.[0] === 'All') { + if (options?.[0] === I18n.getMessage('selectAll')) { clearActivePanelQuery?.(); return true; diff --git a/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/tests/calculateBlockedReasonsFilterValues.ts b/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/tests/calculateBlockedReasonsFilterValues.ts index ffca3be68..6b2089d0c 100644 --- a/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/tests/calculateBlockedReasonsFilterValues.ts +++ b/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/tests/calculateBlockedReasonsFilterValues.ts @@ -13,7 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +/** + * External dependencies + */ +import { BlockedReason } from '@google-psat/common'; +/** + * Internal dependencies + */ import calculateBlockedReasonsFilterValues from '../calculateBlockedReasonsFilterValues'; describe('calculateBlockedReasonsFilterValues', () => { @@ -21,26 +27,63 @@ describe('calculateBlockedReasonsFilterValues', () => { // Arrange const tabCookies = [ { - blockedReasons: ['foo', 'bar'], + parsedCookie: { + name: 'countryCode', + domain: '.cnn.com', + path: '/', + value: 'IN', + sameSite: 'None', + expires: 'Session', + httpOnly: false, + secure: true, + }, + url: '', + blockedReasons: ['DomainMismatch'] as BlockedReason[], frameIdList: [1, 2, 3], }, { - blockedReasons: ['foo', 'baz'], + parsedCookie: { + name: 'countryCode', + domain: '.cnn.com', + path: '/', + value: 'IN', + sameSite: 'None', + expires: 'Session', + httpOnly: false, + secure: true, + }, + url: '', + blockedReasons: ['SameSiteUnspecifiedTreatedAsLax'] as BlockedReason[], frameIdList: [1, 2, 3], }, { - blockedReasons: ['zee', 'zoo'], + parsedCookie: { + name: 'countryCode', + domain: '.cnn.com', + path: '/', + value: 'IN', + sameSite: 'None', + expires: 'Session', + httpOnly: false, + secure: true, + }, + url: '', + blockedReasons: [ + 'ThirdPartyPhaseout', + 'DomainMismatch', + ] as BlockedReason[], + frameIdList: [], }, ]; const expected = { - foo: { + SameSiteUnspecifiedTreatedAsLax: { selected: false, }, - bar: { + ThirdPartyPhaseout: { selected: false, }, - baz: { + DomainMismatch: { selected: false, }, }; @@ -48,6 +91,7 @@ describe('calculateBlockedReasonsFilterValues', () => { const result = calculateBlockedReasonsFilterValues( tabCookies, + //@ts-ignore undefined, mockClearQuery ); @@ -55,16 +99,16 @@ describe('calculateBlockedReasonsFilterValues', () => { expect(result).toEqual(expected); expect(mockClearQuery).not.toHaveBeenCalled(); - const options = ['foo', 'bar']; + const options = ['SameSiteUnspecifiedTreatedAsLax', 'ThirdPartyPhaseout']; const expectedWithSelected = { - foo: { + SameSiteUnspecifiedTreatedAsLax: { selected: true, }, - bar: { + ThirdPartyPhaseout: { selected: true, }, - baz: { + DomainMismatch: { selected: false, }, }; diff --git a/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/tests/calculateDynamicFilterValues.ts b/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/tests/calculateDynamicFilterValues.ts index 749a428e6..dec636944 100644 --- a/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/tests/calculateDynamicFilterValues.ts +++ b/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/tests/calculateDynamicFilterValues.ts @@ -13,7 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +/** + * External dependencies + */ +import { BlockedReason } from '@google-psat/common'; +/** + * Internal dependencies + */ import calculateDynamicFilterValues from '../calculateDynamicFilterValues'; describe('calculateDynamicFilterValues', () => { @@ -21,24 +27,60 @@ describe('calculateDynamicFilterValues', () => { // Arrange const tabCookies = [ { - keyToExtract: 'value1', + parsedCookie: { + name: 'countryCode', + domain: 'cnn.com', + path: '/', + value: 'IN', + sameSite: 'None', + expires: 'Session', + httpOnly: false, + secure: true, + }, + url: '', + blockedReasons: ['DomainMismatch'] as BlockedReason[], + frameIdList: [1, 2, 3], }, { - keyToExtract: 'value2', + parsedCookie: { + name: 'countryCode', + domain: '.cnn.com', + path: '/', + value: 'IN', + sameSite: 'None', + expires: 'Session', + httpOnly: false, + secure: true, + }, + url: '', + blockedReasons: ['SameSiteUnspecifiedTreatedAsLax'] as BlockedReason[], + frameIdList: [1, 2, 3], }, { - keyToExtract: 'value3', + parsedCookie: { + name: 'countryCode', + domain: '.cnn.com', + path: '/', + value: 'IN', + sameSite: 'None', + expires: 'Session', + httpOnly: false, + secure: true, + }, + url: '', + blockedReasons: [ + 'ThirdPartyPhaseout', + 'DomainMismatch', + ] as BlockedReason[], + frameIdList: [], }, ]; const expected = { - value1: { + 'cnn.com': { selected: false, }, - value2: { - selected: false, - }, - value3: { + '.cnn.com': { selected: false, }, }; @@ -47,8 +89,9 @@ describe('calculateDynamicFilterValues', () => { // Act const result = calculateDynamicFilterValues( - 'keyToExtract', + 'parsedCookie.domain', tabCookies, + //@ts-ignore undefined, mockClearQuery ); @@ -57,23 +100,20 @@ describe('calculateDynamicFilterValues', () => { expect(result).toEqual(expected); expect(mockClearQuery).not.toHaveBeenCalled(); - const options = ['value1', 'value2']; + const options = ['cnn.com', '.cnn.com']; const expectedWithSelected = { - value1: { + 'cnn.com': { selected: true, }, - value2: { + '.cnn.com': { selected: true, }, - value3: { - selected: false, - }, }; // Act const resultWithSelected = calculateDynamicFilterValues( - 'keyToExtract', + 'parsedCookie.domain', tabCookies, options, mockClearQuery diff --git a/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/tests/calculateExemptionReasons.ts b/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/tests/calculateExemptionReasons.ts index 0042363a0..e7f5cabb3 100644 --- a/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/tests/calculateExemptionReasons.ts +++ b/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/tests/calculateExemptionReasons.ts @@ -13,24 +13,76 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +/** + * External dependencies + */ +import { BlockedReason, CookieData } from '@google-psat/common'; +/** + * Internal dependencies + */ import calculateExemptionReason from '../calculateExemptionReasons'; describe('calculateExemptionReason', () => { it('should no exemption reasons', () => { const tabCookies = [ { - exemptionReason: 'None', + parsedCookie: { + name: 'countryCode', + domain: 'cnn.com', + path: '/', + value: 'IN', + sameSite: 'None', + expires: 'Session', + httpOnly: false, + secure: true, + }, + url: '', + blockedReasons: ['DomainMismatch'] as BlockedReason[], + frameIdList: [1, 2, 3], + exemptionReason: 'None' as CookieData['exemptionReason'], }, { - exemptionReason: 'None', + parsedCookie: { + name: 'countryCode', + domain: '.cnn.com', + path: '/', + value: 'IN', + sameSite: 'None', + expires: 'Session', + httpOnly: false, + secure: true, + }, + url: '', + blockedReasons: ['SameSiteUnspecifiedTreatedAsLax'] as BlockedReason[], + frameIdList: [1, 2, 3], + exemptionReason: 'TPCDHeuristics' as CookieData['exemptionReason'], }, { - exemptionReason: 'None', + parsedCookie: { + name: 'countryCode', + domain: '.cnn.com', + path: '/', + value: 'IN', + sameSite: 'None', + expires: 'Session', + httpOnly: false, + secure: true, + }, + url: '', + blockedReasons: ['DomainMismatch'] as BlockedReason[], + frameIdList: [], + exemptionReason: 'UserSettings' as CookieData['exemptionReason'], }, ]; - const expected = {}; + const expected = { + UserSettings: { + selected: false, + }, + TPCDHeuristics: { + selected: false, + }, + }; const clearActivePanelQuery = jest.fn(); @@ -44,13 +96,52 @@ describe('calculateExemptionReason', () => { it('should return unique set of exemption reason.', () => { const tabCookies = [ { - exemptionReason: 'UserSettings', + parsedCookie: { + name: 'countryCode', + domain: 'cnn.com', + path: '/', + value: 'IN', + sameSite: 'None', + expires: 'Session', + httpOnly: false, + secure: true, + }, + url: '', + blockedReasons: ['DomainMismatch'] as BlockedReason[], + frameIdList: [1, 2, 3], + exemptionReason: 'None' as CookieData['exemptionReason'], }, { - exemptionReason: 'TPCDHeuristics', + parsedCookie: { + name: 'countryCode', + domain: '.cnn.com', + path: '/', + value: 'IN', + sameSite: 'None', + expires: 'Session', + httpOnly: false, + secure: true, + }, + url: '', + blockedReasons: ['SameSiteUnspecifiedTreatedAsLax'] as BlockedReason[], + frameIdList: [1, 2, 3], + exemptionReason: 'TPCDHeuristics' as CookieData['exemptionReason'], }, { - exemptionReason: 'None', + parsedCookie: { + name: 'countryCode', + domain: '.cnn.com', + path: '/', + value: 'IN', + sameSite: 'None', + expires: 'Session', + httpOnly: false, + secure: true, + }, + url: '', + blockedReasons: ['DomainMismatch'] as BlockedReason[], + frameIdList: [], + exemptionReason: 'UserSettings' as CookieData['exemptionReason'], }, ]; diff --git a/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/tests/evaluateSelectAllOption.ts b/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/tests/evaluateSelectAllOption.ts index 056a07fda..bf92ae550 100644 --- a/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/tests/evaluateSelectAllOption.ts +++ b/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/tests/evaluateSelectAllOption.ts @@ -14,9 +14,20 @@ * limitations under the License. */ +import { I18n } from '@google-psat/i18n'; import evaluateSelectAllOption from '../evaluateSelectAllOption'; describe('evaluateSelectAllOption', () => { + beforeAll(() => { + globalThis.chrome.i18n = null; + + I18n.initMessages({ + selectAll: { + message: 'All', + }, + }); + }); + it('should return true if the first option is "All"', () => { // Arrange const filterKey = 'blockedReasons'; @@ -29,7 +40,7 @@ describe('evaluateSelectAllOption', () => { const result = evaluateSelectAllOption( filterKey, - undefined, + {}, clearActivePanelQuery ); diff --git a/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/tests/evaluateStaticFilterValues.ts b/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/tests/evaluateStaticFilterValues.ts index ae85f22c0..6dda323ab 100644 --- a/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/tests/evaluateStaticFilterValues.ts +++ b/packages/design-system/src/components/table/utils/precalculatedFiltersUtils/tests/evaluateStaticFilterValues.ts @@ -36,7 +36,7 @@ describe('evaluateSelectAllOption', () => { const result1 = evaluateStaticFilterValues( staticValues, filterKey, - undefined, + {}, clearActivePanelQuery ); diff --git a/packages/design-system/src/components/toastMessage/index.tsx b/packages/design-system/src/components/toastMessage/index.tsx index 4ec5a89e0..49fb58112 100644 --- a/packages/design-system/src/components/toastMessage/index.tsx +++ b/packages/design-system/src/components/toastMessage/index.tsx @@ -17,6 +17,7 @@ * External dependencies. */ import React from 'react'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. @@ -37,7 +38,7 @@ const ToastMessage = ({ additionalStyles = '', textAdditionalStyles = '', variant = 'large', - buttonText = 'Reload', + buttonText = I18n.getMessage('reload'), }: ToastMessageProps) => { return (
    This cookie was blocked because it had the 'Secure' attribute and the connection was not secure.

    ", - DomainMismatch: - "

    This cookie was blocked because the request URL's domain did not exactly match the cookie's domain, nor was the request URL's domain a subdomain of the cookie's Domain attribute value.

    ", - NotOnPath: - "

    This cookie was blocked because its path was not an exact match for, or a superdirectory of, the request URL's path.

    ", - SameSiteStrict: - '

    This cookie was blocked because it had the SameSite=Strict attribute and the request was made from a different site. This includes top-level navigation requests initiated by other sites.

    ', + Functional: 'functionalNote', + Marketing: 'marketingNote', + Analytics: 'analyticsNote', + Uncategorized: 'uncategorizedNote', + SecureOnly: ['body_SecureOnly'], + NotOnPath: ['body_NotOnPath'], + DomainMismatch: ['body_DomainMismatch'], + SameSiteStrict: ['body_SameSiteStrict'], + SameSiteUnspecifiedTreatedAsLax: ['body_SameSiteUnspecifiedTreatedAsLax'], + SameSiteNoneInsecure: ['body_SameSiteNoneInsecure'], + UserPreferences: ['body_UserPreferences'], + ThirdPartyPhaseout: ['body_ThirdPartyPhaseout'], + ThirdPartyBlockedInFirstPartySet: ['body_ThirdPartyBlockedInFirstPartySet'], + UnknownError: ['body_UnknownError'], + SchemefulSameSiteStrict: ['body_SchemefulSameSiteStrict'], + SchemefulSameSiteLax: ['body_SchemefulSameSiteLax'], + SchemefulSameSiteUnspecifiedTreatedAsLax: [ + 'body_SchemefulSameSiteUnspecifiedTreatedAsLax', + ], + SamePartyFromCrossPartyContext: ['body_SamePartyFromCrossPartyContext'], + NameValuePairExceedsMaxSize: ['body_NameValuePairExceedsMaxSize'], + InvalidDomain: ['body_InvalidDomain'], + ExcludeSameSiteNoneInsecure: [ + 'header_ExcludeSameSiteNoneInsecure', + 'body_ExcludeSameSiteNoneInsecure', + ], + ExcludeSameSiteLax: 'somethingWentWrong', + ExcludeSameSiteStrict: 'somethingWentWrong', + ExcludeInvalidSameParty: [ + 'header_ExcludeInvalidSameParty', + 'body_ExcludeInvalidSameParty', + ], + ExcludeSamePartyCrossPartyContext: [ + 'header_ExcludeSamePartyCrossPartyContext', + 'body_ExcludeSamePartyCrossPartyContext', + ], + ExcludeDomainNonASCII: [ + 'header_ExcludeDomainNonASCII', + 'body_1_ExcludeDomainNonASCII', + 'body_2_ExcludeDomainNonASCII', + 'body_3_ExcludeDomainNonASCII', + ], + ExcludeThirdPartyCookieBlockedInFirstPartySet: [ + 'header_ExcludeThirdPartyCookieBlockedInFirstPartySet', + 'body_ExcludeThirdPartyCookieBlockedInFirstPartySet', + ], + ExcludeThirdPartyPhaseout: ['body_ExcludeThirdPartyPhaseout'], + 'Total frames': 'totalFramesNote', + 'Frames with cookies': 'framesWithCookiesNote', + 'Frames with blocked cookies': 'framesWithBlockedCookiesNote', + 'Frames with unblocked cookies': 'framesWithUnblockedCookiesNote', + 'Fenced frames': 'fencedFramesNote', + UserSetting: 'exemptionReasonUserSetting', + TPCDMetadata: 'exemptionReasonTPCDMetadata', + TPCDDeprecationTrial: 'exemptionReasonTPCDDeprecationTrial', + TPCDHeuristics: 'exemptionReasonTPCDHeuristics', + EnterprisePolicy: 'exemptionReasonEnterprisePolicy', + StorageAccessAPI: 'exemptionReasonStorageAccessAPI', + TopLevelStorageAccessAPI: 'exemptionReasonTopLevelStorageAccessAPI', + CorsOptIn: 'exemptionReasonCorsOptIn', SameSiteLax: - '

    This cookie was blocked because it had the SameSite=Lax attribute and the request was made from a different site and was not initiated by a top-level navigation.

    ', - SameSiteUnspecifiedTreatedAsLax: - "

    This cookie didn't specify a 'SameSite' attribute when it was stored, was defaulted to SameSite=Lax, and was blocked because the request was made from a different site and was not initiated by a top-level navigation. The cookie had to have been set with SameSite=None to enable cross-site usage.

    ", - SameSiteNoneInsecure: - "

    This cookie was blocked because it had the SameSite=None attribute but was not marked 'Secure'. Cookies without SameSite restrictions must be marked 'Secure' and sent over a secure connection.

    ", - UserPreferences: '

    This cookie was blocked due to user preferences.

    ', - ThirdPartyPhaseout: '

    Prepare for phasing out third-party cookies

    ', - ThirdPartyBlockedInFirstPartySet: - '

    The cookie was blocked by third-party cookie blocking between sites in the same First-Party Set.

    ', - UnknownError: '

    Unknown error

    ', - SchemefulSameSiteStrict: - '

    This cookie was blocked because it had the SameSite=Strict attribute but the request was cross-site. This includes top-level navigation requests initiated by other sites. This request is considered cross-site because the URL has a different scheme than the current site.

    ', - SchemefulSameSiteLax: - '

    This cookie was blocked because it had the SameSite=Lax attribute but the request was cross-site and was not initiated by a top-level navigation. This request is considered cross-site because the URL has a different scheme than the current site.

    ', - SchemefulSameSiteUnspecifiedTreatedAsLax: - "

    This cookie didn't specify a 'SameSite' attribute when it was stored, was defaulted to 'SameSite=Lax\"', and was blocked because the request was cross-site and was not initiated by a top-level navigation. This request is considered cross-site because the URL has a different scheme than the current site.

    ", - SamePartyFromCrossPartyContext: - "

    This cookie was blocked because it had the 'SameParty' attribute but the request was cross-party. The request was considered cross-party because the domain of the resource's URL and the domains of the resource's enclosing frames/documents are neither owners nor members in the same first-party set.

    ", - NameValuePairExceedsMaxSize: - '

    This cookie was blocked because it was too large. The combined size of the name and value must be less than or equal to 4,096 characters.

    ', - ExcludeSameSiteUnspecifiedTreatedAsLax: - "

    This Set-Cookie header didn't specify a 'SameSite' attribute and was defaulted to SameSite=Lax, and was blocked because it came from a cross-site response which was not the response to a top-level navigation. The Set-Cookie had to have been set with SameSite=None to enable cross-site usage.

    ", - ExcludeSameSiteNoneInsecure: - '\n

    Mark cross-site cookies as Secure to allow them to be sent in cross-site requests

    \n
    \n

    Cookies marked with SameSite=None must also be marked with Secure to get sent in cross-site requests. \n This behavior protects user data from being sent over an insecure connection.

    \n
    \n

    Resolve this issue by updating the attributes of the cookie:

    \n
      \n
    • Specify SameSite=None and Secure if the cookie should be sent in cross-site requests. This enables third-party use.
    • \n
    • Specify SameSite=Strict or SameSite=Lax if the cookie should not be sent in cross-site requests.
    • \n
    \n', - ExcludeSameSiteLax: 'Something went wrong', - ExcludeSameSiteStrict: 'Something went wrong', - ExcludeInvalidSameParty: - '\n

    Mark SameParty cookies as Secure and do not use SameSite=Strict for SameParty cookies

    \n
    \n

    Cookies marked with SameParty must also be marked with Secure. In addition, cookies marked with SameParty cannot use SameSite=Strict.

    \n
    \n

    Resolve this issue by updating the attributes of the cookie:

    \n
      \n
    • Remove SameParty if the cookie should only be used by the same site but not the same first-party set
    • \n
    • Remove SameSite=Strict and specify Secure if the cookie should be available to all sites of the same first-party set
    • \n
    \n', - ExcludeSamePartyCrossPartyContext: - "\n

    Make sure a cookie is using the SameParty attribute correctly

    \n
    \n

    Setting cross-site cookies with the SameParty attribute is only possible if both domains are a part of the same First-Party Set.

    \n
    \n

    To allow setting cross-site cookies, try one of the following:

    \n
      \n
    • If the domains satisfy the First-Party Set criteria, add them to the same First-Party Set.
    • \n
    • If the domains don't satisfy the First-Party Set criteria, remove the SameParty attribute and specify SameSite=None.
    • \n
    \n
    \n

    If you don't have the option to do any of the above, cookies are not intended to be set in cross-site contexts.

    \n", - ExcludeDomainNonASCII: - '\n

    Ensure cookie Domain attribute values only contain ASCII characters

    \n
    \n

    Domain attributes in cookies are restricted to the ASCII character set. Any cookies that contain characters outside of the ASCII range in their Domain attribute will be ignored.

    \n
    \n

    To resolve this issue, you need to remove all non-ASCII characters from the Domain attribute of the affected cookies.

    \n
    \n

    If your site has an internationalized domain name (IDN), you should use punycode representation for the Domain attribute instead.

    \n', - ExcludeThirdPartyCookieBlockedInFirstPartySet: - "\n

    Third-party cookie blocked within the same First-Party Set

    \n
    \n

    \n A cookie embedded by a site in the top-level page's First-Party Set was blocked by third-party cookie blocking.\n

    \n", - ExcludeThirdPartyPhaseout: - '\n

    Cookie is blocked when sent in cross-site context

    \n
    \n

    \n Cookies marked with SameSite=None; Secure; and not Partitioned are blocked in cross-site requests. This behavior protects user data from cross-site tracking.\n

    \n', - InvalidDomain: `

    This attempt to set a cookie via 'Set-Cookie' header was blocked because its Domain value was invalid with regards to the current host url.

    `, - 'Total frames': 'The total frames present inside the current page.', - 'Frames with cookies': 'The count of frames which have cookies in them', - 'Frames with blocked cookies': - 'The count of frames which have blocked cookies.', - 'Frames with unblocked cookies': - 'The count of frames which have unblocked cookies.', - 'Fenced frames': - 'A fenced frames is a proposed HTML element for embedded content, similar to an iframe. Unlike iframes, a fenced frame restricts communication with its embedding context to allow the frame access to cross-site data without sharing it with the embedding context.', - UserSetting: 'Allowed by user preferences.', - TPCDMetadata: - 'Allowed by a third-party cookie deprecation trial grace period.', - TPCDDeprecationTrial: - 'Allowed by third-party cookie phaseout deprecation trial.', - TPCDHeuristics: 'Allowed by third-party cookie phaseout heuristics.', - EnterprisePolicy: 'Allowed by Chrome Enterprise policy.', - StorageAccess: 'Allowed by the Storage Access API.', - TopLevelStorageAccess: 'Allowed by the top-level Storage Access API.', - CorsOptIn: 'Allowed by CORS opt-in.', + "This cookie was blocked because it had the 'SameSite=Lax' attribute and the request was made from a different site and was not initiated by a top-level navigation.", + SyntaxError: "This 'Set-Cookie' header had invalid syntax.", + SchemeNotSupported: + 'The scheme of this connection is not allowed to store cookies.', + OverwriteSecure: + "This attempt to set a cookie via a 'Set-Cookie' header was blocked because it was not sent over a secure connection and would have overwritten a cookie with the Secure attribute.", + InvalidPrefix: + "This attempt to set a cookie via a 'Set-Cookie' header was blocked because it used the '__Secure-' or '__Host-' prefix in its name and broke the additional rules applied to cookies with these prefixes.", + SamePartyConflictsWithOtherAttributes: + "This attempt to set a cookie via a 'Set-Cookie' header was blocked because it had the 'SameParty' attribute but also had other conflicting attributes. Chrome requires cookies that use the 'SameParty' attribute to also have the 'Secure' attribute, and to not be restricted to 'SameSite=Strict'.", + DisallowedCharacter: + "This 'Set-Cookie' header contained a disallowed character (a forbidden ASCII control character, or the tab character if it appears in the middle of the cookie name, value, an attribute name, or an attribute value).", + NoCookieContent: + "This attempt to set a cookie via a 'Set-Cookie' header was blocked beacuse the header did not contain any value.", }; export const EMPTY_FRAME_COUNT = [ { - title: 'Frames', + title: I18n.getMessage('frames'), count: 0, data: [ { @@ -115,25 +123,29 @@ export const EMPTY_FRAME_COUNT = [ export const EMPTY_FRAME_LEGEND = [ { - label: 'Total frames', + label: I18n.getMessage('totalFrames'), + descriptionKey: 'Total frames', count: 0, color: '#25ACAD', countClassName: 'text-greenland-green', }, { - label: 'Frames with cookies', + label: I18n.getMessage('framesWithCookies'), + descriptionKey: 'Frames with cookies', count: 0, color: '#C5A06A', countClassName: 'text-good-life', }, { - label: 'Frames with blocked cookies', + label: I18n.getMessage('framesWithBlockedCookies'), + descriptionKey: 'Frames with blocked cookies', count: 0, color: '#AF7AA3', countClassName: 'text-victorian-violet', }, { - label: 'Frames with unblocked cookies', + label: I18n.getMessage('framesWithUnblockedCookies'), + descriptionKey: 'Frames with unblocked cookies', count: 0, color: '#F54021', countClassName: 'text-strawberry-spinach-red', diff --git a/packages/design-system/src/icons/groups.svg b/packages/design-system/src/icons/groups.svg new file mode 100644 index 000000000..72d8d804b --- /dev/null +++ b/packages/design-system/src/icons/groups.svg @@ -0,0 +1 @@ + diff --git a/packages/design-system/src/icons/index.tsx b/packages/design-system/src/icons/index.tsx index 9fff1ea4b..4b67a45ed 100644 --- a/packages/design-system/src/icons/index.tsx +++ b/packages/design-system/src/icons/index.tsx @@ -72,3 +72,4 @@ export { default as QuestionMark } from './question-mark.svg'; export { default as GreenTick } from './green-tick-icon.svg'; export { default as ChevronDown } from './chevron-down.svg'; export { default as WarningBare } from './warning.svg'; +export { default as GroupsIcon } from './groups.svg'; diff --git a/packages/design-system/src/test-data/cookieMockData.ts b/packages/design-system/src/test-data/cookieMockData.ts index 1d793ffe6..ae6d1d099 100644 --- a/packages/design-system/src/test-data/cookieMockData.ts +++ b/packages/design-system/src/test-data/cookieMockData.ts @@ -17,7 +17,7 @@ * External dependencies. */ import { type Cookie as ParsedCookie } from 'simple-cookie'; -import { TabCookies, TabFrames } from '@ps-analysis-tool/common'; +import { TabCookies, TabFrames } from '@google-psat/common'; export const emptyAnalytics = { platform: '', @@ -166,7 +166,7 @@ const data: { tabUrl: 'https://edition.cnn.com/', tabFrames: { 'https://edition.cnn.com/': { - frameIds: [1], + frameIds: ['1'], }, }, selectedFrame: 'https://edition.cnn.com/', diff --git a/packages/design-system/src/test-data/cookieTableBody.ts b/packages/design-system/src/test-data/cookieTableBody.ts index 16b2b1470..75687480e 100644 --- a/packages/design-system/src/test-data/cookieTableBody.ts +++ b/packages/design-system/src/test-data/cookieTableBody.ts @@ -16,7 +16,7 @@ /** * External dependencies */ -import { noop } from '@ps-analysis-tool/common'; +import { noop } from '@google-psat/common'; /** * Internal dependencies. diff --git a/packages/design-system/src/test-data/cookieTableMockData.tsx b/packages/design-system/src/test-data/cookieTableMockData.tsx index 38cad920e..f3e839d32 100644 --- a/packages/design-system/src/test-data/cookieTableMockData.tsx +++ b/packages/design-system/src/test-data/cookieTableMockData.tsx @@ -16,8 +16,12 @@ /** * External dependencies */ +import { noop } from '@google-psat/common'; import React from 'react'; -import { noop, type InfoType } from '@ps-analysis-tool/design-system'; +/** + * Internal dependencies. + */ +import { InfoType } from '../components'; export const originalData = [ { diff --git a/packages/design-system/src/theme/colors.ts b/packages/design-system/src/theme/colors.ts index f38f13822..27a1f8384 100644 --- a/packages/design-system/src/theme/colors.ts +++ b/packages/design-system/src/theme/colors.ts @@ -187,4 +187,32 @@ export const COLOR_MAP: { color: '#D741A7', className: 'text-hollywood-cerise', }, + SyntaxError: { + color: '#FF6F91', + className: 'text-baby-pink-gradient', + }, + SchemeNotSupported: { + color: '#B39CD0', + className: 'text-spot-palette-mud-pink', + }, + OverwriteSecure: { + color: '#E59500', + className: 'text-jamboo', + }, + InvalidPrefix: { + color: '#EB4B98', + className: 'text-rose-bourbon', + }, + SamePartyConflictsWithOtherAttributes: { + color: '#E0A890', + className: 'text-orange-buff', + }, + DisallowedCharacter: { + color: '#0FFF95', + className: 'text-spring-green', + }, + NoCookieContent: { + color: '#840032', + className: 'text-maroon-claret', + }, }; diff --git a/packages/design-system/src/utils/prepareCookieDataMapping.ts b/packages/design-system/src/utils/prepareCookieDataMapping.ts index 82a66290a..b9b534823 100644 --- a/packages/design-system/src/utils/prepareCookieDataMapping.ts +++ b/packages/design-system/src/utils/prepareCookieDataMapping.ts @@ -19,12 +19,9 @@ import type { CookieStatsComponents, CookiesCount, -} from '@ps-analysis-tool/common'; - -/** - * Internal dependencies - */ -import { DataMapping } from '../components/cookiesLanding/landingHeader'; + DataMapping, +} from '@google-psat/common'; +import { I18n } from '@google-psat/i18n'; /** * Calcualte insights about frames to be shown on cookies landing page. @@ -40,22 +37,24 @@ export default function prepareCookieDataMapping( ): DataMapping[] { return [ { - title: 'Total cookies', + title: I18n.getMessage('totalCookies'), count: cookieStats.total, data: cookiesStatsComponents.legend, onClick: () => selectedItemUpdater('', 'isFirstParty'), // title is empty as we don't want to select any filter. }, { - title: '1st party cookies', + title: I18n.getMessage('firstPartyCookies'), count: cookieStats.firstParty.total, data: cookiesStatsComponents.firstParty, - onClick: () => selectedItemUpdater('First Party', 'isFirstParty'), + onClick: () => + selectedItemUpdater(I18n.getMessage('firstParty'), 'isFirstParty'), }, { - title: '3rd party cookies', + title: I18n.getMessage('thirdPartyCookies'), count: cookieStats.thirdParty.total, data: cookiesStatsComponents.thirdParty, - onClick: () => selectedItemUpdater('Third Party', 'isFirstParty'), + onClick: () => + selectedItemUpdater(I18n.getMessage('thirdParty'), 'isFirstParty'), }, ]; } diff --git a/packages/design-system/src/utils/prepareCookieStatsComponents.ts b/packages/design-system/src/utils/prepareCookieStatsComponents.ts index 61ce8ecd9..bd88e78ca 100644 --- a/packages/design-system/src/utils/prepareCookieStatsComponents.ts +++ b/packages/design-system/src/utils/prepareCookieStatsComponents.ts @@ -16,10 +16,8 @@ /** * External dependencies. */ -import type { - CookieStatsComponents, - CookiesCount, -} from '@ps-analysis-tool/common'; +import type { CookieStatsComponents, CookiesCount } from '@google-psat/common'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. */ @@ -48,14 +46,14 @@ const prepareCookieStatsComponents = ( blockedCookiesStats.push({ count: cookieStats.blockedCookies[key], - color: COLOR_MAP[key].color, + color: COLOR_MAP[key]?.color ?? '#E59500', }); blockedCookiesLegend.push({ label: key, count: cookieStats.blockedCookies[key], - color: COLOR_MAP[key].color, - countClassName: COLOR_MAP[key].className, + color: COLOR_MAP[key]?.color ?? '#E59500', + countClassName: COLOR_MAP[key]?.className ?? 'text-jamboo', }); }); @@ -69,42 +67,46 @@ const prepareCookieStatsComponents = ( exemptedCookiesStats.push({ count: cookieStats.exemptedCookies[key], - color: COLOR_MAP[key].color, + color: COLOR_MAP[key]?.color ?? '#E59500', }); exemptedCookiesLegend.push({ label: key, count: cookieStats.exemptedCookies[key], - color: COLOR_MAP[key].color, - countClassName: COLOR_MAP[key].className, + color: COLOR_MAP[key]?.color ?? '#E59500', + countClassName: COLOR_MAP[key]?.className ?? 'text-jamboo', }); }); return { legend: [ { - label: 'Functional', + label: I18n.getMessage('functional'), + descriptionKey: 'Functional', count: cookieStats.firstParty.functional + cookieStats.thirdParty.functional, color: COLOR_MAP.functional.color, countClassName: COLOR_MAP.functional.className, }, { - label: 'Marketing', + label: I18n.getMessage('marketing'), + descriptionKey: 'Marketing', count: cookieStats.firstParty.marketing + cookieStats.thirdParty.marketing, color: COLOR_MAP.marketing.color, countClassName: COLOR_MAP.uncategorized.className, }, { - label: 'Analytics', + label: I18n.getMessage('analytics'), + descriptionKey: 'Analytics', count: cookieStats.firstParty.analytics + cookieStats.thirdParty.analytics, color: COLOR_MAP.analytics.color, countClassName: COLOR_MAP.uncategorized.className, }, { - label: 'Uncategorized', + label: I18n.getMessage('uncategorized'), + descriptionKey: 'Uncategorized', count: cookieStats.firstParty.uncategorized + cookieStats.thirdParty.uncategorized, diff --git a/packages/design-system/src/utils/prepareCookiesCount.ts b/packages/design-system/src/utils/prepareCookiesCount.ts index 41c1b57f3..d4bc5a499 100644 --- a/packages/design-system/src/utils/prepareCookiesCount.ts +++ b/packages/design-system/src/utils/prepareCookiesCount.ts @@ -17,7 +17,7 @@ /** * External dependencies. */ -import type { CookieData, CookiesCount } from '@ps-analysis-tool/common'; +import type { CookieData, CookiesCount } from '@google-psat/common'; /** * Categorize cookies count into 1st party and 3rd party cookies and then into functional, marketing, analytics and uncategorized. @@ -54,13 +54,17 @@ const prepareCookiesCount = (cookies: { [key: string]: CookieData } | null) => { } const cookieList = Object.values(cookies).filter( - (cookie) => cookie.parsedCookie && cookie.frameIdList?.length >= 0 + (cookie) => + cookie.parsedCookie && + cookie.frameIdList && + cookie.frameIdList?.length >= 0 ); cookiesCount.total = Object.keys(cookies).filter( (cookieKey) => cookies[cookieKey].parsedCookie && - cookies[cookieKey].frameIdList?.length >= 0 + //@ts-ignore + cookies[cookieKey].frameIdList.length >= 0 ).length; cookiesCount.blockedCookies.total = cookieList.filter( diff --git a/packages/design-system/src/utils/prepareFrameStatsComponent.ts b/packages/design-system/src/utils/prepareFrameStatsComponent.ts index b3b19b7b1..2b676fd11 100644 --- a/packages/design-system/src/utils/prepareFrameStatsComponent.ts +++ b/packages/design-system/src/utils/prepareFrameStatsComponent.ts @@ -16,7 +16,8 @@ /** * External dependencies */ -import type { TabCookies, TabFrames } from '@ps-analysis-tool/common'; +import type { TabCookies, TabFrames } from '@google-psat/common'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies @@ -49,7 +50,7 @@ export default function prepareFrameStatsComponent( let hasUnblockedCookie = false; let hasCookie = false; - tabFrames[frame]?.frameIds?.forEach((frameId: number) => { + tabFrames[frame]?.frameIds?.forEach((frameId: string) => { Object.values(tabCookies || {}).forEach((cookie) => { if ( cookie?.parsedCookie && @@ -84,7 +85,7 @@ export default function prepareFrameStatsComponent( return { dataMapping: [ { - title: 'Frames', + title: I18n.getMessage('frames'), count: Object.keys(tabFrames || {}).length, data: [ { @@ -114,31 +115,36 @@ export default function prepareFrameStatsComponent( ], legend: [ { - label: 'Total frames', + label: I18n.getMessage('totalFrames'), + descriptionKey: 'Total frames', count: Object.keys(tabFrames || {}).length, color: '#25ACAD', countClassName: 'text-greenland-green', }, { - label: 'Frames with cookies', + label: I18n.getMessage('framesWithCookies'), + descriptionKey: 'Frames with cookies', count: cookieFrame.size, color: '#C5A06A', countClassName: 'text-good-life', }, { - label: 'Frames with blocked cookies', + label: I18n.getMessage('framesWithBlockedCookies'), + descriptionKey: 'Frames with blocked cookies', count: blockedCookieFrame.size, color: '#AF7AA3', countClassName: 'text-victorian-violet', }, { - label: 'Frames with unblocked cookies', + label: I18n.getMessage('framesWithUnblockedCookies'), + descriptionKey: 'Frames with unblocked cookies', count: unBlockedCookieFrame.size, color: '#F54021', countClassName: 'text-strawberry-spinach-red', }, { - label: 'Fenced frames', + label: I18n.getMessage('fencedFrames'), + descriptionKey: 'Fenced frames', count: Object.values(tabFrames || {}).filter( (frame) => frame?.frameType === 'fenced_frame' ).length, diff --git a/packages/design-system/tsconfig.json b/packages/design-system/tsconfig.json index 9ab65ef3b..6c600a345 100644 --- a/packages/design-system/tsconfig.json +++ b/packages/design-system/tsconfig.json @@ -10,8 +10,16 @@ "sourceMap": true, "esModuleInterop": true, "moduleResolution": "node", - "jsx": "preserve" + "jsx": "preserve", + "noImplicitAny": true, + "strictNullChecks": true }, + "exclude": [ + "**/tests/**/*.ts", + "**/tests/**/*.tsx", + "**/dist/**/*", + "**/dist-types/**/*" + ], "references": [ { "path": "../common" diff --git a/packages/eslint-import-resolver/README.md b/packages/eslint-import-resolver/README.md index 1f38ed3b5..87f4b33cb 100644 --- a/packages/eslint-import-resolver/README.md +++ b/packages/eslint-import-resolver/README.md @@ -6,7 +6,7 @@ Custom resolver for eslint-plugin-import to resolve packages locally in a monore { "settings": { "import/resolver": { - "@ps-analysis-tool/eslint-import-resolver": { + "@google-psat/eslint-import-resolver": { "mapping": { "^@foo\\/(.*)": "./packages/$1/src/", } diff --git a/packages/eslint-import-resolver/package.json b/packages/eslint-import-resolver/package.json index e4f475841..2a82836ee 100644 --- a/packages/eslint-import-resolver/package.json +++ b/packages/eslint-import-resolver/package.json @@ -1,15 +1,21 @@ { - "name": "@ps-analysis-tool/eslint-import-resolver", - "version": "0.8.0", + "name": "@google-psat/eslint-import-resolver", + "version": "0.9.0", "description": "", "main": "src/index.cjs", "scripts": { + "publish:local": "", + "publish:remote": "", + "unpublish:local": "", + "unpublish:remote": "" }, "repository": { "type": "git", "url": "git+https://github.com/GoogleChromeLabs/ps-analysis-tool" }, - "author": "", + "author": { + "name": "Google" + }, "license": "Apache-2.0", "bugs": { "url": "https://github.com/GoogleChromeLabs/ps-analysis-tool/issues" diff --git a/packages/extension/babel.config.cjs b/packages/extension/babel.config.cjs new file mode 100644 index 000000000..6ab27bceb --- /dev/null +++ b/packages/extension/babel.config.cjs @@ -0,0 +1,36 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +module.exports = function (api) { + const isProduction = api.env('production'); + + return { + presets: [ + ['@babel/preset-env'], + [ + '@babel/preset-react', + { + development: !isProduction, + }, + ], + '@babel/preset-typescript', + ], + plugins: [ + ['@babel/plugin-transform-react-jsx'], + ['babel-plugin-styled-components'], + ], + sourceMaps: true, + }; +}; diff --git a/packages/extension/package.json b/packages/extension/package.json index 1ee0ba3f2..45279421c 100644 --- a/packages/extension/package.json +++ b/packages/extension/package.json @@ -1,23 +1,35 @@ { - "name": "@ps-analysis-tool/extension", - "version": "0.8.0", + "name": "@google-psat/extension", + "version": "0.9.0", "description": "Chrome extension for cookie analysis", "repository": { "type": "git", "url": "git+https://github.com/GoogleChromeLabs/ps-analysis-tool.git" }, "license": "Apache-2.0", - "scripts": {}, + "scripts": { + "dev": "webpack --config ../../extension.webpack.config.cjs --watch", + "build": "npm run build:remove && webpack --config ../../extension.webpack.config.cjs", + "build:remove": "rimraf ../../dist/extension", + "publish:local": "", + "publish:remote": "", + "unpublish:local": "", + "unpublish:remote": "" + }, "bugs": { "url": "https://github.com/GoogleChromeLabs/ps-analysis-tool/issues" }, + "author": { + "name": "Google" + }, "homepage": "https://github.com/GoogleChromeLabs/ps-analysis-tool#readme", "dependencies": { "@floating-ui/core": "^1.5.0", "@floating-ui/dom": "^1.5.3", - "@ps-analysis-tool/common": "*", - "@ps-analysis-tool/design-system": "*", - "@ps-analysis-tool/library-detection": "*", + "@google-psat/common": "*", + "@google-psat/design-system": "*", + "@google-psat/i18n": "*", + "@google-psat/library-detection": "*", "classnames": "^2.3.2", "fast-xml-parser": "^4.3.2", "file-saver": "^2.0.5", diff --git a/packages/extension/src/constants.ts b/packages/extension/src/constants.ts index 7034e045d..ef254c5f3 100644 --- a/packages/extension/src/constants.ts +++ b/packages/extension/src/constants.ts @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { I18n } from '@google-psat/i18n'; + export const ALLOWED_NUMBER_OF_TABS = 1; export const WEBPAGE_PORT_NAME = 'psat-webpage'; export const SERVICE_WORKER_PORT_NAME = 'psat-serviceworker'; @@ -20,15 +22,21 @@ export const SERVICE_WORKER_PORT_NAME = 'psat-serviceworker'; export const SETTING_PAGE_CONTROLS = [ { id: 'enableCDP', - heading: 'Enable CDP', - description: - 'The Chrome DevTools Protocol allows for tools to instrument, inspect, debug and profile Chromium, Chrome and other Blink-based browsers. Learn More.', + heading: () => I18n.getMessage('enableCDP'), + description: () => + I18n.getMessage('enableCDPNote', [ + ``, + '', + ]), }, { id: 'multitabDebugging', - heading: 'Multitab Debugging', - description: - "The PSAT tool is designed for efficient single-tab analysis. While multi-tab debugging is available for more comprehensive analysis, it is intended for examining 2-3 tabs simultaneously. Using more tabs may impact the tool's responsiveness.", + heading: () => I18n.getMessage('multitabDebugging'), + description: () => + I18n.getMessage('multitabDebuggingNote', [ + ``, + '', + ]), }, ]; diff --git a/packages/extension/src/contentScript/index.ts b/packages/extension/src/contentScript/index.ts index 94bc6d7e5..ba0f4255b 100644 --- a/packages/extension/src/contentScript/index.ts +++ b/packages/extension/src/contentScript/index.ts @@ -22,9 +22,10 @@ import { findAnalyticsMatch, calculateEffectiveExpiryDate, type CookieDatabase, -} from '@ps-analysis-tool/common'; +} from '@google-psat/common'; import { computePosition, flip, shift } from '@floating-ui/core'; import { autoUpdate, platform, arrow } from '@floating-ui/dom'; + /** * Internal dependencies. */ @@ -67,6 +68,11 @@ class WebpageContentScript { */ tabId: number | null = null; + /** + * Main frame id. + */ + frameId: string | null = null; + /** * TabId of the current Tab */ @@ -152,6 +158,7 @@ class WebpageContentScript { if (message?.payload?.type === TABID_STORAGE) { this.tabId = message.payload.tabId; + this.frameId = message.payload.frameId; } if (message?.payload?.type === GET_JS_COOKIES) { @@ -178,11 +185,16 @@ class WebpageContentScript { */ async getAndProcessJSCookies(tabId: string) { try { + if (!this.frameId) { + return; + } + //@ts-ignore const jsCookies = await cookieStore?.getAll(); await processAndStoreDocumentCookies({ tabUrl: window.location.href, tabId, + frameId: this.frameId, documentCookies: jsCookies, cookieDB: this.cookieDB ?? {}, }); @@ -264,7 +276,7 @@ class WebpageContentScript { analytics: findAnalyticsMatch(cookie?.name, this.cookieDB), url: window.location.href, headerType: 'javascript', - frameIdList: [0], + frameIdList: [this.frameId?.toString() ?? '0'], blockedReasons: [], warningReasons: [], isBlocked: false, diff --git a/packages/extension/src/contentScript/popovers/tooltip/createShowMoreButton.ts b/packages/extension/src/contentScript/popovers/tooltip/createShowMoreButton.ts index cde3a7ca4..1d4b725c6 100644 --- a/packages/extension/src/contentScript/popovers/tooltip/createShowMoreButton.ts +++ b/packages/extension/src/contentScript/popovers/tooltip/createShowMoreButton.ts @@ -13,6 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/** + * External dependencies. + */ +import { I18n } from '@google-psat/i18n'; const createShowMoreButton = (): HTMLElement => { const tooltipShowButton: HTMLButtonElement = document.createElement('button'); @@ -27,7 +31,7 @@ const createShowMoreButton = (): HTMLElement => { 'ps-tooltip-info-toggle-btn', 'ps-tooltip-compact' ); - tooltipShowButton.innerText = 'Show more'; + tooltipShowButton.innerText = I18n.getMessage('showMore'); tooltipShowButton.onclick = (event) => { const showMoreButton = event.target as HTMLElement; @@ -49,7 +53,7 @@ const createShowMoreButton = (): HTMLElement => { const isCompact = showMoreButton.classList.contains('ps-tooltip-compact'); if (isCompact) { - showMoreButton.innerText = 'Show less'; + showMoreButton.innerText = I18n.getMessage('showLess'); showMoreButton.classList.remove('ps-tooltip-compact'); const hiddenElements = tooltipContainer.querySelectorAll('.hidden'); hiddenElements.forEach((element) => { @@ -57,7 +61,7 @@ const createShowMoreButton = (): HTMLElement => { }); allowedFeatures.innerText = expandedAllowedFeatures ?? 'N/A'; } else { - showMoreButton.innerText = 'Show more'; + showMoreButton.innerText = I18n.getMessage('showMore'); showMoreButton.classList.add('ps-tooltip-compact'); const compactViewHiddenElements = tooltipContainer.querySelectorAll('.ps-compact'); diff --git a/packages/extension/src/contentScript/popovers/tooltip/createTooltip.ts b/packages/extension/src/contentScript/popovers/tooltip/createTooltip.ts index 8cba06561..3efe1e944 100644 --- a/packages/extension/src/contentScript/popovers/tooltip/createTooltip.ts +++ b/packages/extension/src/contentScript/popovers/tooltip/createTooltip.ts @@ -13,6 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/** + * External dependencies. + */ +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. */ @@ -81,13 +85,21 @@ const createTooltip = ( } const infoData = getTooltipInfoData( - getFrameType(isHidden, insideFrame, frame ? frame.tagName : 'Unknown'), + getFrameType( + isHidden, + insideFrame, + frame ? frame.tagName : I18n.getMessage('unknown') + ), origin ?? '', numberOfVisibleFrames, numberOfHiddenFrames, data?.firstPartyCookies || 0, data?.thirdPartyCookies || 0, - origin ? (data?.isOnRWS ? 'Yes' : 'No') : 'N/A', + origin + ? data?.isOnRWS + ? I18n.getMessage('yes') + : I18n.getMessage('no') + : 'N/A', allowedFeatured, DISPLAY_SHOW_MORE_BUTTON, data?.blockedCookies || 0, diff --git a/packages/extension/src/contentScript/popovers/tooltip/tests/createShowMoreButton.ts b/packages/extension/src/contentScript/popovers/tooltip/tests/createShowMoreButton.ts index 0294bfb24..31a32fbe8 100644 --- a/packages/extension/src/contentScript/popovers/tooltip/tests/createShowMoreButton.ts +++ b/packages/extension/src/contentScript/popovers/tooltip/tests/createShowMoreButton.ts @@ -16,6 +16,7 @@ /** * External dependencies. */ +import { I18n } from '@google-psat/i18n'; import '@testing-library/jest-dom'; /** @@ -24,6 +25,19 @@ import '@testing-library/jest-dom'; import createShowMoreButton from '../createShowMoreButton'; describe('createShowMoreButton', () => { + beforeAll(() => { + globalThis.chrome.i18n = null; + + I18n.initMessages({ + showMore: { + message: 'Show more', + }, + showLess: { + message: 'Show less', + }, + }); + }); + it('should contain "Show More" button components', () => { // Get HTML element from the function being tested. const tooltipShowButtonContainer = createShowMoreButton(); diff --git a/packages/extension/src/contentScript/popovers/tooltip/tests/createToolTip.ts b/packages/extension/src/contentScript/popovers/tooltip/tests/createToolTip.ts index 82776b1ad..a8410b46b 100644 --- a/packages/extension/src/contentScript/popovers/tooltip/tests/createToolTip.ts +++ b/packages/extension/src/contentScript/popovers/tooltip/tests/createToolTip.ts @@ -17,6 +17,7 @@ * External dependencies. */ import '@testing-library/jest-dom'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. @@ -24,6 +25,25 @@ import '@testing-library/jest-dom'; import createTooltip from '../createTooltip'; describe('createTooltip', () => { + beforeAll(() => { + globalThis.chrome.i18n = null; + + I18n.initMessages({ + hiddenIframe: { + message: 'Hidden iframe', + }, + nestedIframe: { + message: 'Nested iframe', + }, + mainFrame: { + message: 'Main frame', + }, + unknown: { + message: 'Unknown', + }, + }); + }); + it('should be an instance of HTMLDivElement', () => { const response = { isInspecting: true, diff --git a/packages/extension/src/contentScript/utils/getAllowedFeatures.ts b/packages/extension/src/contentScript/utils/getAllowedFeatures.ts index 59ddca48b..ed6e66a90 100644 --- a/packages/extension/src/contentScript/utils/getAllowedFeatures.ts +++ b/packages/extension/src/contentScript/utils/getAllowedFeatures.ts @@ -13,6 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/** + * External dependencies. + */ +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. */ @@ -20,7 +24,7 @@ import { PS_RELATED_ALLOWED_FEATURES } from '../constants'; const getAllowedFeatures = (iframe: HTMLElement) => { if (!iframe || !iframe?.featurePolicy) { - return 'Unknown'; + return I18n.getMessage('unknown'); } const featurePolicy = iframe?.featurePolicy; diff --git a/packages/extension/src/contentScript/utils/getFrameType.ts b/packages/extension/src/contentScript/utils/getFrameType.ts index 3993d793f..9c08a5129 100644 --- a/packages/extension/src/contentScript/utils/getFrameType.ts +++ b/packages/extension/src/contentScript/utils/getFrameType.ts @@ -13,28 +13,31 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import { I18n } from '@google-psat/i18n'; + const getFrameType = ( isHidden: boolean, insideFrame: HTMLIFrameElement | null, tagName: string ): string => { if (isHidden) { - return 'Hidden iframe'; + return I18n.getMessage('hiddenIframe'); } if (insideFrame) { - return 'Nested iframe'; + return I18n.getMessage('nestedIframe'); } if (tagName === 'BODY') { - return 'Main frame'; + return I18n.getMessage('mainFrame'); } if (tagName === 'Unknown') { - return 'Unknown'; + return I18n.getMessage('unknown'); } - return 'iframe'; + return I18n.getMessage('iframe'); }; export default getFrameType; diff --git a/packages/extension/src/contentScript/utils/getTooltipInfoData.ts b/packages/extension/src/contentScript/utils/getTooltipInfoData.ts index 65477f404..2af3610db 100644 --- a/packages/extension/src/contentScript/utils/getTooltipInfoData.ts +++ b/packages/extension/src/contentScript/utils/getTooltipInfoData.ts @@ -16,6 +16,7 @@ /** * Internal Dependencies. */ +import { I18n } from '@google-psat/i18n'; import { NUMBER_OF_ALLOWED_FEATURES_IN_COMPACT_VIEW } from '../constants'; const getTooltipInfoData = ( @@ -81,8 +82,7 @@ const getTooltipInfoData = ( : allowedFeatureInExpandedView; if (frameType === 'Unknown') { - info['Note'] = - 'We are unable to detect this frame as it may be an iframe nested inside one or more iframes.'; + info['Note'] = I18n.getMessage('unknownFrameNote'); } else { info['Belongs to RWS'] = belongsToRWS; attr['allowedFeaturesInCompactView'] = allowedFeaturesInCompactView; diff --git a/packages/extension/src/contentScript/utils/tests/getAllowedFeature.ts b/packages/extension/src/contentScript/utils/tests/getAllowedFeature.ts index 32124a179..9ba343759 100644 --- a/packages/extension/src/contentScript/utils/tests/getAllowedFeature.ts +++ b/packages/extension/src/contentScript/utils/tests/getAllowedFeature.ts @@ -17,6 +17,7 @@ * External dependencies. */ import '@testing-library/jest-dom'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. @@ -43,6 +44,14 @@ describe('getAllowedFeatures', () => { return {}; }); + + globalThis.chrome.i18n = null; + + I18n.initMessages({ + unknown: { + message: 'Unknown', + }, + }); }); it('should return array of PS related allowed features', () => { diff --git a/packages/extension/src/contentScript/utils/tests/getFrameType.ts b/packages/extension/src/contentScript/utils/tests/getFrameType.ts index cb909338f..86eeb9e9c 100644 --- a/packages/extension/src/contentScript/utils/tests/getFrameType.ts +++ b/packages/extension/src/contentScript/utils/tests/getFrameType.ts @@ -17,6 +17,7 @@ * External dependencies. */ import '@testing-library/jest-dom'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. @@ -24,6 +25,25 @@ import '@testing-library/jest-dom'; import getFrameType from '../getFrameType'; describe('getFrameType', () => { + beforeAll(() => { + globalThis.chrome.i18n = null; + + I18n.initMessages({ + hiddenIframe: { + message: 'Hidden iframe', + }, + nestedIframe: { + message: 'Nested iframe', + }, + mainFrame: { + message: 'Main frame', + }, + iframe: { + message: 'iframe', + }, + }); + }); + it('should return frame type (Hidden iframe)', () => { const iframe = document.createElement('iframe'); diff --git a/packages/extension/src/manifest.json b/packages/extension/src/manifest.json index 651068567..43d551640 100644 --- a/packages/extension/src/manifest.json +++ b/packages/extension/src/manifest.json @@ -1,8 +1,9 @@ { "name": "Privacy Sandbox Analysis Tool", "description": "Tooling for understanding cookie usage and guidance on new privacy-preserving Chrome APIs.", - "version": "0.8.0", + "version": "0.9.0", "manifest_version": 3, + "default_locale": "en", "icons": { "16": "icons/icon-16.png", "32": "icons/icon-32.png", diff --git a/packages/extension/src/serviceWorker/attachCDP.ts b/packages/extension/src/serviceWorker/attachCDP.ts new file mode 100644 index 000000000..55597a9df --- /dev/null +++ b/packages/extension/src/serviceWorker/attachCDP.ts @@ -0,0 +1,39 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This function will attach the debugger to the given target. + * @param {{ [key: string]: number | string }} target The target where debugger needs to be attached. + */ +export default async function attachCDP(target: { + [key: string]: number | string; +}) { + try { + await chrome.debugger.attach(target, '1.3'); + await chrome.debugger.sendCommand(target, 'Target.setAutoAttach', { + // If this is set to true, debugger will be attached to every new target that is added to the current target. + autoAttach: true, + waitForDebuggerOnStart: false, + //Enables "flat" access to the session via specifying sessionId attribute in the commands. + // If this is set to true the debugger is also attached to the child targets of that the target it has been attached to. + flatten: true, + }); + await chrome.debugger.sendCommand(target, 'Network.enable'); + await chrome.debugger.sendCommand(target, 'Audits.enable'); + await chrome.debugger.sendCommand(target, 'Page.enable'); + } catch (error) { + //Fail silently + } +} diff --git a/packages/extension/src/serviceWorker/chromeListeners/beforeSendHeadersListener.ts b/packages/extension/src/serviceWorker/chromeListeners/beforeSendHeadersListener.ts new file mode 100644 index 000000000..775e7414c --- /dev/null +++ b/packages/extension/src/serviceWorker/chromeListeners/beforeSendHeadersListener.ts @@ -0,0 +1,63 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Internal dependencies + */ +import parseHeaders from '../../utils/parseHeaders'; +import synchnorousCookieStore from '../../store/synchnorousCookieStore'; +import { getTab } from '../../utils/getTab'; + +export const onBeforeSendHeadersListener = ({ + url, + requestHeaders, + tabId, + frameId, + requestId, +}: chrome.webRequest.WebRequestHeadersDetails) => { + if (synchnorousCookieStore.globalIsUsingCDP) { + return; + } + + (async () => { + const tab = await getTab(tabId); + let tabUrl = synchnorousCookieStore?.getTabUrl(tabId); + + if (tab && tab.pendingUrl) { + tabUrl = tab.pendingUrl; + } + + const cookies = await parseHeaders( + synchnorousCookieStore.globalIsUsingCDP, + 'request', + synchnorousCookieStore.tabToRead, + synchnorousCookieStore.tabMode, + tabId, + url, + synchnorousCookieStore.cookieDB ?? {}, + tabUrl, + frameId.toString(), + requestId, + requestHeaders + ); + + if (!cookies || (cookies && cookies?.length === 0)) { + return; + } + + // Adds the cookies from the request headers to the cookies object. + synchnorousCookieStore?.update(tabId, cookies); + })(); +}; diff --git a/packages/extension/src/serviceWorker/chromeListeners/index.ts b/packages/extension/src/serviceWorker/chromeListeners/index.ts new file mode 100644 index 000000000..2e506721e --- /dev/null +++ b/packages/extension/src/serviceWorker/chromeListeners/index.ts @@ -0,0 +1,104 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Internal dependencies. + */ +import { onBeforeSendHeadersListener } from './beforeSendHeadersListener'; +import { onResponseStartedListener } from './onResponseStartedListener'; +import { onStartUpListener } from './runtimeStartUpListener'; +import { runtimeOnInstalledListener } from './runtimeOnInstalledListener'; +import { runtimeOnMessageListener } from './runtimeOnMessageListener'; +import { + onSyncStorageChangedListenerForMultiTab, + onSyncStorageChangedListenerForCDP, +} from './syncStorageOnChangedListener'; +import { onTabCreatedListener } from './tabOnCreatedListener'; +import { onTabRemovedListener } from './tabOnRemovedListener'; +import { onTabUpdatedListener } from './tabsOnUpdatedListener'; +import { windowsOnRemovedListener } from './windowsOnRemovedListener'; +import { windowsOnCreatedListener } from './windowsOnCreatedListener'; + +/** + * Fires before sending an HTTP request, once the request headers are available. + * @see https://developer.chrome.com/docs/extensions/reference/api/webRequest#event-onBeforeSendHeaders + */ +chrome.webRequest.onBeforeSendHeaders.addListener( + onBeforeSendHeadersListener, + { urls: ['*://*/*'] }, + ['extraHeaders', 'requestHeaders'] +); + +/** + * Fires when the browser receives a response from a web server. + * @see https://developer.chrome.com/docs/extensions/reference/api/webRequest + */ +chrome?.webRequest?.onResponseStarted?.addListener( + onResponseStartedListener, + { urls: ['*://*/*'] }, + ['extraHeaders', 'responseHeaders'] +); + +chrome.tabs.onCreated.addListener(onTabCreatedListener); + +/** + * Fires when a profile with extension is started. + * @see https://developer.chrome.com/docs/extensions/reference/api/runtime#event-onStartup + */ +chrome.runtime.onStartup.addListener(onStartUpListener); + +/** + * Fires when the extension is first installed, + * when clicked on the extension refresh button from chrome://extensions/ + * when the extension is updated to a new version, + * when Chrome is updated to a new version. + * @see https://developer.chrome.com/docs/extensions/reference/api/runtime#event-onInstalled + */ +chrome.runtime.onInstalled.addListener(runtimeOnInstalledListener); + +/** + * Fires when a message is sent from either an extension process (by runtime.sendMessage) or a content script (by tabs.sendMessage). + * @see https://developer.chrome.com/docs/extensions/reference/api/runtime#event-onMessage + */ +chrome.runtime.onMessage.addListener(runtimeOnMessageListener); + +/** + * Fires when a tab is closed. + * @see https://developer.chrome.com/docs/extensions/reference/api/tabs#event-onRemoved + */ +chrome.tabs.onRemoved.addListener(onTabRemovedListener); + +/** + * Fires when a tab is updated. + * @see https://developer.chrome.com/docs/extensions/reference/api/tabs#event-onUpdated + */ +chrome.tabs.onUpdated.addListener(onTabUpdatedListener); + +/** + * Fires when a window is removed (closed). + * @see https://developer.chrome.com/docs/extensions/reference/api/windows#event-onRemoved + */ +chrome.windows.onRemoved.addListener(windowsOnRemovedListener); + +/** + * Fires when the browser window is opened. + * @see https://developer.chrome.com/docs/extensions/reference/api/windows#event-onCreated + */ +chrome.windows.onCreated.addListener(windowsOnCreatedListener); + +chrome.storage.sync.onChanged.addListener(onSyncStorageChangedListenerForCDP); +chrome.storage.sync.onChanged.addListener( + onSyncStorageChangedListenerForMultiTab +); diff --git a/packages/extension/src/serviceWorker/chromeListeners/onResponseStartedListener.ts b/packages/extension/src/serviceWorker/chromeListeners/onResponseStartedListener.ts new file mode 100644 index 000000000..de8c8d9f7 --- /dev/null +++ b/packages/extension/src/serviceWorker/chromeListeners/onResponseStartedListener.ts @@ -0,0 +1,63 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Internal dependencies + */ +import parseHeaders from '../../utils/parseHeaders'; +import synchnorousCookieStore from '../../store/synchnorousCookieStore'; +import { getTab } from '../../utils/getTab'; + +export const onResponseStartedListener = ({ + tabId, + url, + responseHeaders, + frameId, + requestId, +}: chrome.webRequest.WebResponseCacheDetails) => { + if (synchnorousCookieStore.globalIsUsingCDP) { + return; + } + + (async () => { + const tab = await getTab(tabId); + let tabUrl = synchnorousCookieStore?.getTabUrl(tabId); + + if (tab && tab.pendingUrl) { + tabUrl = tab.pendingUrl; + } + + const cookies = await parseHeaders( + synchnorousCookieStore.globalIsUsingCDP, + 'response', + synchnorousCookieStore.tabToRead, + synchnorousCookieStore.tabMode, + tabId, + url, + synchnorousCookieStore.cookieDB ?? {}, + tabUrl, + frameId.toString(), + requestId, + responseHeaders + ); + + if (!cookies || (cookies && cookies?.length === 0)) { + return; + } + + // Adds the cookies from the request headers to the cookies object. + synchnorousCookieStore?.update(tabId, cookies); + })(); +}; diff --git a/packages/extension/src/serviceWorker/chromeListeners/runtimeOnInstalledListener.ts b/packages/extension/src/serviceWorker/chromeListeners/runtimeOnInstalledListener.ts new file mode 100644 index 000000000..58969f519 --- /dev/null +++ b/packages/extension/src/serviceWorker/chromeListeners/runtimeOnInstalledListener.ts @@ -0,0 +1,131 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Internal dependencies + */ +import synchnorousCookieStore from '../../store/synchnorousCookieStore'; +import { getAndParseNetworkCookies } from '../../utils/getAndParseNetworkCookies'; +import attachCDP from '../attachCDP'; + +export const runtimeOnInstalledListener = async ( + details: chrome.runtime.InstalledDetails +) => { + synchnorousCookieStore?.clear(); + + // @see https://developer.chrome.com/blog/longer-esw-lifetimes#whats_changed + // Doing this to keep the service worker alive so that we dont loose any data and introduce any bug. + setInterval(async () => { + await chrome.storage.session.get(); + }, 20000); + + // @todo Send tab data of the active tab only, also if sending only the difference would make it any faster. + setInterval(() => { + if (Object.keys(synchnorousCookieStore?.tabsData ?? {}).length === 0) { + return; + } + + Object.keys(synchnorousCookieStore?.tabsData ?? {}).forEach((key) => { + synchnorousCookieStore?.sendUpdatedDataToPopupAndDevTools(Number(key)); + }); + }, 1200); + + // @todo Send tab data of the active tab only, also if sending only the difference would make it any faster. + setInterval(() => { + if (Object.keys(synchnorousCookieStore?.tabsData ?? {}).length === 0) { + return; + } + + Object.keys(synchnorousCookieStore?.tabsData ?? {}).forEach((key) => { + getAndParseNetworkCookies(key); + }); + }, 5000); + + if (details.reason === 'install') { + await chrome.storage.sync.clear(); + await chrome.storage.sync.set({ + allowedNumberOfTabs: 'single', + isUsingCDP: false, + }); + } + + if (details.reason === 'update') { + await chrome.storage.local.clear(); + const preSetSettings = await chrome.storage.sync.get(); + synchnorousCookieStore.tabMode = + preSetSettings?.allowedNumberOfTabs ?? 'single'; + synchnorousCookieStore.globalIsUsingCDP = + preSetSettings?.isUsingCDP ?? false; + + if (synchnorousCookieStore.tabMode === 'unlimited') { + const allTabs = await chrome.tabs.query({}); + const targets = await chrome.debugger.getTargets(); + await Promise.all( + allTabs.map(async (tab) => { + if (!tab.id || tab.url?.startsWith('chrome://')) { + return; + } + + synchnorousCookieStore?.addTabData(tab.id); + + if (synchnorousCookieStore.globalIsUsingCDP) { + synchnorousCookieStore.initialiseVariablesForNewTab( + tab.id.toString() + ); + + await attachCDP({ tabId: tab.id }); + + const currentTab = targets.filter( + ({ tabId }) => tabId && tab.id && tabId === tab.id + ); + synchnorousCookieStore?.updateParentChildFrameAssociation( + tab.id, + currentTab[0].id, + '0' + ); + } + }) + ); + } + + if ( + preSetSettings?.allowedNumberOfTabs && + Object.keys(preSetSettings).includes('isUsingCDP') + ) { + return; + } + + await chrome.storage.sync.clear(); + await chrome.storage.sync.set({ + allowedNumberOfTabs: 'single', + isUsingCDP: false, + }); + } +}; diff --git a/packages/extension/src/serviceWorker/chromeListeners/runtimeOnMessageListener.ts b/packages/extension/src/serviceWorker/chromeListeners/runtimeOnMessageListener.ts new file mode 100644 index 000000000..c849785c7 --- /dev/null +++ b/packages/extension/src/serviceWorker/chromeListeners/runtimeOnMessageListener.ts @@ -0,0 +1,194 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Internal dependencies + */ +import synchnorousCookieStore from '../../store/synchnorousCookieStore'; +import { + DEVTOOLS_CLOSE, + DEVTOOLS_OPEN, + DEVTOOLS_SET_JAVASCSCRIPT_COOKIE, + INITIAL_SYNC, + POPUP_CLOSE, + POPUP_OPEN, + SERVICE_WORKER_RELOAD_MESSAGE, + SERVICE_WORKER_TABS_RELOAD_COMMAND, + SET_TAB_TO_READ, +} from '../../constants'; +import listenToNewTab from '../../utils/listenToNewTab'; +import attachCDP from '../attachCDP'; +import reloadCurrentTab from '../../utils/reloadCurrentTab'; +import { getTab } from '../../utils/getTab'; +import sendMessageWrapper from '../../utils/sendMessageWrapper'; + +// eslint-disable-next-line complexity +export const runtimeOnMessageListener = async (request: any) => { + if (!request.type) { + return; + } + + const incomingMessageType = request.type; + + if (SET_TAB_TO_READ === incomingMessageType) { + synchnorousCookieStore.tabToRead = request?.payload?.tabId?.toString(); + const newTab = await listenToNewTab(request?.payload?.tabId); + // Can't use sendResponse as delay is too long. So using sendMessage instead. + await sendMessageWrapper(SET_TAB_TO_READ, { + tabId: Number(newTab), + }); + + if (synchnorousCookieStore.globalIsUsingCDP) { + await attachCDP({ tabId: Number(newTab) }); + } + + await reloadCurrentTab(Number(newTab)); + } + + if (SERVICE_WORKER_TABS_RELOAD_COMMAND === incomingMessageType) { + const sessionStorage = await chrome.storage.session.get(); + if (sessionStorage?.allowedNumberOfTabs) { + synchnorousCookieStore.tabMode = sessionStorage.allowedNumberOfTabs; + } + + if (Object.keys(sessionStorage).includes('isUsingCDP')) { + synchnorousCookieStore.globalIsUsingCDP = sessionStorage.isUsingCDP; + } + + await chrome.storage.session.remove(['allowedNumberOfTabs', 'isUsingCDP']); + + await chrome.storage.session.set({ + pendingReload: false, + }); + + await chrome.storage.sync.set({ + allowedNumberOfTabs: synchnorousCookieStore.tabMode, + isUsingCDP: synchnorousCookieStore.globalIsUsingCDP, + }); + + const tabs = await chrome.tabs.query({}); + const targets = await chrome.debugger.getTargets(); + await Promise.all( + tabs.map(async ({ id }) => { + if (!id) { + return; + } + + if (synchnorousCookieStore.tabMode === 'unlimited') { + synchnorousCookieStore.initialiseVariablesForNewTab(id.toString()); + + const currentTab = targets.filter( + ({ tabId }) => tabId && id && tabId === id + ); + synchnorousCookieStore?.addTabData(id); + synchnorousCookieStore?.updateParentChildFrameAssociation( + id, + currentTab[0].id, + '0' + ); + } + try { + if (synchnorousCookieStore.globalIsUsingCDP) { + await attachCDP({ tabId: id }); + } else { + await chrome.debugger.detach({ tabId: id }); + } + } catch (error) { + //Fail silently + } + + await reloadCurrentTab(id); + }) + ); + await sendMessageWrapper(SERVICE_WORKER_RELOAD_MESSAGE); + } + + if (!request?.payload?.tabId) { + return; + } + + const incomingMessageTabId = request.payload.tabId; + + if (DEVTOOLS_OPEN === incomingMessageType) { + const dataToSend: { [key: string]: string | boolean } = {}; + dataToSend['tabMode'] = synchnorousCookieStore.tabMode; + + if (synchnorousCookieStore.tabMode === 'single') { + dataToSend['tabToRead'] = synchnorousCookieStore.tabToRead; + } + + if ( + !synchnorousCookieStore?.tabs[incomingMessageTabId] && + synchnorousCookieStore.tabMode === 'unlimited' + ) { + const currentTab = await getTab(incomingMessageTabId); + dataToSend['psatOpenedAfterPageLoad'] = request.payload.doNotReReload + ? false + : true; + synchnorousCookieStore?.addTabData(incomingMessageTabId); + synchnorousCookieStore?.updateUrl( + incomingMessageTabId, + currentTab?.url || '' + ); + } + + await sendMessageWrapper(INITIAL_SYNC, dataToSend); + + synchnorousCookieStore?.updateDevToolsState(incomingMessageTabId, true); + + if (synchnorousCookieStore?.tabsData[incomingMessageTabId]) { + synchnorousCookieStore?.sendUpdatedDataToPopupAndDevTools( + incomingMessageTabId, + true + ); + } + } + + if (POPUP_OPEN === incomingMessageType) { + const dataToSend: { [key: string]: string } = {}; + dataToSend['tabMode'] = synchnorousCookieStore.tabMode; + + if (synchnorousCookieStore.tabMode === 'single') { + dataToSend['tabToRead'] = synchnorousCookieStore.tabToRead; + } + + await sendMessageWrapper(INITIAL_SYNC, dataToSend); + + synchnorousCookieStore?.updatePopUpState(incomingMessageTabId, true); + + if (synchnorousCookieStore?.tabsData[incomingMessageTabId]) { + synchnorousCookieStore?.sendUpdatedDataToPopupAndDevTools( + incomingMessageTabId, + true + ); + } + } + + if (DEVTOOLS_CLOSE === incomingMessageType) { + synchnorousCookieStore?.updateDevToolsState(incomingMessageTabId, false); + } + + if (POPUP_CLOSE === incomingMessageType) { + synchnorousCookieStore?.updatePopUpState(incomingMessageTabId, false); + } + + if (DEVTOOLS_SET_JAVASCSCRIPT_COOKIE === incomingMessageType) { + synchnorousCookieStore?.update( + incomingMessageTabId, + request?.payload?.cookieData + ); + } +}; diff --git a/packages/extension/src/serviceWorker/chromeListeners/runtimeStartUpListener.ts b/packages/extension/src/serviceWorker/chromeListeners/runtimeStartUpListener.ts new file mode 100644 index 000000000..ea49d3614 --- /dev/null +++ b/packages/extension/src/serviceWorker/chromeListeners/runtimeStartUpListener.ts @@ -0,0 +1,60 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Internal dependencies + */ +import synchnorousCookieStore from '../../store/synchnorousCookieStore'; +import { getAndParseNetworkCookies } from '../../utils/getAndParseNetworkCookies'; + +export const onStartUpListener = async () => { + const storage = await chrome.storage.sync.get(); + + // @see https://developer.chrome.com/blog/longer-esw-lifetimes#whats_changed + // Doing this to keep the service worker alive so that we dont loose any data and introduce any bug. + setInterval(async () => { + await chrome.storage.session.get(); + }, 20000); + + // @todo Send tab data of the active tab only, also if sending only the difference would make it any faster. + setInterval(() => { + if (Object.keys(synchnorousCookieStore?.tabsData ?? {}).length === 0) { + return; + } + + Object.keys(synchnorousCookieStore?.tabsData ?? {}).forEach((key) => { + synchnorousCookieStore?.sendUpdatedDataToPopupAndDevTools(Number(key)); + }); + }, 1200); + + // @todo Send tab data of the active tab only, also if sending only the difference would make it any faster. + setInterval(() => { + if (Object.keys(synchnorousCookieStore?.tabsData ?? {}).length === 0) { + return; + } + + Object.keys(synchnorousCookieStore?.tabsData ?? {}).forEach((key) => { + getAndParseNetworkCookies(key); + }); + }, 5000); + + if (storage?.allowedNumberOfTabs) { + synchnorousCookieStore.tabMode = storage.allowedNumberOfTabs; + } + + if (Object.keys(storage).includes('isUsingCDP')) { + synchnorousCookieStore.globalIsUsingCDP = storage.isUsingCDP; + } +}; diff --git a/packages/extension/src/serviceWorker/chromeListeners/syncStorageOnChangedListener.ts b/packages/extension/src/serviceWorker/chromeListeners/syncStorageOnChangedListener.ts new file mode 100644 index 000000000..19f9ac0dc --- /dev/null +++ b/packages/extension/src/serviceWorker/chromeListeners/syncStorageOnChangedListener.ts @@ -0,0 +1,111 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Internal dependencies + */ +import { INITIAL_SYNC } from '../../constants'; +import synchnorousCookieStore from '../../store/synchnorousCookieStore'; +import resetCookieBadgeText from '../../store/utils/resetCookieBadgeText'; +import sendMessageWrapper from '../../utils/sendMessageWrapper'; + +export const onSyncStorageChangedListenerForMultiTab = async (changes: { + [key: string]: chrome.storage.StorageChange; +}) => { + if ( + !changes?.allowedNumberOfTabs || + !changes?.allowedNumberOfTabs?.newValue || + !changes?.allowedNumberOfTabs?.oldValue + ) { + return; + } + synchnorousCookieStore.tabMode = changes.allowedNumberOfTabs.newValue; + + const tabs = await chrome.tabs.query({}); + await sendMessageWrapper(INITIAL_SYNC, { + tabMode: synchnorousCookieStore.tabMode, + tabToRead: synchnorousCookieStore.tabToRead, + }); + + if (changes?.allowedNumberOfTabs?.newValue === 'single') { + synchnorousCookieStore.tabToRead = ''; + + tabs.map((tab) => { + if (!tab?.id) { + return tab; + } + + resetCookieBadgeText(tab.id); + + synchnorousCookieStore?.removeTabData(tab.id); + + return tab; + }); + } else { + tabs.forEach((tab) => { + if (!tab?.id) { + return; + } + synchnorousCookieStore?.addTabData(tab.id); + synchnorousCookieStore?.sendUpdatedDataToPopupAndDevTools(tab.id); + synchnorousCookieStore?.updateDevToolsState(tab.id, true); + }); + } +}; + +export const onSyncStorageChangedListenerForCDP = async (changes: { + [key: string]: chrome.storage.StorageChange; +}) => { + if ( + !changes?.isUsingCDP || + typeof changes?.isUsingCDP?.newValue === 'undefined' || + typeof changes?.isUsingCDP?.oldValue === 'undefined' + ) { + return; + } + + synchnorousCookieStore.globalIsUsingCDP = changes?.isUsingCDP?.newValue; + + const tabs = await chrome.tabs.query({}); + const debuggerTargets = await chrome.debugger.getTargets(); + + if (!changes?.isUsingCDP?.newValue) { + await Promise.all( + debuggerTargets.map(async ({ id, tabId }) => { + if (!id) { + return; + } + + try { + await chrome.debugger.detach({ targetId: id }); + if (tabId) { + synchnorousCookieStore?.sendUpdatedDataToPopupAndDevTools(tabId); + } + } catch (error) { + // eslint-disable-next-line no-console + console.warn(error); + } + }) + ); + } else { + tabs.forEach(({ id }) => { + if (!id) { + return; + } + + synchnorousCookieStore?.sendUpdatedDataToPopupAndDevTools(id); + }); + } +}; diff --git a/packages/extension/src/serviceWorker/chromeListeners/tabOnCreatedListener.ts b/packages/extension/src/serviceWorker/chromeListeners/tabOnCreatedListener.ts new file mode 100644 index 000000000..65e4bd8ff --- /dev/null +++ b/packages/extension/src/serviceWorker/chromeListeners/tabOnCreatedListener.ts @@ -0,0 +1,75 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Internal dependencies + */ +import { ALLOWED_NUMBER_OF_TABS } from '../../constants'; +import syncCookieStore from '../../store/synchnorousCookieStore'; + +export const onTabCreatedListener = async (tab: chrome.tabs.Tab) => { + try { + if (!tab?.id) { + return; + } + + const targets = await chrome.debugger.getTargets(); + + if (syncCookieStore.tabMode && syncCookieStore.tabMode !== 'unlimited') { + const doesTabExist = syncCookieStore.tabToRead; + if ( + Object.keys(syncCookieStore?.tabsData ?? {}).length >= + ALLOWED_NUMBER_OF_TABS && + doesTabExist + ) { + return; + } + syncCookieStore.tabToRead = tab.id.toString(); + syncCookieStore?.addTabData(tab.id); + + if (syncCookieStore.globalIsUsingCDP) { + const currentTab = targets.filter( + ({ tabId }) => tabId && tab.id && tabId === tab.id + ); + syncCookieStore.initialiseVariablesForNewTab(tab.id.toString()); + + syncCookieStore.updateParentChildFrameAssociation( + tab.id, + currentTab[0].id, + '0' + ); + } + } else { + syncCookieStore?.addTabData(tab.id); + + if (syncCookieStore.globalIsUsingCDP) { + const currentTab = targets.filter( + ({ tabId }) => tabId && tab.id && tabId === tab.id + ); + syncCookieStore.initialiseVariablesForNewTab(tab.id.toString()); + + syncCookieStore.updateParentChildFrameAssociation( + tab.id, + currentTab[0].id, + '0' + ); + } + } + } catch (error) { + //Fail silently + //eslint-disable-next-line no-console + console.warn(error); + } +}; diff --git a/packages/cli/src/utils/browserManagement/types.ts b/packages/extension/src/serviceWorker/chromeListeners/tabOnRemovedListener.ts similarity index 63% rename from packages/cli/src/utils/browserManagement/types.ts rename to packages/extension/src/serviceWorker/chromeListeners/tabOnRemovedListener.ts index 57fc27fb6..5435fa095 100644 --- a/packages/cli/src/utils/browserManagement/types.ts +++ b/packages/extension/src/serviceWorker/chromeListeners/tabOnRemovedListener.ts @@ -13,23 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/** + * Internal dependencies + */ +import synchnorousCookieStore from '../../store/synchnorousCookieStore'; -import { CookieData } from '@ps-analysis-tool/common'; - -export type ViewportConfig = { - width: number; - height: number; - deviceScaleFactor: number; -}; - -export type ResponseData = { - frameId: string; - serverUrl: string; - cookies: CookieData[]; -}; +export const onTabRemovedListener = (tabId: number) => { + synchnorousCookieStore.deinitialiseVariablesForTab(tabId.toString()); -export type RequestData = { - frameId: string; - serverUrl: string; - cookies: CookieData[]; + synchnorousCookieStore?.removeTabData(tabId); }; diff --git a/packages/extension/src/serviceWorker/chromeListeners/tabsOnUpdatedListener.ts b/packages/extension/src/serviceWorker/chromeListeners/tabsOnUpdatedListener.ts new file mode 100644 index 000000000..c690cd276 --- /dev/null +++ b/packages/extension/src/serviceWorker/chromeListeners/tabsOnUpdatedListener.ts @@ -0,0 +1,98 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Internal dependencies + */ +import { TABID_STORAGE } from '../../constants'; +import synchnorousCookieStore from '../../store/synchnorousCookieStore'; +import getQueryParams from '../../utils/getQueryParams'; +import attachCDP from '../attachCDP'; + +export const onTabUpdatedListener = async ( + tabId: number, + changeInfo: chrome.tabs.TabChangeInfo, + tab: chrome.tabs.Tab +) => { + try { + if (!tab.url) { + return; + } + + const targets = await chrome.debugger.getTargets(); + const mainFrameId = synchnorousCookieStore?.globalIsUsingCDP + ? targets.filter((target) => target.tabId && target.tabId === tabId)[0] + ?.id + : 0; + + const queryParams = getQueryParams(tab.url); + + if (queryParams.psat_cdp || queryParams.psat_multitab) { + await chrome.storage.sync.set({ + allowedNumberOfTabs: + queryParams.psat_multitab === 'on' ? 'unlimited' : 'single', + isUsingCDP: queryParams.psat_cdp === 'on', + }); + + synchnorousCookieStore.globalIsUsingCDP = queryParams.psat_cdp === 'on'; + synchnorousCookieStore.tabMode = + queryParams.psat_multitab === 'on' ? 'unlimited' : 'single'; + } + + synchnorousCookieStore?.updateUrl(tabId, tab.url); + + if ( + changeInfo.status === 'loading' && + tab.url && + !tab.url.startsWith('chrome://') + ) { + synchnorousCookieStore?.removeCookieData(tabId); + + if (synchnorousCookieStore.globalIsUsingCDP) { + synchnorousCookieStore.deinitialiseVariablesForTab(tabId.toString()); + synchnorousCookieStore.initialiseVariablesForNewTab(tabId.toString()); + + await attachCDP({ tabId }); + + const { + targetInfo: { targetId }, + } = await chrome.debugger.sendCommand( + { tabId }, + 'Target.getTargetInfo', + { + targetId: mainFrameId, + } + ); + + synchnorousCookieStore.updateParentChildFrameAssociation( + tabId, + targetId, + '0' + ); + } + } + await chrome.tabs.sendMessage(tabId, { + tabId, + payload: { + type: TABID_STORAGE, + tabId, + frameId: mainFrameId, + }, + }); + } catch (error) { + // eslint-disable-next-line no-console + console.warn(error); + } +}; diff --git a/packages/extension/src/serviceWorker/chromeListeners/test-utils/requestHeaders.ts b/packages/extension/src/serviceWorker/chromeListeners/test-utils/requestHeaders.ts new file mode 100644 index 000000000..6bfa0514e --- /dev/null +++ b/packages/extension/src/serviceWorker/chromeListeners/test-utils/requestHeaders.ts @@ -0,0 +1,142 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export const requestHeaders = [ + { + name: 'sec-ch-ua', + value: '"Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"', + }, + { + name: 'sec-ch-ua-mobile', + value: '?0', + }, + { + name: 'sec-ch-ua-platform', + value: '"macOS"', + }, + { + name: 'Upgrade-Insecure-Requests', + value: '1', + }, + { + name: 'User-Agent', + value: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36', + }, + { + name: 'Accept', + value: + 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', + }, + { + name: 'Sec-Fetch-Site', + value: 'same-origin', + }, + { + name: 'Sec-Fetch-Mode', + value: 'navigate', + }, + { + name: 'Sec-Fetch-User', + value: '?1', + }, + { + name: 'Sec-Fetch-Dest', + value: 'document', + }, + { + name: 'Accept-Encoding', + value: 'gzip, deflate, br, zstd', + }, + { + name: 'Accept-Language', + value: 'en-GB,en-US;q=0.9,en;q=0.8', + }, + { + name: 'Cookie', + value: + 'ckns_policy=111; ckns_explicit=0; dnsDisplayed=undefined; ccpaApplies=true; signedLspa=undefined; _sp_su=false; ckpf_ppid=7d0bd88ecf324db19821be161cb5ad91; blaize_session=ea3cb167-b847-4104-99ba-f431c90ebca2; blaize_tracking_id=f65e2382-2d6f-481d-9af5-739fe68d366e; optimizelyEndUserId=oeu1713868228553r0.5596691584936613; _cb=Dc8zQZBgqC4hDMk73y; _chartbeat2=.1713868228779.1713868228779.1.Ck0GLA0g2mgD8fTrDDOOWkrDBd6gQ.1; _cb_svref=external; pa_privacy=%22optin%22; _pcid=%7B%22browserId%22%3A%22lvc8w5f71rc6q8yv%22%2C%22_t%22%3A%22mb0ntmrb%7Clvc8w5fb%22%7D; _pctx=%7Bu%7DN4IgrgzgpgThIC4B2YA2qA05owMoBcBDfSREQpAeyRCwgEt8oBJAE0RXSwH18yBbAEYAGJPn4xBAH1QA3AMYAOAO4BWAGaCQAXyA; ccpaUUID=d39f1884-d63e-482c-b930-1e985086ccba; __gads=ID=6108e835b29f8b3c:T=1713868229:RT=1713868229:S=ALNI_MZ0vaDLqS7ojil37yQ4YD5rcMXQFg; __gpi=UID=00000df700db65bb:T=1713868229:RT=1713868229:S=ALNI_MbjgojPDkG9qvDDeEZeT_nnC3J2hw; __eoi=ID=347bbb3c9dcf685a:T=1713868229:RT=1713868229:S=AA-AfjY0xW3okcSSZeF3n4hYuF3h; DM_SitId1778=1; DM_SitId1778SecId13934=1; permutive-id=660c0cbe-527d-420e-81f8-d8a6d753712c; ecos.dt=1713868244876', + }, +]; + +export const responseHeaders = [ + { + name: 'content-type', + value: 'application/javascript', + }, + { + name: 'content-length', + value: '0', + }, + { + name: 'date', + value: 'Thu, 25 Apr 2024 12:38:09 GMT', + }, + { + name: 'server', + value: 'Kestrel', + }, + { + name: 'access-control-allow-credentials', + value: 'true', + }, + { + name: 'access-control-allow-headers', + value: 'Content-Type, Authorization, Accept, X-Requested-With', + }, + { + name: 'access-control-allow-methods', + value: 'POST, GET, PUT, DELETE, OPTIONS', + }, + { + name: 'access-control-allow-origin', + value: 'https://www.bbc.com', + }, + { + name: 'cache-control', + value: 'no-cache', + }, + { + name: 'p3p', + value: + 'policyref="https://uk-script.dotmetrics.net/w3c/p3p.xml", CP="NOI DSP LAW CURa ADMa DEVa PSAa HISa OUR IND STA"', + }, + { + name: 'x-cache', + value: 'Miss from cloudfront', + }, + { + name: 'via', + value: '1.1 b20b2466b25d82e16d99dbc5c99cfe3e.cloudfront.net (CloudFront)', + }, + { + name: 'x-amz-cf-pop', + value: 'BOM54-P1', + }, + { + name: 'x-amz-cf-id', + value: 'dydln3ts4sE6lhqGnXt4HeOEnt2WBnpplUQhSmanGxzvBZgrfUSY0w==', + }, + { + name: 'set-cookie', + value: + 'DotMetrics.DeviceKey=DeviceID=; expires=Fri, 25 Apr 2025 12:38:09 GMT; domain=.dotmetrics.net; path=/; SameSite=None; secure', + }, + { + name: 'set-cookie', + value: + 'DotMetrics.UniqueUserIdentityCookie=UserID=cf92e7f8-16f4-4caf-acb6-f4fcd61639c4&Created=04/25/2024 12:38:09&UserMode=0&guid=e7c1f426-58e5-415e-b233-5730639dab5a&ver=1; expires=Fri, 25 Apr 2025 12:38:09 GMT; domain=.dotmetrics.net; path=/; SameSite=None; secure', + }, +]; diff --git a/packages/extension/src/serviceWorker/chromeListeners/tests/beforeSendHeadersListener.ts b/packages/extension/src/serviceWorker/chromeListeners/tests/beforeSendHeadersListener.ts new file mode 100644 index 000000000..8c5ff0c2a --- /dev/null +++ b/packages/extension/src/serviceWorker/chromeListeners/tests/beforeSendHeadersListener.ts @@ -0,0 +1,110 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies + */ +import '@testing-library/jest-dom'; +import SinonChrome from 'sinon-chrome'; +/** + * Internal dependencies + */ +//@ts-ignore +// eslint-disable-next-line import/no-unresolved +import OpenCookieDatabase from 'ps-analysis-tool/assets/data/open-cookie-database.json'; +import { onBeforeSendHeadersListener } from '../beforeSendHeadersListener'; +import { requestHeaders } from '../test-utils/requestHeaders'; +import synchnorousCookieStore from '../../../store/synchnorousCookieStore'; + +describe('chrome.webRequest.onBeforeSendHeaders.addListener', () => { + beforeAll(() => { + globalThis.chrome = SinonChrome as unknown as typeof chrome; + SinonChrome.webRequest.onBeforeSendHeaders.addListener( + onBeforeSendHeadersListener, + { urls: ['*://*/*'] }, + ['extraHeaders', 'requestHeaders'] + ); + globalThis.fetch = function () { + return Promise.resolve({ + json: () => + Promise.resolve({ + ...OpenCookieDatabase, + }), + text: () => Promise.resolve({}), + }); + } as unknown as typeof fetch; + }); + + beforeEach(() => { + synchnorousCookieStore.globalIsUsingCDP = false; + synchnorousCookieStore.tabMode = 'single'; + synchnorousCookieStore.addTabData(1141143618); + synchnorousCookieStore.updateUrl(1141143618, 'https://bbc.com'); + synchnorousCookieStore.tabToRead = '1141143618'; + }); + afterEach(() => { + synchnorousCookieStore.removeTabData(1141143618); + }); + + test('Should parse request Cookies', async () => { + SinonChrome.webRequest.onBeforeSendHeaders.dispatch({ + url: 'https://bbc.com', + frameId: 0, + requestHeaders, + tabId: 1141143618, + requestId: 457, + }); + + await new Promise((r) => setTimeout(r, 2000)); + + expect( + Object.keys(synchnorousCookieStore.tabsData[1141143618]).length + ).toEqual(24); + }); + + test('Should not parse cookies if no cookie header is found in request header', async () => { + SinonChrome.webRequest.onBeforeSendHeaders.dispatch({ + url: 'https://bbc.com', + frameId: 0, + requestHeaders: requestHeaders.filter(({ name }) => name !== 'Cookie'), + tabId: 1141143618, + requestId: 457, + }); + + await new Promise((r) => setTimeout(r, 2000)); + + expect( + Object.keys(synchnorousCookieStore.tabsData[1141143618]).length + ).toEqual(0); + }); + + test('Should not parse cookies if cdp is on', async () => { + synchnorousCookieStore.globalIsUsingCDP = true; + SinonChrome.webRequest.onBeforeSendHeaders.dispatch({ + url: 'https://bbc.com', + frameId: 0, + requestHeaders: requestHeaders.filter(({ name }) => name !== 'Cookie'), + tabId: 1141143618, + requestId: 457, + }); + + await new Promise((r) => setTimeout(r, 2000)); + + expect( + Object.keys(synchnorousCookieStore.tabsData[1141143618]).length + ).toEqual(0); + synchnorousCookieStore.globalIsUsingCDP = false; + }); +}); diff --git a/packages/extension/src/serviceWorker/chromeListeners/tests/onResponseStartedListener.ts b/packages/extension/src/serviceWorker/chromeListeners/tests/onResponseStartedListener.ts new file mode 100644 index 000000000..4ce05500c --- /dev/null +++ b/packages/extension/src/serviceWorker/chromeListeners/tests/onResponseStartedListener.ts @@ -0,0 +1,118 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies + */ +import '@testing-library/jest-dom'; +import SinonChrome from 'sinon-chrome'; +/** + * Internal dependencies + */ +//@ts-ignore +// eslint-disable-next-line import/no-unresolved +import OpenCookieDatabase from 'ps-analysis-tool/assets/data/open-cookie-database.json'; + +import { onResponseStartedListener } from '../onResponseStartedListener'; +import { responseHeaders } from '../test-utils/requestHeaders'; +import synchnorousCookieStore from '../../../store/synchnorousCookieStore'; + +describe('chrome.webRequest.onResponseStarted.addListener', () => { + beforeAll(() => { + globalThis.chrome = SinonChrome as unknown as typeof chrome; + globalThis.fetch = function () { + return Promise.resolve({ + json: () => + Promise.resolve({ + ...OpenCookieDatabase, + }), + text: () => Promise.resolve({}), + }); + } as unknown as typeof fetch; + + SinonChrome.webRequest.onResponseStarted.addListener( + onResponseStartedListener, + { urls: ['*://*/*'] }, + ['extraHeaders', 'responseHeaders'] + ); + }); + + beforeEach(() => { + synchnorousCookieStore.globalIsUsingCDP = false; + synchnorousCookieStore.tabMode = 'single'; + synchnorousCookieStore.addTabData(1141143618); + synchnorousCookieStore.updateUrl(1141143618, 'https://bbc.com'); + synchnorousCookieStore.tabToRead = '1141143618'; + }); + + afterEach(() => { + synchnorousCookieStore.removeTabData(1141143618); + }); + + test('Should parse response Cookies', async () => { + await new Promise((r) => setTimeout(r, 2000)); + SinonChrome.webRequest.onResponseStarted.dispatch({ + url: 'https://bbc.com', + frameId: 0, + responseHeaders, + tabId: 1141143618, + requestId: 457, + }); + + await new Promise((r) => setTimeout(r, 2000)); + + expect( + Object.keys(synchnorousCookieStore.tabsData[1141143618]).length + ).toEqual(2); + }); + + test('Should not parse cookies if no cookie header is found in response header', async () => { + SinonChrome.webRequest.onResponseStarted.dispatch({ + url: 'https://bbc.com', + frameId: 0, + requestHeaders: responseHeaders.filter( + ({ name }) => name !== 'set-cookie' + ), + tabId: 1141143618, + requestId: 457, + }); + + await new Promise((r) => setTimeout(r, 2000)); + + expect( + Object.keys(synchnorousCookieStore.tabsData[1141143618]).length + ).toEqual(0); + }); + + test('Should not parse cookies if cdp is on', async () => { + synchnorousCookieStore.globalIsUsingCDP = true; + SinonChrome.webRequest.onResponseStarted.dispatch({ + url: 'https://bbc.com', + frameId: 0, + requestHeaders: responseHeaders.filter( + ({ name }) => name !== 'set-cookie' + ), + tabId: 1141143618, + requestId: 457, + }); + + await new Promise((r) => setTimeout(r, 2000)); + + expect( + Object.keys(synchnorousCookieStore.tabsData[1141143618]).length + ).toEqual(0); + synchnorousCookieStore.globalIsUsingCDP = false; + }); +}); diff --git a/packages/extension/src/serviceWorker/chromeListeners/tests/tabOnCreatedListener.ts b/packages/extension/src/serviceWorker/chromeListeners/tests/tabOnCreatedListener.ts new file mode 100644 index 000000000..b839c74ee --- /dev/null +++ b/packages/extension/src/serviceWorker/chromeListeners/tests/tabOnCreatedListener.ts @@ -0,0 +1,169 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies + */ +import '@testing-library/jest-dom'; +import SinonChrome from 'sinon-chrome'; +/** + * Internal dependencies + */ +//@ts-ignore +// eslint-disable-next-line import/no-unresolved +import OpenCookieDatabase from 'ps-analysis-tool/assets/data/open-cookie-database.json'; +import { onTabCreatedListener } from '../tabOnCreatedListener'; +import synchnorousCookieStore from '../../../store/synchnorousCookieStore'; + +describe('chrome.tabs.onCreated.addListener', () => { + beforeAll(() => { + globalThis.chrome = SinonChrome as unknown as typeof chrome; + SinonChrome.tabs.onCreated.addListener(onTabCreatedListener); + globalThis.fetch = function () { + return Promise.resolve({ + json: () => + Promise.resolve({ + ...OpenCookieDatabase, + }), + text: () => Promise.resolve({}), + }); + } as unknown as typeof fetch; + }); + + describe('Multitab Mode', () => { + beforeAll(() => { + synchnorousCookieStore.globalIsUsingCDP = false; + synchnorousCookieStore.tabMode = 'unlimited'; + }); + + test('Openeing new tabs should create an entry for the new tab in synchnorous cookie store.', async () => { + SinonChrome.tabs.onCreated.dispatch({ + status: 'loading', + index: 1, + title: 'CNN News Website', + url: 'https://edition.cnn.com', + pinned: false, + highlighted: false, + windowId: 11, + active: true, + id: 123456, + audible: false, + discarded: false, + autoDiscardable: false, + groupId: 1, + }); + + await new Promise((r) => setTimeout(r, 2000)); + + expect(synchnorousCookieStore.tabs[123456]).toBeTruthy(); + }); + }); + + describe('Single Tab Mode', () => { + beforeEach(() => { + synchnorousCookieStore.globalIsUsingCDP = false; + synchnorousCookieStore.tabMode = 'single'; + synchnorousCookieStore.addTabData(123456); + synchnorousCookieStore.tabToRead = ''; + }); + afterEach(() => { + synchnorousCookieStore.tabs = {}; + synchnorousCookieStore.tabsData = {}; + }); + + test('Openeing new tabs should create an entry for the new tab in synchnorous cookie store.', async () => { + SinonChrome.tabs.onCreated.dispatch({ + status: 'loading', + index: 1, + title: 'CNN News Website', + url: 'https://edition.cnn.com', + pinned: false, + highlighted: false, + windowId: 11, + active: true, + id: 123456, + audible: false, + discarded: false, + autoDiscardable: false, + groupId: 1, + }); + + await new Promise((r) => setTimeout(r, 2000)); + + expect(synchnorousCookieStore.tabs[123456]).toBeTruthy(); + }); + + test('Openeing more than 1 tab in single tab processing mode should not create an entry in synchrorous cookie store.', async () => { + SinonChrome.tabs.onCreated.dispatch({ + status: 'loading', + index: 1, + title: 'CNN News Website', + url: 'https://edition.cnn.com', + pinned: false, + highlighted: false, + windowId: 11, + active: true, + id: 123456, + audible: false, + discarded: false, + autoDiscardable: false, + groupId: 1, + }); + + SinonChrome.tabs.onCreated.dispatch({ + status: 'loading', + index: 1, + title: 'CNN News Website', + url: 'https://edition.cnn.com', + pinned: false, + highlighted: false, + windowId: 11, + active: true, + id: 24567, + audible: false, + discarded: false, + autoDiscardable: false, + groupId: 1, + }); + + await new Promise((r) => setTimeout(r, 2000)); + + expect(synchnorousCookieStore.tabs[24567]).not.toBeTruthy(); + }); + }); + + test('Openeing new tab and if tabId is missing it should not create new tab.', async () => { + SinonChrome.tabs.onCreated.dispatch({ + status: 'loading', + index: 1, + title: 'CNN News Website', + url: 'https://edition.cnn.com', + pinned: false, + highlighted: false, + windowId: 11, + active: true, + audible: false, + discarded: false, + autoDiscardable: false, + groupId: 1, + }); + + await new Promise((r) => setTimeout(r, 2000)); + + expect(Object.keys(synchnorousCookieStore.tabs).length).toBeLessThanOrEqual( + 1 + ); + }); +}); diff --git a/packages/extension/src/serviceWorker/chromeListeners/windowsOnCreatedListener.ts b/packages/extension/src/serviceWorker/chromeListeners/windowsOnCreatedListener.ts new file mode 100644 index 000000000..cd2c87cf9 --- /dev/null +++ b/packages/extension/src/serviceWorker/chromeListeners/windowsOnCreatedListener.ts @@ -0,0 +1,29 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export const windowsOnCreatedListener = async () => { + const totalWindows = await chrome.windows.getAll(); + + // @see https://developer.chrome.com/blog/longer-esw-lifetimes#whats_changed + // Doing this to keep the service worker alive so that we dont loose any data and introduce any bug. + setInterval(() => { + chrome.storage.local.get(); + }, 28000); + + // We do not want to clear content settings if a user has create one more window. + if (totalWindows.length < 2) { + chrome.contentSettings.cookies.clear({}); + } +}; diff --git a/packages/cli/src/utils/checkPortInUse.ts b/packages/extension/src/serviceWorker/chromeListeners/windowsOnRemovedListener.ts similarity index 59% rename from packages/cli/src/utils/checkPortInUse.ts rename to packages/extension/src/serviceWorker/chromeListeners/windowsOnRemovedListener.ts index d743665eb..2e4d2fda2 100644 --- a/packages/cli/src/utils/checkPortInUse.ts +++ b/packages/extension/src/serviceWorker/chromeListeners/windowsOnRemovedListener.ts @@ -13,23 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -import http from 'http'; - /** - * @param port number Port number to test - * @returns Promise which will resolve in a boolean value + * Internal dependencies */ -export function checkPortInUse(port: number): Promise { - return new Promise((resolve) => { - const server = http - .createServer() - .listen(port, () => { - server.close(); - resolve(false); - }) - .on('error', () => { - resolve(true); - }); +import synchnorousCookieStore from '../../store/synchnorousCookieStore'; + +export const windowsOnRemovedListener = (windowId: number) => { + chrome.tabs.query({ windowId }, (tabs) => { + tabs.map((tab) => { + if (tab.id) { + synchnorousCookieStore.deinitialiseVariablesForTab(tab.id.toString()); + } + return tab; + }); }); -} + + synchnorousCookieStore?.removeWindowData(windowId); +}; diff --git a/packages/extension/src/serviceWorker/createCookieObject.ts b/packages/extension/src/serviceWorker/createCookieObject.ts index b12679bb4..b226fb5c5 100644 --- a/packages/extension/src/serviceWorker/createCookieObject.ts +++ b/packages/extension/src/serviceWorker/createCookieObject.ts @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - /** * External dependencies. */ @@ -21,8 +20,9 @@ import { calculateEffectiveExpiryDate, type CookieData, getDomainFromUrl, -} from '@ps-analysis-tool/common'; +} from '@google-psat/common'; import type { Protocol } from 'devtools-protocol'; + /** * Internal dependencies */ @@ -184,5 +184,6 @@ function parseAttributeValues( default: return value; } + return value; } diff --git a/packages/extension/src/serviceWorker/findAnalyticsMatch.ts b/packages/extension/src/serviceWorker/findAnalyticsMatch.ts index 7e2b95e64..741e72a96 100644 --- a/packages/extension/src/serviceWorker/findAnalyticsMatch.ts +++ b/packages/extension/src/serviceWorker/findAnalyticsMatch.ts @@ -13,17 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - /** - * Internal dependencies. + * External dependencies. */ -import type { - CookieAnalytics, - CookieDatabase, -} from '../utils/fetchCookieDictionary'; +import type { CookieAnalytics, CookieDatabase } from '@google-psat/common'; /** - * * Matches wildcard string to a provided string. For eg Foo_* matches Foo_123. * @param {string} wildcard Wildcard cookie name. * @param {string} str cookie name to be matched. @@ -35,6 +30,7 @@ const wildTest = (wildcard: string, str: string): boolean => { `^${regExp.replace(/\*/g, '.*').replace(/\?/g, '.')}$`, 'i' ); + return result.test(str); // remove last 'i' above to have case sensitive }; @@ -79,4 +75,5 @@ const findAnalyticsMatch = ( return analytics; }; + export default findAnalyticsMatch; diff --git a/packages/extension/src/serviceWorker/findPreviousCookieDataObject.ts b/packages/extension/src/serviceWorker/findPreviousCookieDataObject.ts index c4171aeae..130c66a8c 100644 --- a/packages/extension/src/serviceWorker/findPreviousCookieDataObject.ts +++ b/packages/extension/src/serviceWorker/findPreviousCookieDataObject.ts @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - /** - * Internal dependencies. + * External dependencies. */ -import type { CookieData } from '../store'; +import type { CookieData } from '@google-psat/common'; /** * Find previous cookie object from local storage for given tabId and cookieName. diff --git a/packages/extension/src/serviceWorker/index.ts b/packages/extension/src/serviceWorker/index.ts index ec99a1ce4..08f5aced2 100644 --- a/packages/extension/src/serviceWorker/index.ts +++ b/packages/extension/src/serviceWorker/index.ts @@ -17,790 +17,376 @@ * External dependencies. */ import { Protocol } from 'devtools-protocol'; -import { - type CookieDatabase, - type CookieData, - parseResponseReceivedExtraInfo, - parseRequestWillBeSentExtraInfo, - auditsToNetworkMap, -} from '@ps-analysis-tool/common'; /** * Internal dependencies. */ -import { fetchDictionary } from '../utils/fetchCookieDictionary'; -import { - ALLOWED_NUMBER_OF_TABS, - DEVTOOLS_CLOSE, - DEVTOOLS_OPEN, - DEVTOOLS_SET_JAVASCSCRIPT_COOKIE, - INITIAL_SYNC, - POPUP_CLOSE, - POPUP_OPEN, - SERVICE_WORKER_RELOAD_MESSAGE, - SERVICE_WORKER_TABS_RELOAD_COMMAND, - SET_TAB_TO_READ, - TABID_STORAGE, -} from '../constants'; -import SynchnorousCookieStore from '../store/synchnorousCookieStore'; -import { getTab } from '../utils/getTab'; -import parseHeaders from '../utils/parseHeaders'; -import resetCookieBadgeText from '../store/utils/resetCookieBadgeText'; -import getQueryParams from '../utils/getQueryParams'; -import reloadCurrentTab from '../utils/reloadCurrentTab'; - -let cookieDB: CookieDatabase | null = null; -let syncCookieStore: SynchnorousCookieStore | undefined; - -const requestIdToCDPURLMapping: { - [tabId: string]: { - [requestId: string]: string; - }; -} = {}; - -let tabMode: 'single' | 'unlimited' = 'single'; -let tabToRead = ''; -let globalIsUsingCDP = false; +import syncCookieStore from '../store/synchnorousCookieStore'; +import createCookieFromAuditsIssue from '../utils/createCookieFromAuditsIssue'; +import attachCDP from './attachCDP'; +import './chromeListeners'; const ALLOWED_EVENTS = [ 'Network.responseReceived', 'Network.requestWillBeSentExtraInfo', 'Network.responseReceivedExtraInfo', 'Audits.issueAdded', + 'Network.requestWillBeSent', + 'Page.frameAttached', + 'Page.frameNavigated', + 'Target.attachedToTarget', ]; -/** - * Fires when the browser receives a response from a web server. - * @see https://developer.chrome.com/docs/extensions/reference/api/webRequest - */ -chrome.webRequest.onResponseStarted.addListener( - ({ tabId, url, responseHeaders, frameId, requestId }) => { - (async () => { - const tab = await getTab(tabId); - let tabUrl = syncCookieStore?.getTabUrl(tabId); - - // Sometimes, a site may send out requests while it is still in the preloading state. Any cookie set from these requests are classified as third-party cookies. - // For example nikkei.com. The cookie domain may be nikkei.com however the tab URL would be xyz.com so it becomes third-party - if (tab && tab.pendingUrl) { - tabUrl = tab.pendingUrl; - } - - if (!cookieDB) { - cookieDB = await fetchDictionary(); - } - - const cookies = await parseHeaders( - globalIsUsingCDP, - 'response', - tabToRead, - tabMode, - tabId, - url, - cookieDB, - tabUrl, - frameId, - requestId, - responseHeaders - ); - - if (!cookies || (cookies && cookies?.length === 0)) { - return; - } - - // Adds the cookies from the request headers to the cookies object. - syncCookieStore?.update(tabId, cookies); - })(); - }, - { urls: ['*://*/*'] }, - ['extraHeaders', 'responseHeaders'] -); +let targets: chrome.debugger.TargetInfo[] = []; /** - * Fires before sending an HTTP request, once the request headers are available. - * @see https://developer.chrome.com/docs/extensions/reference/api/webRequest#event-onBeforeSendHeaders + * Fires whenever debugging target issues instrumentation event. + * @see https://developer.chrome.com/docs/extensions/reference/api/debugger */ -chrome.webRequest.onBeforeSendHeaders.addListener( - ({ url, requestHeaders, tabId, frameId, requestId }) => { - (async () => { - const tab = await getTab(tabId); - let tabUrl = syncCookieStore?.getTabUrl(tabId); - - if (tab && tab.pendingUrl) { - tabUrl = tab.pendingUrl; - } - - if (!cookieDB) { - cookieDB = await fetchDictionary(); +chrome.debugger.onEvent.addListener((source, method, params) => { + // eslint-disable-next-line complexity + (async () => { + try { + if (!ALLOWED_EVENTS.includes(method)) { + return; } - const cookies = await parseHeaders( - globalIsUsingCDP, - 'request', - tabToRead, - tabMode, - tabId, - url, - cookieDB, - tabUrl, - frameId, - requestId, - requestHeaders - ); - - if (!cookies || (cookies && cookies?.length === 0)) { + // This is done because sometimes we get tabId for which the event is emitted for. + // In some cases we get only the targetId from which we have to ascertain the tabId from the frameTree. + // In cases where we dont get any targetId or tabId we dont process it because these events are mostly from an extension. + if (!source.tabId && !source.targetId) { return; } - // Adds the cookies from the request headers to the cookies object. - syncCookieStore?.update(tabId, cookies); - })(); - }, - { urls: ['*://*/*'] }, - ['extraHeaders', 'requestHeaders'] -); - -/** - * Fires when a profile with extension is started. - * @see https://developer.chrome.com/docs/extensions/reference/api/runtime#event-onStartup - */ -chrome.runtime.onStartup.addListener(async () => { - setInterval(async () => { - await chrome.storage.session.get(); - }, 20000); + let tabId = ''; - const storage = await chrome.storage.sync.get(); - - if (!syncCookieStore) { - syncCookieStore = new SynchnorousCookieStore(); - } - - if (Object.keys(storage).includes('allowedNumberOfTabs')) { - tabMode = storage.allowedNumberOfTabs; - } - - if (Object.keys(storage).includes('isUsingCDP')) { - globalIsUsingCDP = storage.isUsingCDP; - } -}); - -/** - * Fires when a tab is created. - * @see https://developer.chrome.com/docs/extensions/reference/api/tabs#event-onCreated - */ -chrome.tabs.onCreated.addListener((tab) => { - if (!tab.id) { - return; - } - - if (!syncCookieStore) { - syncCookieStore = new SynchnorousCookieStore(); - } - - if (tabMode && tabMode !== 'unlimited') { - const tabExists = tabToRead; - const data = syncCookieStore?.tabsData ?? {}; - - if (Object.keys(data).length >= ALLOWED_NUMBER_OF_TABS && tabExists) { - return; - } - - tabToRead = tab.id.toString(); - syncCookieStore?.addTabData(tab.id); - } else { - syncCookieStore?.addTabData(tab.id); - } -}); - -/** - * Fires when a tab is closed. - * @see https://developer.chrome.com/docs/extensions/reference/api/tabs#event-onRemoved - */ -chrome.tabs.onRemoved.addListener((tabId) => { - syncCookieStore?.removeTabData(tabId); -}); - -/** - * Fires when a tab is updated. - * @see https://developer.chrome.com/docs/extensions/reference/api/tabs#event-onUpdated - */ -chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => { - if (!tab.url) { - return; - } - - if (!syncCookieStore) { - syncCookieStore = new SynchnorousCookieStore(); - } - - const queryParams = getQueryParams(tab.url); - - if (queryParams.psat_cdp || queryParams.psat_multitab) { - await chrome.storage.sync.set({ - allowedNumberOfTabs: - queryParams.psat_multitab === 'on' ? 'unlimited' : 'single', - isUsingCDP: queryParams.psat_cdp === 'on', - }); - - globalIsUsingCDP = queryParams.psat_cdp === 'on'; - tabMode = queryParams.psat_multitab === 'on' ? 'unlimited' : 'single'; - } - - syncCookieStore?.updateUrl(tabId, tab.url); - - if (changeInfo.status === 'loading' && tab.url) { - syncCookieStore?.removeCookieData(tabId); - } - try { - await chrome.tabs.sendMessage(tabId, { - tabId, - payload: { - type: TABID_STORAGE, - tabId, - }, - }); - } catch (error) { - // eslint-disable-next-line no-console - console.warn(error); - } - - try { - if (globalIsUsingCDP) { - await chrome.debugger.attach({ tabId }, '1.3'); - await chrome.debugger.sendCommand({ tabId }, 'Network.enable'); - await chrome.debugger.sendCommand({ tabId }, 'Audits.enable'); - } else { - await chrome.debugger.detach({ tabId }); - } - } catch (error) { - //Fail silently - } -}); + targets = await chrome.debugger.getTargets(); -/** - * Fires when a window is removed (closed). - * @see https://developer.chrome.com/docs/extensions/reference/api/windows#event-onRemoved - */ -chrome.windows.onRemoved.addListener((windowId) => { - syncCookieStore?.removeWindowData(windowId); -}); - -/** - * Fires when the extension is first installed, - * when clicked on the extension refresh button from chrome://extensions/ - * when the extension is updated to a new version, - * when Chrome is updated to a new version. - * @see https://developer.chrome.com/docs/extensions/reference/api/runtime#event-onInstalled - * @todo Shouldn't have to reinstall the extension. - */ -chrome.runtime.onInstalled.addListener(async (details) => { - syncCookieStore = new SynchnorousCookieStore(); - syncCookieStore?.clear(); - - setInterval(async () => { - await chrome.storage.session.get(); - }, 20000); - - if (details.reason === 'install') { - await chrome.storage.sync.clear(); - await chrome.storage.sync.set({ - allowedNumberOfTabs: 'single', - isUsingCDP: false, - }); - } - - if (details.reason === 'update') { - await chrome.storage.local.clear(); - const preSetSettings = await chrome.storage.sync.get(); - tabMode = preSetSettings?.allowedNumberOfTabs ?? 'single'; - globalIsUsingCDP = preSetSettings?.isUsingCDP ?? false; - if ( - preSetSettings?.allowedNumberOfTabs && - Object.keys(preSetSettings).includes('isUsingCDP') - ) { - return; - } - - await chrome.storage.sync.clear(); - await chrome.storage.sync.set({ - allowedNumberOfTabs: 'single', - isUsingCDP: false, - }); - } -}); - -/** - * Fires whenever debugging target issues instrumentation event. - * @see https://developer.chrome.com/docs/extensions/reference/api/debugger - */ -// eslint-disable-next-line complexity -chrome.debugger.onEvent.addListener((source, method, params) => { - if (!ALLOWED_EVENTS.includes(method) || !source?.tabId || !params) { - return; - } - - const url = syncCookieStore?.getTabUrl(source?.tabId); - - const tabId = source.tabId.toString(); - - if (tabMode !== 'unlimited' && tabToRead !== tabId) { - return; - } - - if (method === 'Network.responseReceived') { - const request = params as Protocol.Network.ResponseReceivedEvent; - - // To get domain from the request URL if not given in the cookie line. - if (!requestIdToCDPURLMapping[tabId]) { - requestIdToCDPURLMapping[tabId] = { - [request.requestId]: request?.response.url, - }; - } else { - requestIdToCDPURLMapping[tabId] = { - ...requestIdToCDPURLMapping[tabId], - [request.requestId]: request?.response.url, - }; - } - } + await Promise.all( + targets.map(async ({ id, url }) => { + if (url.startsWith('http')) { + await attachCDP({ targetId: id }); + } + }) + ); - if (method === 'Network.requestWillBeSentExtraInfo') { - const requestParams = - params as Protocol.Network.RequestWillBeSentExtraInfoEvent; + // This is to get a list of all targets being attached to the main frame. + if (method === 'Target.attachedToTarget' && params) { + const { + targetInfo: { targetId, url }, + } = params as Protocol.Target.AttachedToTargetEvent; - const { associatedCookies, requestId } = requestParams; + await attachCDP({ targetId }); - if (associatedCookies.length === 0) { - return; - } + targets = await chrome.debugger.getTargets(); + const parentFrameId = targets.filter( + (target) => target?.tabId && target.tabId === source.tabId + )[0]?.id; - const cookies: CookieData[] = parseRequestWillBeSentExtraInfo( - associatedCookies, - cookieDB ?? {}, - requestIdToCDPURLMapping[tabId], - url ?? '', - requestId - ); + syncCookieStore?.addFrameToTabAndUpdateMetadata( + source.tabId ?? null, + source.targetId ?? null, + targetId, + parentFrameId ?? source.targetId, + url + ); - if (cookies.length === 0) { - return; - } + return; + } - syncCookieStore?.update(Number(tabId), cookies); - } + //If we get the tabId from source.tabId the we use it else we parse the tabsData and find the parentId in the frameIdSet and return the tabId. + if (source?.tabId) { + tabId = source?.tabId?.toString(); + } else if (source.targetId) { + const tab = Object.keys(syncCookieStore?.tabs ?? {}).filter( + (key) => + source.targetId && + syncCookieStore?.getFrameIDSet(Number(key))?.has(source.targetId) + ); + tabId = tab[0]; + } + // Using Page.frameAttached and Page.frameNavigated we will find the tabId using the frameId because in certain events source.tabId is missing and source.targetId is availale. + if (method === 'Page.frameAttached' && params) { + const { frameId, parentFrameId } = + params as Protocol.Page.FrameAttachedEvent; + + await syncCookieStore?.addFrameToTabAndUpdateMetadata( + source.tabId ?? null, + source.targetId ?? null, + frameId, + parentFrameId + ); + return; + } - if (method === 'Network.responseReceivedExtraInfo') { - const responseParams = - params as Protocol.Network.ResponseReceivedExtraInfoEvent; + //Some times we get frames here which we dont get in the extension, which is mostly because of frame Navigation we collect the data and send it to the extension. + if (method === 'Page.frameNavigated' && params) { + const { + frame: { parentId = '', id, url: frameUrl }, + } = params as Protocol.Page.FrameNavigatedEvent; - const { - headers, - blockedCookies, - requestId, - cookiePartitionKey = '', - exemptedCookies = [], - } = responseParams; + if (!parentId && !source.targetId) { + return; + } - // Sometimes CDP gives "set-cookie" and sometimes it gives "Set-Cookie". - if (!headers['set-cookie'] && !headers['Set-Cookie']) { - return; - } + await syncCookieStore?.addFrameToTabAndUpdateMetadata( + source.tabId ?? null, + source.targetId ?? null, + id, + parentId, + frameUrl + ); + } - const cookies: CookieData[] = parseResponseReceivedExtraInfo( - headers, - blockedCookies, - exemptedCookies, - cookiePartitionKey, - requestIdToCDPURLMapping[tabId], - url ?? '', - cookieDB ?? {}, - requestId - ); - - if (cookies.length === 0) { - return; - } + if ( + syncCookieStore.tabMode !== 'unlimited' && + syncCookieStore.tabToRead !== tabId + ) { + return; + } - syncCookieStore?.update(Number(tabId), cookies); - } + //If we get requestWillBeSent before requestWillBeSentExtraInfo then we add the frame if to the object. + // If we get requestWillBeSent afterwards then we will remove the add the frameId and then process the requestWillBeSentExtraInfo. + if (method === 'Network.requestWillBeSent' && params) { + const { + requestId, + request: { url: requestUrl }, + frameId = '', + } = params as Protocol.Network.RequestWillBeSentEvent; - if (method === 'Audits.issueAdded' && params) { - const auditParams = params as Protocol.Audits.IssueAddedEvent; - const { code, details } = auditParams.issue; - if (code !== 'CookieIssue' && !details.cookieIssueDetails) { - return; - } + let finalFrameId = frameId; - if ( - !details.cookieIssueDetails?.cookie && - !details.cookieIssueDetails?.cookieWarningReasons && - !details.cookieIssueDetails?.cookieExclusionReasons - ) { - return; - } + if (!finalFrameId) { + return; + } - const { cookie, cookieExclusionReasons, cookieWarningReasons } = - details.cookieIssueDetails; + targets = await chrome.debugger.getTargets(); + const setTargets = new Set(); - const domainToUse = cookie?.domain.startsWith('.') - ? cookie.domain.slice(1) - : cookie?.domain; + targets.map(({ id }) => { + setTargets.add(id); + return id; + }); - if (!cookie?.name || !domainToUse) { - return; - } - try { - const modifiedCookieExclusionReasons = cookieExclusionReasons.map( - (reason) => { - if (reason.toLowerCase().startsWith('exclude')) { - return auditsToNetworkMap[ - reason - ] as Protocol.Network.CookieBlockedReason; - } - return reason as Protocol.Network.CookieBlockedReason; + finalFrameId = syncCookieStore.addFrameIdAndRequestUrlToResourceMap( + tabId, + frameId, + setTargets, + requestUrl + ); + + if (!syncCookieStore.requestIdToCDPURLMapping[tabId]) { + syncCookieStore.requestIdToCDPURLMapping[tabId] = { + [requestId]: { + finalFrameId, + frameId, + url: requestUrl, + }, + }; + } else { + syncCookieStore.requestIdToCDPURLMapping[tabId] = { + ...syncCookieStore.requestIdToCDPURLMapping[tabId], + [requestId]: { + finalFrameId, + frameId, + url: requestUrl, + }, + }; } - ); - syncCookieStore?.addCookieExclusionWarningReason( - cookie?.name + domainToUse + cookie?.path, - modifiedCookieExclusionReasons, - cookieWarningReasons, - source.tabId - ); - } catch (error) { - // fail silently - } - } -}); - -const listenToNewTab = async (tabId?: number) => { - const newTabId = - tabId?.toString() || chrome.devtools.inspectedWindow.tabId.toString(); - - if (!newTabId) { - return ''; - } - - if (tabMode && tabMode !== 'unlimited') { - const storedTabData = Object.keys(syncCookieStore?.tabsData ?? {}); - - await Promise.all( - storedTabData.map(async (tabIdToDelete) => { - syncCookieStore?.removeTabData(Number(tabIdToDelete)); - try { - await chrome.action.setBadgeText({ - tabId: Number(tabIdToDelete), - text: '', - }); - await chrome.debugger.detach({ tabId: Number(tabIdToDelete) }); - } catch (error) { - // Fail silently + if (syncCookieStore.unParsedRequestHeaders[tabId][requestId]) { + syncCookieStore.parseRequestHeaders( + syncCookieStore.unParsedRequestHeaders[tabId][requestId], + requestId, + tabId, + Array.from(new Set([finalFrameId, frameId])) + ); } - return tabIdToDelete; - }) - ); - } - - tabToRead = newTabId; - - syncCookieStore?.addTabData(Number(newTabId)); - syncCookieStore?.updateDevToolsState(Number(newTabId), true); - syncCookieStore?.updatePopUpState(Number(newTabId), true); - - const currentTab = await getTab(Number(newTabId)); - - syncCookieStore?.updateUrl(Number(newTabId), currentTab?.url ?? ''); - - return newTabId; -}; + return; + } -/** - * Fires when a message is sent from either an extension process (by runtime.sendMessage) or a content script (by tabs.sendMessage). - * @see https://developer.chrome.com/docs/extensions/reference/api/runtime#event-onMessage - */ -// eslint-disable-next-line complexity -chrome.runtime.onMessage.addListener(async (request) => { - if (!request.type) { - return; - } - - const incomingMessageType = request.type; - - if (SET_TAB_TO_READ === incomingMessageType) { - tabToRead = request?.payload?.tabId?.toString(); - const newTab = await listenToNewTab(request?.payload?.tabId); - // Can't use sendResponse as delay is too long. So using sendMessage instead. - chrome.runtime.sendMessage({ - type: SET_TAB_TO_READ, - payload: { - tabId: Number(newTab), - }, - }); - - if (globalIsUsingCDP) { - try { - if (globalIsUsingCDP) { - await chrome.debugger.attach({ tabId: Number(newTab) }, '1.3'); - await chrome.debugger.sendCommand( - { tabId: Number(newTab) }, - 'Network.enable' - ); - await chrome.debugger.sendCommand( - { tabId: Number(newTab) }, - 'Audits.enable' + if (method === 'Network.requestWillBeSentExtraInfo') { + const { requestId } = + params as Protocol.Network.RequestWillBeSentExtraInfoEvent; + + if (syncCookieStore.requestIdToCDPURLMapping[tabId]?.[requestId]) { + syncCookieStore.parseRequestHeaders( + params as Protocol.Network.RequestWillBeSentExtraInfoEvent, + requestId, + tabId, + Array.from( + new Set([ + syncCookieStore.requestIdToCDPURLMapping[tabId][requestId] + ?.finalFrameId, + syncCookieStore.requestIdToCDPURLMapping[tabId][requestId] + ?.frameId, + ]) + ) ); } else { - await chrome.debugger.detach({ tabId: Number(newTab) }); + syncCookieStore.unParsedRequestHeaders[tabId][requestId] = + params as Protocol.Network.RequestWillBeSentExtraInfoEvent; } - } catch (error) { - //Fail silently + return; } - } - - await reloadCurrentTab(Number(newTab)); - } - - if (SERVICE_WORKER_TABS_RELOAD_COMMAND === incomingMessageType) { - const sessionStorage = await chrome.storage.session.get(); - if (Object.keys(sessionStorage).includes('allowedNumberOfTabs')) { - tabMode = sessionStorage.allowedNumberOfTabs; - } - - if (Object.keys(sessionStorage).includes('isUsingCDP')) { - globalIsUsingCDP = sessionStorage.isUsingCDP; - } - await chrome.storage.session.remove(['allowedNumberOfTabs', 'isUsingCDP']); + //If we get responseReceived before responseReceivedExtraInfo then we add the frame if to the object. + // If we get responseReceived afterwards then we will remove the add the frameId and then process the responseReceivedExtraInfo. + //Here if we get the audits before we get the frameId then we will add the audits to auditsIssueForTab else we will process the audits and delete it from the auditsIssueForTab. + if (method === 'Network.responseReceived' && params) { + const { + frameId = '', + requestId, + response: { url: requestUrl }, + } = params as Protocol.Network.ResponseReceivedEvent; - await chrome.storage.session.set({ - pendingReload: false, - }); + let finalFrameId = frameId; - await chrome.storage.sync.set({ - allowedNumberOfTabs: tabMode, - isUsingCDP: globalIsUsingCDP, - }); - - const tabs = await chrome.tabs.query({}); - - await Promise.all( - tabs.map(async ({ id }) => { - if (!id) { + if (!finalFrameId) { return; } - try { - if (globalIsUsingCDP) { - await chrome.debugger.attach({ tabId: id }, '1.3'); - await chrome.debugger.sendCommand({ tabId: id }, 'Network.enable'); - await chrome.debugger.sendCommand({ tabId: id }, 'Audits.enable'); - } else { - await chrome.debugger.detach({ tabId: id }); - } - } catch (error) { - //Fail silently - } - resetCookieBadgeText(id); - await reloadCurrentTab(id); - }) - ); - - await chrome.runtime.sendMessage({ - type: SERVICE_WORKER_RELOAD_MESSAGE, - }); - } - - if (!request?.payload?.tabId) { - return; - } - - const incomingMessageTabId = request.payload.tabId; - - if (DEVTOOLS_OPEN === incomingMessageType) { - const dataToSend: { [key: string]: string | boolean } = {}; - dataToSend['tabMode'] = tabMode; - - if (tabMode === 'single') { - dataToSend['tabToRead'] = tabToRead; - } - - if ( - !syncCookieStore?.tabs[incomingMessageTabId] && - tabMode === 'unlimited' - ) { - const currentTab = await getTab(incomingMessageTabId); - dataToSend['psatOpenedAfterPageLoad'] = request.payload.doNotReReload - ? false - : true; - syncCookieStore?.addTabData(incomingMessageTabId); - syncCookieStore?.updateUrl(incomingMessageTabId, currentTab?.url || ''); - } - - chrome.runtime.sendMessage({ - type: INITIAL_SYNC, - payload: dataToSend, - }); - - syncCookieStore?.updateDevToolsState(incomingMessageTabId, true); - - if (syncCookieStore?.tabsData[incomingMessageTabId]) { - syncCookieStore?.sendUpdatedDataToPopupAndDevTools( - incomingMessageTabId, - true - ); - } - } - if (POPUP_OPEN === incomingMessageType) { - const dataToSend: { [key: string]: string } = {}; - dataToSend['tabMode'] = tabMode; + targets = await chrome.debugger.getTargets(); + const setTargets = new Set(); - if (tabMode === 'single') { - dataToSend['tabToRead'] = tabToRead; - } - - chrome.runtime.sendMessage({ - type: INITIAL_SYNC, - payload: dataToSend, - }); - - syncCookieStore?.updatePopUpState(incomingMessageTabId, true); - - if (syncCookieStore?.tabsData[incomingMessageTabId]) { - syncCookieStore?.sendUpdatedDataToPopupAndDevTools( - incomingMessageTabId, - true - ); - } - } - - if (DEVTOOLS_CLOSE === incomingMessageType) { - syncCookieStore?.updateDevToolsState(incomingMessageTabId, false); - } - - if (POPUP_CLOSE === incomingMessageType) { - syncCookieStore?.updatePopUpState(incomingMessageTabId, false); - } - - if (DEVTOOLS_SET_JAVASCSCRIPT_COOKIE === incomingMessageType) { - syncCookieStore?.update(incomingMessageTabId, request?.payload?.cookieData); - } -}); + targets.map(({ id }) => { + setTargets.add(id); + return id; + }); -/** - * Fires when the browser window is opened. - * @see https://developer.chrome.com/docs/extensions/reference/api/windows#event-onCreated - */ -chrome.windows.onCreated.addListener(async () => { - const totalWindows = await chrome.windows.getAll(); + finalFrameId = syncCookieStore.addFrameIdAndRequestUrlToResourceMap( + tabId, + frameId, + setTargets, + requestUrl + ); + + if (!syncCookieStore.requestIdToCDPURLMapping[tabId]) { + syncCookieStore.requestIdToCDPURLMapping[tabId] = { + [requestId]: { + finalFrameId, + frameId, + url: requestUrl, + }, + }; + } else { + syncCookieStore.requestIdToCDPURLMapping[tabId] = { + ...syncCookieStore.requestIdToCDPURLMapping[tabId], + [requestId]: { + finalFrameId, + frameId, + url: requestUrl, + }, + }; + } - // @see https://developer.chrome.com/blog/longer-esw-lifetimes#whats_changed - // Doing this to keep the service worker alive so that we dont loose any data and introduce any bug. + if (syncCookieStore.unParsedResponseHeaders[tabId][requestId]) { + syncCookieStore.parseResponseHeaders( + syncCookieStore.unParsedResponseHeaders[tabId][requestId], + requestId, + tabId, + Array.from(new Set([finalFrameId, frameId])) + ); + } - // We do not want to clear content settings if a user has create one more window. - if (totalWindows.length < 2) { - chrome.contentSettings.cookies.clear({}); - } -}); + if (syncCookieStore.unParsedRequestHeaders[tabId][requestId]) { + syncCookieStore.parseRequestHeaders( + syncCookieStore.unParsedRequestHeaders[tabId][requestId], + requestId, + tabId, + Array.from(new Set([finalFrameId, frameId])) + ); + } -chrome.storage.sync.onChanged.addListener( - async (changes: { [key: string]: chrome.storage.StorageChange }) => { - if ( - !changes?.allowedNumberOfTabs || - !changes?.allowedNumberOfTabs?.newValue || - !changes?.allowedNumberOfTabs?.oldValue - ) { - return; - } - tabMode = changes.allowedNumberOfTabs.newValue; - - const tabs = await chrome.tabs.query({}); - - if (changes?.allowedNumberOfTabs?.newValue === 'single') { - tabToRead = ''; - try { - await chrome.runtime.sendMessage({ - type: INITIAL_SYNC, - payload: { - tabMode, - tabToRead: tabToRead, - }, - }); - } catch (error) { - //Fail silently + delete syncCookieStore.requestIdToCDPURLMapping[tabId][requestId]; + return; } - tabs.map((tab) => { - if (!tab?.id) { - return tab; + if (method === 'Network.responseReceivedExtraInfo') { + const { headers, requestId } = + params as Protocol.Network.ResponseReceivedExtraInfoEvent; + + // Sometimes CDP gives "set-cookie" and sometimes it gives "Set-Cookie". + if (!headers['set-cookie'] && !headers['Set-Cookie']) { + return; } - resetCookieBadgeText(tab.id); + if (syncCookieStore.requestIdToCDPURLMapping[tabId][requestId]) { + const frameIds = Array.from( + new Set([ + syncCookieStore.requestIdToCDPURLMapping[tabId][requestId] + ?.finalFrameId, + syncCookieStore.requestIdToCDPURLMapping[tabId][requestId] + ?.frameId, + ]) + ); - syncCookieStore?.removeTabData(tab.id); + syncCookieStore.parseResponseHeaders( + params as Protocol.Network.ResponseReceivedExtraInfoEvent, + requestId, + tabId, + frameIds + ); - return tab; - }); - } else { - try { - await chrome.runtime.sendMessage({ - type: INITIAL_SYNC, - payload: { - tabMode, - tabToRead: tabToRead, - }, - }); - } catch (error) { - //Fail silently + if (syncCookieStore.unParsedRequestHeaders[tabId][requestId]) { + syncCookieStore.parseRequestHeaders( + syncCookieStore.unParsedRequestHeaders[tabId][requestId], + requestId, + tabId, + frameIds + ); + } + } else { + syncCookieStore.unParsedResponseHeaders[tabId][requestId] = + params as Protocol.Network.ResponseReceivedExtraInfoEvent; + } + return; } - tabs.forEach((tab) => { - if (!tab?.id) { + if (method === 'Audits.issueAdded' && params) { + const auditParams = params as Protocol.Audits.IssueAddedEvent; + const { + code, + details: { cookieIssueDetails }, + } = auditParams.issue; + + if (code !== 'CookieIssue' && !cookieIssueDetails) { return; } - syncCookieStore?.addTabData(tab.id); - syncCookieStore?.sendUpdatedDataToPopupAndDevTools(tab.id); - syncCookieStore?.updateDevToolsState(tab.id, true); - }); - } - } -); - -chrome.storage.sync.onChanged.addListener( - async (changes: { [key: string]: chrome.storage.StorageChange }) => { - if ( - !changes?.isUsingCDP || - typeof changes?.isUsingCDP?.newValue === 'undefined' || - typeof changes?.isUsingCDP?.oldValue === 'undefined' - ) { - return; - } - - globalIsUsingCDP = changes?.isUsingCDP?.newValue; - const tabs = await chrome.tabs.query({}); + if ( + !cookieIssueDetails?.cookie || + !cookieIssueDetails?.cookieWarningReasons || + !cookieIssueDetails?.cookieExclusionReasons || + !cookieIssueDetails?.request + ) { + return; + } - if (!changes?.isUsingCDP?.newValue) { - await Promise.all( - tabs.map(async ({ id }) => { - if (!id) { - return; - } + const { requestId = '', url: requestUrl = '' } = + cookieIssueDetails.request; - try { - await chrome.debugger.detach({ tabId: id }); - syncCookieStore?.sendUpdatedDataToPopupAndDevTools(id); - } catch (error) { - // eslint-disable-next-line no-console - console.warn(error); - } - }) - ); - } else { - tabs.forEach(({ id }) => { - if (!id) { + if (!requestId || !requestUrl) { return; } - syncCookieStore?.sendUpdatedDataToPopupAndDevTools(id); - }); + const cookieObjectToUpdate = createCookieFromAuditsIssue( + cookieIssueDetails, + syncCookieStore?.getTabUrl(Number(tabId)) ?? '', + [], + syncCookieStore.requestIdToCDPURLMapping[tabId][requestId]?.url, + syncCookieStore.cookieDB ?? {} + ); + + if (cookieObjectToUpdate) { + syncCookieStore?.update(Number(tabId), [cookieObjectToUpdate]); + } + return; + } + } catch (error) { + //Fail silently. + // eslint-disable-next-line no-console + console.warn(error); } - } -); + })(); +}); diff --git a/packages/extension/src/serviceWorker/parseRequestCookieHeader.ts b/packages/extension/src/serviceWorker/parseRequestCookieHeader.ts index 96e0796a0..83995cd25 100644 --- a/packages/extension/src/serviceWorker/parseRequestCookieHeader.ts +++ b/packages/extension/src/serviceWorker/parseRequestCookieHeader.ts @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - /** * External dependencies. */ @@ -25,7 +24,7 @@ import { type CookieAnalytics, type CookieDatabase, REQUEST_EVENT, -} from '@ps-analysis-tool/common'; +} from '@google-psat/common'; /** * Internal dependencies. @@ -39,7 +38,7 @@ import { createCookieObject } from './createCookieObject'; * @param {string} value header value * @param {CookieDatabase} dictionary Dictionary from open cookie database * @param {string} tabUrl top url of the tab from which the request originated. - * @param {number} frameId Id of the frame the cookie is used in. + * @param {string} frameId Id of the frame the cookie is used in. * @param {Protocol.Network.Cookie[]} cdpCookiesList List cookies from the request. * @param {string} requestId Request id. * @returns {CookieData[]} Parsed cookie object array. @@ -49,7 +48,7 @@ const parseRequestCookieHeader = ( value: string, dictionary: CookieDatabase, tabUrl: string, - frameId: number, + frameId: string, cdpCookiesList: Protocol.Network.Cookie[], requestId: string ): CookieData[] => { diff --git a/packages/extension/src/serviceWorker/parseResponseCookieHeader.ts b/packages/extension/src/serviceWorker/parseResponseCookieHeader.ts index 11a47f3cf..745599b94 100644 --- a/packages/extension/src/serviceWorker/parseResponseCookieHeader.ts +++ b/packages/extension/src/serviceWorker/parseResponseCookieHeader.ts @@ -24,7 +24,7 @@ import { type CookieAnalytics, type CookieDatabase, RESPONSE_EVENT, -} from '@ps-analysis-tool/common'; +} from '@google-psat/common'; import { getDomain } from 'tldts'; import type { Protocol } from 'devtools-protocol'; @@ -40,7 +40,7 @@ import { createCookieObject } from './createCookieObject'; * @param {string} value header value * @param {CookieDatabase} dictionary Dictionary from open cookie database * @param {string} tabUrl top url of the tab from which the request originated. - * @param {number} frameId Id of a frame in which this cookie is used. + * @param {string} frameId Id of a frame in which this cookie is used. * @param {Protocol.Network.Cookie[]} cdpCookiesList List cookies from the request. * @param {string} requestId Request id. * @param {boolean} globalIsUsingCDP Boolean to determie whether or not CDP is being used. @@ -51,7 +51,7 @@ const parseResponseCookieHeader = ( value: string, dictionary: CookieDatabase, tabUrl: string, - frameId: number, + frameId: string, cdpCookiesList: Protocol.Network.Cookie[], requestId: string, globalIsUsingCDP: boolean diff --git a/packages/extension/src/serviceWorker/tests/parseRequestCookieHeader.ts b/packages/extension/src/serviceWorker/tests/parseRequestCookieHeader.ts index 0c86ca57d..703d86335 100644 --- a/packages/extension/src/serviceWorker/tests/parseRequestCookieHeader.ts +++ b/packages/extension/src/serviceWorker/tests/parseRequestCookieHeader.ts @@ -18,7 +18,7 @@ * External dependencies. */ import SinonChrome from 'sinon-chrome'; -import { emptyAnalytics } from '@ps-analysis-tool/common'; +import { emptyAnalytics } from '@google-psat/common'; /** * Internal dependencies. diff --git a/packages/extension/src/serviceWorker/tests/parseResponseCookieHeader.ts b/packages/extension/src/serviceWorker/tests/parseResponseCookieHeader.ts index ce0da36bd..195425d5e 100644 --- a/packages/extension/src/serviceWorker/tests/parseResponseCookieHeader.ts +++ b/packages/extension/src/serviceWorker/tests/parseResponseCookieHeader.ts @@ -18,7 +18,7 @@ * External dependencies. */ import SinonChrome from 'sinon-chrome'; -import { emptyAnalytics } from '@ps-analysis-tool/common'; +import { emptyAnalytics } from '@google-psat/common'; /** * Internal dependencies. */ diff --git a/packages/extension/src/store/synchnorousCookieStore.ts b/packages/extension/src/store/synchnorousCookieStore.ts index ae2fa595f..b3b59598b 100644 --- a/packages/extension/src/store/synchnorousCookieStore.ts +++ b/packages/extension/src/store/synchnorousCookieStore.ts @@ -20,15 +20,21 @@ import { getCookieKey, type CookieData, type BlockedReason, -} from '@ps-analysis-tool/common'; + parseResponseReceivedExtraInfo, + type CookieDatabase, + parseRequestWillBeSentExtraInfo, + deriveBlockingStatus, +} from '@google-psat/common'; import type { Protocol } from 'devtools-protocol'; /** * Internal dependencies. */ import updateCookieBadgeText from './utils/updateCookieBadgeText'; -import { deriveBlockingStatus } from './utils/deriveBlockingStatus'; import { NEW_COOKIE_DATA } from '../constants'; +import isValidURL from '../utils/isValidURL'; +import { doesFrameExist } from '../utils/doesFrameExist'; +import { fetchDictionary } from '../utils/fetchCookieDictionary'; class SynchnorousCookieStore { /** @@ -40,6 +46,66 @@ class SynchnorousCookieStore { }; } = {}; + /** + * The cookie data of the tabs. + */ + tabMode: 'single' | 'unlimited' = 'single'; + + /** + * CookieDatabase to run analytics match on. + */ + cookieDB: CookieDatabase | null = null; + + /** + * The cookie data of the tabs. + */ + tabToRead = ''; + + /** + * This variable stores the requestId and required information like frameId, URL and ancestorFrameId for a request associated to that tab. + */ + requestIdToCDPURLMapping: { + [tabId: string]: { + [requestId: string]: { + frameId: string; + url: string; + finalFrameId: string; + }; + }; + } = {}; + + /** + * This variable stores the unParsedRequest headers received from Network.requestWillBeSentExtraInfo. + * These are the requests whose Network.requestWillBeSent counter part havent yet been fired. + */ + unParsedRequestHeaders: { + [tabId: string]: { + [requestId: string]: Protocol.Network.RequestWillBeSentExtraInfoEvent; + }; + } = {}; + + /** + * This variable stores the unParsedResonse headers received from Network.responseReceivedExtraInfo. + * These are the responses whose Network.responseReceived counter part havent yet been fired. + */ + unParsedResponseHeaders: { + [tabId: string]: { + [requestId: string]: Protocol.Network.ResponseReceivedExtraInfoEvent; + }; + } = {}; + + /** + * This variable stores requestUrl related to a particular frameId of a particular tab. + * These urls are used as arguments to call Network.getCookies on the frameId. + */ + frameIdToResourceMap: { + [tabId: string]: { + [frameId: string]: Set; + }; + } = {}; + + globalIsUsingCDP = false; + /** * Required data of the tabs and PSAT panel of the tab. */ @@ -49,7 +115,8 @@ class SynchnorousCookieStore { devToolsOpenState: boolean; popupOpenState: boolean; newUpdates: number; - portRef: any; + frameIDURLSet: Record; + parentChildFrameAssociation: Record; }; } = {}; @@ -70,174 +137,171 @@ class SynchnorousCookieStore { } /** - * Update cookie store. - * @param {number} tabId Tab id. - * @param {Array} cookies Cookies data. + * This function adds frame to the appropriate tab. + * @param {number} tabId The tabId of the event to which the event is pointing to. + * @param {string} frameId The frameId of the frame to determine which the requestUrl is for. + * @param {Set} setTargets Set of targets available in the tab. + * @param {string} requestUrl The request url to be added to the frameResouceMap. + * @returns {string} An alternate frameId if available. */ - // eslint-disable-next-line complexity - update(tabId: number, cookies: CookieData[]) { - try { - if (!this.tabsData[tabId] || !this.tabs[tabId]) { - return; - } - - for (const cookie of cookies) { - const cookieKey = getCookieKey(cookie.parsedCookie); - - if (!cookieKey) { - continue; - } - - // Merge in previous blocked reasons. - const blockedReasons: BlockedReason[] = [ - ...new Set([ - ...(cookie?.blockedReasons ?? []), - ...(this.tabsData[tabId]?.[cookieKey]?.blockedReasons ?? []), - ]), - ]; - - const warningReasons = Array.from( - new Set([ - ...(cookie?.warningReasons ?? []), - ...(this.tabsData[tabId]?.[cookieKey]?.warningReasons ?? []), - ]) - ); - - const frameIdList = Array.from( - new Set([ - ...((cookie?.frameIdList ?? []) as number[]), - ...((this.tabsData[tabId]?.[cookieKey]?.frameIdList ?? - []) as number[]), - ]) - ); - - if (this.tabsData[tabId]?.[cookieKey]) { - this.tabs[tabId].newUpdates++; - // Merge in previous warning reasons. - const parsedCookie = { - ...this.tabsData[tabId][cookieKey].parsedCookie, - ...cookie.parsedCookie, - priority: - cookie.parsedCookie?.priority ?? - this.tabsData[tabId][cookieKey].parsedCookie?.priority ?? - 'Medium', - partitionKey: - cookie.parsedCookie?.partitionKey ?? - this.tabsData[tabId][cookieKey].parsedCookie?.partitionKey, - }; - - const networkEvents: CookieData['networkEvents'] = { - requestEvents: [ - ...(this.tabsData[tabId][cookieKey]?.networkEvents - ?.requestEvents || []), - ...(cookie.networkEvents?.requestEvents || []), - ], - responseEvents: [ - ...(this.tabsData[tabId][cookieKey]?.networkEvents - ?.responseEvents || []), - ...(cookie.networkEvents?.responseEvents || []), - ], - }; - - this.tabsData[tabId][cookieKey] = { - ...this.tabsData[tabId][cookieKey], - ...cookie, - // Insert data receieved from CDP or new data recieved through webRequest API. - parsedCookie, - isBlocked: blockedReasons.length > 0, - blockedReasons, - networkEvents, - blockingStatus: deriveBlockingStatus(networkEvents), - warningReasons, - url: this.tabsData[tabId][cookieKey].url ?? cookie.url, - headerType: - this.tabsData[tabId][cookieKey].headerType === 'javascript' - ? this.tabsData[tabId][cookieKey].headerType - : cookie.headerType, - frameIdList, - exemptionReason: - cookie?.exemptionReason || - this.tabsData[tabId][cookieKey]?.exemptionReason, - }; - } else { - this.tabs[tabId].newUpdates++; - this.tabsData[tabId][cookieKey] = { - ...cookie, - blockingStatus: deriveBlockingStatus(cookie.networkEvents), - }; - } + addFrameIdAndRequestUrlToResourceMap( + tabId: string, + frameId: string, + setTargets: Set, + requestUrl: string + ) { + if (!this.frameIdToResourceMap[tabId][frameId]) { + this.frameIdToResourceMap[tabId][frameId] = new Set(); + } + if (setTargets.has(frameId)) { + this.frameIdToResourceMap[tabId][frameId].add(requestUrl); + return frameId; + } else { + const ancestorFrameId = this.findFirstAncestorFrameId( + tabId, + frameId, + setTargets + ); + + if (ancestorFrameId) { + this.frameIdToResourceMap[tabId][frameId].add(requestUrl); + return ancestorFrameId; } - - updateCookieBadgeText(this.tabsData[tabId], tabId); - } catch (error) { - //Fail silently - // eslint-disable-next-line no-console - console.warn(error); + return frameId; } } /** - * Clears the whole storage. + * This function parses response headers + * @param {Protocol.Network.ResponseReceivedExtraInfoEvent} response The response to be parsed. + * @param {string} requestId This is used to get the related data for parsing the response. + * @param {string} tabId The tabId this request is associated to. + * @param {string[]} frameIds This is used to associate the cookies from request to set of frameIds. */ - clear() { - Object.keys(this.tabsData).forEach((key) => { - delete this.tabsData[Number(key)]; - }); - Object.keys(this.tabs).forEach((key) => { - delete this.tabs[Number(key)]; - }); - this.tabsData = {}; - this.tabs = {}; + parseResponseHeaders( + response: Protocol.Network.ResponseReceivedExtraInfoEvent, + requestId: string, + tabId: string, + frameIds: string[] + ) { + const { + headers, + blockedCookies, + cookiePartitionKey = '', + exemptedCookies, + } = response; + + const cookies: CookieData[] = parseResponseReceivedExtraInfo( + headers, + blockedCookies, + exemptedCookies, + cookiePartitionKey, + this.requestIdToCDPURLMapping[tabId][requestId]?.url ?? '', + this.tabs[Number(tabId)].url ?? '', + this.cookieDB ?? {}, + frameIds, + requestId + ); + this.update(Number(tabId), cookies); + + delete this.unParsedResponseHeaders[tabId][requestId]; } /** - * Gets the tabUrl for the given tab id if tab exists. - * @param {number} tabId Tab id. - * @returns {string | null} The url of the tab if exists else null. + * This function parses request headers + * @param {Protocol.Network.RequestWillBeSentExtraInfoEvent} request The response to be parsed. + * @param {string} requestId This is used to get the related data for parsing the response. + * @param {string} tabId The tabId this request is associated to. + * @param {string[]} frameIds This is used to associate the cookies from request to set of frameIds. */ - getTabUrl(tabId: number): string | null { - if (!this.tabs[tabId]) { - return null; + parseRequestHeaders( + request: Protocol.Network.RequestWillBeSentExtraInfoEvent, + requestId: string, + tabId: string, + frameIds: string[] + ) { + const { associatedCookies } = request; + + const cookies: CookieData[] = parseRequestWillBeSentExtraInfo( + associatedCookies, + this.cookieDB ?? {}, + this.requestIdToCDPURLMapping[tabId][requestId]?.url ?? '', + this.tabs[Number(tabId)].url ?? '', + frameIds, + requestId + ); + + delete this.unParsedRequestHeaders[tabId][requestId]; + if (cookies.length === 0) { + return; } - return this.tabs[tabId].url; + this.update(Number(tabId), cookies); + delete this.unParsedRequestHeaders[tabId][requestId]; } /** - * Update tab url for given tab - * @param {number} tabId The url whose url needs to be update. - * @param {string} url The updated URL. + * This function adds frame to the appropriate tab. + * @param {number} tabId The tabId of the event if available. + * @param {string} targetId The targetId for which frame has to be added. + * @param {string} frameId The frameId of the frame that has been added. + * @param {string} parentFrameId The parent frame id to which the frame has been added to. + * @param {string} frameUrl This is and optional parameter that is sent to decide if we need to run the command for updating the frame url with async function. */ - updateUrl(tabId: number, url: string) { - if (!this.tabs[tabId]) { + async addFrameToTabAndUpdateMetadata( + tabId: number | null, + targetId: string | null, + frameId: string, + parentFrameId: string, + frameUrl?: string + ) { + if (!tabId && !targetId) { return; - } else { - this.tabs[tabId].url = url; } - } - /** - * Update Popup State for given tab - * @param {number} tabId The tabId whose popup state needs to be update. - * @param {boolean} state The updated popup state. - */ - updatePopUpState(tabId: number, state: boolean) { - if (!this.tabs[tabId]) { - return; + if (tabId) { + this.updateParentChildFrameAssociation(tabId, frameId, parentFrameId); + if (frameUrl) { + this.updateFrameIdURLSet(tabId, frameId, frameUrl); + return; + } else { + await this.updateFrameIdURLSet(tabId, frameId); + return; + } } - this.tabs[tabId].popupOpenState = state; - } - /** - * Update Devtools State for given tab - * @param {number} tabId The tabId whose devtools state needs to be update. - * @param {boolean} state The updated devtools state. - */ - updateDevToolsState(tabId: number, state: boolean) { - if (!this.tabs[tabId]) { - return; - } - this.tabs[tabId].devToolsOpenState = state; + const isFrameIdInPage = await doesFrameExist(frameId); + + await Promise.all( + Object.keys(this?.tabs ?? {}).map(async (key) => { + const currentTabFrameIdSet = this.getFrameIDSet(Number(key)); + if ( + targetId && + currentTabFrameIdSet && + currentTabFrameIdSet.has(targetId) + ) { + this.updateParentChildFrameAssociation( + Number(key), + frameId, + parentFrameId + ); + + if (frameUrl) { + this.updateFrameIdURLSet( + Number(key), + isFrameIdInPage ? frameId : '', + frameUrl + ); + return key; + } else { + await this.updateFrameIdURLSet(Number(key), frameId); + return key; + } + } + + return key; + }) + ); } /** @@ -288,21 +352,6 @@ class SynchnorousCookieStore { } } - /** - * Clear cookie data from cached cookie data for the given tabId - * @param {number} tabId The active tab id. - */ - removeCookieData(tabId: number) { - if (!this.tabs[tabId] || !this.tabsData[tabId]) { - return; - } - - delete this.tabsData[tabId]; - this.tabsData[tabId] = {}; - this.tabs[tabId].newUpdates = 0; - this.sendUpdatedDataToPopupAndDevTools(tabId, true); - } - /** * Creates an entry for a tab * @param {number} tabId The tab id. @@ -323,8 +372,133 @@ class SynchnorousCookieStore { devToolsOpenState: false, popupOpenState: false, newUpdates: 0, - portRef: null, + frameIDURLSet: {}, + parentChildFrameAssociation: {}, }; + (async () => { + if (!this.cookieDB) { + this.cookieDB = await fetchDictionary(); + } + })(); + } + + /** + * Clears the whole storage. + */ + clear() { + Object.keys(this.tabsData).forEach((key) => { + delete this.tabsData[Number(key)]; + }); + Object.keys(this.tabs).forEach((key) => { + delete this.tabs[Number(key)]; + }); + this.tabsData = {}; + this.tabs = {}; + } + + /** + * This function will deinitialise variables for given tab. + * @param {string} tabId The tab whose data has to be deinitialised. + */ + deinitialiseVariablesForTab(tabId: string) { + delete this.unParsedRequestHeaders[tabId]; + delete this.unParsedResponseHeaders[tabId]; + delete this.requestIdToCDPURLMapping[tabId]; + delete this.frameIdToResourceMap[tabId]; + } + + /** + * This function will find the first ancestor frameId + * @param {string} tabId The tab where the operation has to be performed. + * @param {string} frameId The frameId whose ancestor has to be searched. + * @param {Set} targetSet The current set of targets. + * @returns {string | null} The first ancestor frameId. + */ + findFirstAncestorFrameId( + tabId: string, + frameId: string, + targetSet: Set + ): string | null { + if (targetSet.has(frameId)) { + return frameId; + } else { + if (this.tabs[Number(tabId)]?.parentChildFrameAssociation[frameId]) { + return this.findFirstAncestorFrameId( + tabId, + this.tabs[Number(tabId)]?.parentChildFrameAssociation[frameId], + targetSet + ); + } + return null; + } + } + + /** + * Gets the tabUrl for the given tab id if tab exists. + * @param {number} tabId Tab id. + * @returns {string | null} The url of the tab if exists else null. + */ + getTabUrl(tabId: number): string | null { + if (!this.tabs[tabId]) { + return null; + } + + return this.tabs[tabId].url; + } + + /** + * Gets the frameIDSet for the given tab id if tab exists. + * @param {number} tabId Tab id. + * @returns {string | null} The url of the tab if exists else null. + */ + getFrameIDSet(tabId: number): Set | null { + if (!this.tabs[tabId]) { + return null; + } + + const formedSet = new Set(); + + Object.keys(this.tabs[tabId].parentChildFrameAssociation).forEach((key) => { + formedSet.add(key); + formedSet.add(this.tabs[tabId].parentChildFrameAssociation[key]); + }); + return formedSet; + } + + /** + * This function will initialise variables for given tab. + * @param {string} tabId The tab whose data has to be initialised. + */ + initialiseVariablesForNewTab(tabId: string) { + this.unParsedRequestHeaders[tabId] = {}; + this.unParsedResponseHeaders[tabId] = {}; + this.requestIdToCDPURLMapping[tabId] = {}; + this.frameIdToResourceMap[tabId] = {}; + //@ts-ignore + globalThis.PSATAdditionalData = { + unParsedRequestHeaders: this.unParsedRequestHeaders, + unParsedResponseHeaders: this.unParsedResponseHeaders, + requestIdToCDPURLMapping: this.requestIdToCDPURLMapping, + frameIdToResourceMap: this.frameIdToResourceMap, + }; + } + + /** + * Clear cookie data from cached cookie data for the given tabId + * @param {number} tabId The active tab id. + */ + removeCookieData(tabId: number) { + if (!this.tabs[tabId] || !this.tabsData[tabId]) { + return; + } + + delete this.tabsData[tabId]; + this.tabsData[tabId] = {}; + this.tabs[tabId].newUpdates = 0; + this.tabs[tabId].frameIDURLSet = {}; + this.tabs[tabId].parentChildFrameAssociation = {}; + + this.sendUpdatedDataToPopupAndDevTools(tabId, true); } /** @@ -367,10 +541,9 @@ class SynchnorousCookieStore { try { if ( - ((this.tabs[tabId].devToolsOpenState || - this.tabs[tabId].popupOpenState) && - this.tabs[tabId].newUpdates > 0) || - overrideForInitialSync + this.tabs[tabId].devToolsOpenState || + (this.tabs[tabId].popupOpenState && + (overrideForInitialSync || this.tabs[tabId].newUpdates > 0)) ) { sentMessageAnyWhere = true; @@ -379,6 +552,9 @@ class SynchnorousCookieStore { payload: { tabId, cookieData: this.tabsData[tabId], + extraData: { + extraFrameData: this.tabs[tabId].frameIDURLSet, + }, }, }); } @@ -392,6 +568,212 @@ class SynchnorousCookieStore { //Fail silently. Ignoring the console.warn here because the only error this will throw is of "Error: Could not establish connection". } } + + /** + * Update cookie store. + * @param {number} tabId Tab id. + * @param {Array} cookies Cookies data. + */ + // eslint-disable-next-line complexity + update(tabId: number, cookies: CookieData[]) { + try { + if (!this.tabsData[tabId] || !this.tabs[tabId]) { + return; + } + + for (const cookie of cookies) { + const cookieKey = getCookieKey(cookie.parsedCookie); + if (!cookieKey) { + continue; + } + + // Merge in previous blocked reasons. + const blockedReasons: BlockedReason[] = [ + ...new Set([ + ...(cookie?.blockedReasons ?? []), + ...(this.tabsData[tabId]?.[cookieKey]?.blockedReasons ?? []), + ]), + ]; + + const warningReasons = Array.from( + new Set([ + ...(cookie?.warningReasons ?? []), + ...(this.tabsData[tabId]?.[cookieKey]?.warningReasons ?? []), + ]) + ); + + const frameIdList = Array.from( + new Set([ + ...((cookie?.frameIdList ?? []) as number[]), + ...((this.tabsData[tabId]?.[cookieKey]?.frameIdList ?? + []) as number[]), + ]) + ).map((frameId) => frameId.toString()); + + if (this.tabsData[tabId]?.[cookieKey]) { + this.tabs[tabId].newUpdates++; + // Merge in previous warning reasons. + const parsedCookie = { + ...this.tabsData[tabId][cookieKey].parsedCookie, + ...cookie.parsedCookie, + priority: + cookie.parsedCookie?.priority ?? + this.tabsData[tabId][cookieKey].parsedCookie?.priority ?? + 'Medium', + partitionKey: + cookie.parsedCookie?.partitionKey ?? + this.tabsData[tabId][cookieKey].parsedCookie?.partitionKey, + }; + + const networkEvents: CookieData['networkEvents'] = { + requestEvents: [ + ...(this.tabsData[tabId][cookieKey]?.networkEvents + ?.requestEvents || []), + ...(cookie.networkEvents?.requestEvents || []), + ], + responseEvents: [ + ...(this.tabsData[tabId][cookieKey]?.networkEvents + ?.responseEvents || []), + ...(cookie.networkEvents?.responseEvents || []), + ], + }; + this.tabsData[tabId][cookieKey] = { + ...this.tabsData[tabId][cookieKey], + ...cookie, + parsedCookie, + isBlocked: blockedReasons.length > 0, + blockedReasons, + networkEvents, + blockingStatus: deriveBlockingStatus(networkEvents), + warningReasons, + url: this.tabsData[tabId][cookieKey].url ?? cookie.url, + headerType: + this.tabsData[tabId][cookieKey].headerType === 'javascript' + ? this.tabsData[tabId][cookieKey].headerType + : cookie.headerType, + frameIdList, + exemptionReason: + cookie?.exemptionReason || + this.tabsData[tabId][cookieKey]?.exemptionReason, + }; + } else { + this.tabs[tabId].newUpdates++; + this.tabsData[tabId][cookieKey] = { + ...cookie, + blockingStatus: deriveBlockingStatus(cookie.networkEvents), + }; + } + } + + updateCookieBadgeText(this.tabsData[tabId], tabId); + } catch (error) { + //Fail silently + // eslint-disable-next-line no-console + console.warn(error); + } + } + + /** + * Update FrameId set for a given url for a given tab. + * @param {number} tabId The url whose url needs to be update. + * @param {string | undefined} frameIdToAdd The new frameId to be added. + * @param {string | undefined} parentFrameId The parent frame id the frameIdToAdd is associated to + */ + updateParentChildFrameAssociation( + tabId: number, + frameIdToAdd: string | undefined, + parentFrameId: string | undefined + ) { + if (!parentFrameId || !frameIdToAdd) { + return; + } + + if (!this.tabs[tabId]) { + return; + } else { + this.tabs[tabId].parentChildFrameAssociation[frameIdToAdd] = + parentFrameId; + } + } + + /** + * Updates FrameIdURLSet set for a given url for a given tab. + * @param {number} tabId The url whose url needs to be update. + * @param {string} frameId The new frameId to be added. + * @param {string} targetUrl TargetUrl to be updated in frameIdSet. + */ + async updateFrameIdURLSet(tabId: number, frameId: string, targetUrl = '') { + try { + let url = isValidURL(targetUrl) ? new URL(targetUrl).origin : ''; + + if (!url) { + const info = (await chrome.debugger.sendCommand( + { targetId: frameId }, + 'Target.getTargetInfo', + { targetId: frameId } + )) as { [key: string]: Protocol.Target.TargetInfo }; + + if (!info) { + return; + } + + url = isValidURL(info.targetInfo.url) + ? new URL(info.targetInfo.url).origin + : ''; + } + + if (!url) { + return; + } + + if (!this.tabs[tabId].frameIDURLSet[url]) { + this.tabs[tabId].frameIDURLSet[url] = []; + } + + this.tabs[tabId].frameIDURLSet[url] = [ + ...new Set([...this.tabs[tabId].frameIDURLSet[url], frameId]), + ]; + } catch (error) { + //Fail silently. This is done because we are going to get either 2 errors invalid url or chrome.debugger error. + } + } + + /** + * Update tab url for given tab + * @param {number} tabId The url whose url needs to be update. + * @param {string} url The updated URL. + */ + updateUrl(tabId: number, url: string) { + if (!this.tabs[tabId]) { + return; + } else { + this.tabs[tabId].url = url; + } + } + + /** + * Update Popup State for given tab + * @param {number} tabId The tabId whose popup state needs to be update. + * @param {boolean} state The updated popup state. + */ + updatePopUpState(tabId: number, state: boolean) { + if (!this.tabs[tabId]) { + return; + } + this.tabs[tabId].popupOpenState = state; + } + + /** + * Update Devtools State for given tab + * @param {number} tabId The tabId whose devtools state needs to be update. + * @param {boolean} state The updated devtools state. + */ + updateDevToolsState(tabId: number, state: boolean) { + if (!this.tabs[tabId]) { + return; + } + this.tabs[tabId].devToolsOpenState = state; + } } -export default SynchnorousCookieStore; +export default new SynchnorousCookieStore(); diff --git a/packages/extension/src/store/tests/synchnorousCookieStore.ts b/packages/extension/src/store/tests/synchnorousCookieStore.ts index 19bfac91a..be257e247 100644 --- a/packages/extension/src/store/tests/synchnorousCookieStore.ts +++ b/packages/extension/src/store/tests/synchnorousCookieStore.ts @@ -14,17 +14,31 @@ * limitations under the License. */ /** - * Internal dependencies + * External dependencies. */ import SinonChrome from 'sinon-chrome'; + +/** + * Internal dependencies + */ +//@ts-ignore +// eslint-disable-next-line import/no-unresolved +import OpenCookieDatabase from 'ps-analysis-tool/assets/data/open-cookie-database.json'; import data from '../../utils/test-data/cookieMockData'; -import SynchnorousCookieStore from '../synchnorousCookieStore'; +import synchnorousCookieStore from '../synchnorousCookieStore'; -let synchnorousCookieStore: SynchnorousCookieStore; describe('SynchnorousCookieStore:', () => { beforeAll(() => { globalThis.chrome = SinonChrome as unknown as typeof chrome; - synchnorousCookieStore = new SynchnorousCookieStore(); + globalThis.fetch = function () { + return Promise.resolve({ + json: () => + Promise.resolve({ + ...OpenCookieDatabase, + }), + text: () => Promise.resolve({}), + }); + } as unknown as typeof fetch; }); beforeEach(() => { diff --git a/packages/extension/src/store/types.ts b/packages/extension/src/store/types.ts index 1321eac79..aae0f4833 100644 --- a/packages/extension/src/store/types.ts +++ b/packages/extension/src/store/types.ts @@ -16,7 +16,7 @@ /** * External dependencies. */ -import type { CookieData } from '@ps-analysis-tool/common'; +import type { CookieData } from '@google-psat/common'; export type PreferenceKeyValues = | 'columnSorting' diff --git a/packages/extension/src/store/utils/updateCookieBadgeText.ts b/packages/extension/src/store/utils/updateCookieBadgeText.ts index 0ce8c13c0..732b8c13f 100644 --- a/packages/extension/src/store/utils/updateCookieBadgeText.ts +++ b/packages/extension/src/store/utils/updateCookieBadgeText.ts @@ -16,7 +16,7 @@ /** * External dependencies */ -import type { CookieData } from '@ps-analysis-tool/common'; +import type { CookieData } from '@google-psat/common'; /** * Update cookie badge text. * @param storage {object} The storage object from local store. diff --git a/packages/extension/src/utils/createCookieFromAuditsIssue.ts b/packages/extension/src/utils/createCookieFromAuditsIssue.ts new file mode 100644 index 000000000..6daa8639c --- /dev/null +++ b/packages/extension/src/utils/createCookieFromAuditsIssue.ts @@ -0,0 +1,136 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies + */ +import { + REQUEST_EVENT, + type CookieData, + isFirstParty, + findAnalyticsMatch, + type CookieDatabase, + RESPONSE_EVENT, + auditsToNetworkMap, +} from '@google-psat/common'; +import type { Protocol } from 'devtools-protocol'; +import { type Cookie, parse } from 'simple-cookie'; + +/** + * This function creates cookie object from the audits issue. + * @param {Protocol.Audits.CookieIssueDetails} issue The cookieIssue Details. + * @param {string} tabUrl The url of the tab. + * @param {string[]} frameIds frameId this cookie belongs to. + * @param {string} requestUrl The url of the request this cookie belongs to. + * @param {CookieDatabase} cookieDB CookieDatabase to find analytics match. + * @returns {CookieData | null} The create cookie object. + */ +export default function createCookieFromAuditsIssue( + issue: Protocol.Audits.CookieIssueDetails, + tabUrl: string, + frameIds: string[], + requestUrl: string, + cookieDB: CookieDatabase +) { + const { + cookie, + cookieExclusionReasons, + cookieWarningReasons, + rawCookieLine, + request, + operation, + } = issue; + + if (!request) { + return null; + } + + const { requestId, url = '' } = request; + + if (!cookie && !rawCookieLine) { + return null; + } + + let generatedCookie: Protocol.Audits.AffectedCookie | Cookie | undefined = + cookie; + + if (cookie) { + generatedCookie = cookie; + } + + if (!cookie && rawCookieLine) { + generatedCookie = parse(rawCookieLine); + } + + const requestEvents = []; + const responseEvents = []; + + if (operation === 'ReadCookie') { + requestEvents.push({ + type: REQUEST_EVENT.CDP_REQUEST_WILL_BE_SENT_EXTRA_INFO, + requestId, + url, + blocked: Boolean(cookieExclusionReasons.length), + timeStamp: Date.now(), + }); + } + + if (operation === 'SetCookie') { + responseEvents.push({ + type: RESPONSE_EVENT.CDP_RESPONSE_RECEIVED_EXTRA_INFO, + requestId, + url, + blocked: Boolean(cookieExclusionReasons.length), + timeStamp: Date.now(), + }); + } + + const modifiedCookieExclusionReasons = cookieExclusionReasons.map( + (reason) => { + if (reason.toLowerCase().startsWith('exclude')) { + return auditsToNetworkMap[ + reason + ] as Protocol.Network.CookieBlockedReason; + } + + return reason as Protocol.Network.CookieBlockedReason; + } + ); + + const cookieObjectToUpdate: CookieData = { + parsedCookie: { + ...generatedCookie, + name: generatedCookie?.name ?? '', + priority: 'Medium', + value: '', + }, + warningReasons: cookieWarningReasons, + blockedReasons: modifiedCookieExclusionReasons, + networkEvents: { + requestEvents, + responseEvents, + }, + headerType: 'request', + isFirstParty: isFirstParty(cookie?.domain, tabUrl ?? ''), + url: url ?? requestUrl, + frameIdList: [...(frameIds ?? [])], + analytics: + cookie?.name && cookieDB + ? findAnalyticsMatch(cookie?.name, cookieDB) + : null, + }; + + return cookieObjectToUpdate; +} diff --git a/packages/extension/src/utils/doesFrameExist.ts b/packages/extension/src/utils/doesFrameExist.ts new file mode 100644 index 000000000..8f80f6870 --- /dev/null +++ b/packages/extension/src/utils/doesFrameExist.ts @@ -0,0 +1,34 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This function will return true if the frame exists. + * @param {string} targetId The targetId for the frame which needs to be checked if it exists. + * @returns {boolean} True if frame exists else false. + */ +export const doesFrameExist = async (targetId: string | null) => { + if (!targetId) { + return false; + } + try { + const { targetInfo: { browserContextId = '' } = {} } = + await chrome.debugger.sendCommand({ targetId }, 'Target.getTargetInfo', { + targetId, + }); + return browserContextId ? true : false; + } catch (error) { + return false; + } +}; diff --git a/packages/extension/src/utils/downloadReport.ts b/packages/extension/src/utils/downloadReport.ts index 475735f70..addcb736c 100644 --- a/packages/extension/src/utils/downloadReport.ts +++ b/packages/extension/src/utils/downloadReport.ts @@ -16,11 +16,7 @@ /** * External dependencies. */ -import type { - LibraryData, - TabCookies, - TabFrames, -} from '@ps-analysis-tool/common'; +import type { LibraryData, TabCookies, TabFrames } from '@google-psat/common'; import { saveAs } from 'file-saver'; /** @@ -48,7 +44,7 @@ export default async function downloadReport( // Injections const script = reportDom.createElement('script'); - const reportData = generateReportObject( + const reportData = await generateReportObject( tabCookies, tabFrames, libraryMatches, diff --git a/packages/extension/src/utils/fetchCookieDictionary.ts b/packages/extension/src/utils/fetchCookieDictionary.ts index b7f6aa254..ffa7fea15 100644 --- a/packages/extension/src/utils/fetchCookieDictionary.ts +++ b/packages/extension/src/utils/fetchCookieDictionary.ts @@ -16,7 +16,7 @@ /** * External dependencies */ -import { type CookieDatabase } from '@ps-analysis-tool/common'; +import { type CookieDatabase } from '@google-psat/common'; /** * Fetch dictionary from local data folder. diff --git a/packages/extension/src/utils/generateReportObject.ts b/packages/extension/src/utils/generateReportObject.ts index cffb34a14..bc5db674c 100644 --- a/packages/extension/src/utils/generateReportObject.ts +++ b/packages/extension/src/utils/generateReportObject.ts @@ -20,13 +20,14 @@ import type { LibraryData, TabCookies, TabFrames, -} from '@ps-analysis-tool/common'; + DataMapping, +} from '@google-psat/common'; import { prepareCookieStatsComponents, prepareCookiesCount, prepareFrameStatsComponent, - type DataMapping, -} from '@ps-analysis-tool/design-system'; +} from '@google-psat/design-system'; +import { I18n } from '@google-psat/i18n'; /** * Utility function to generate report object. @@ -36,7 +37,7 @@ import { * @param url Top level URL. * @returns Report Object */ -export default function generateReportObject( +export default async function generateReportObject( tabCookies: TabCookies, tabFrames: TabFrames, libraryMatches: LibraryData, @@ -48,17 +49,17 @@ export default function generateReportObject( const cookieClassificationDataMapping: DataMapping[] = [ { - title: 'Total cookies', + title: I18n.getMessage('totalCookies'), count: cookieStats.total, data: cookiesStatsComponents.legend, }, { - title: '1st party cookies', + title: I18n.getMessage('1stPartyCookies'), count: cookieStats.firstParty.total, data: cookiesStatsComponents.firstParty, }, { - title: '3rd party cookies', + title: I18n.getMessage('3rdPartyCookies'), count: cookieStats.thirdParty.total, data: cookiesStatsComponents.thirdParty, }, @@ -66,7 +67,7 @@ export default function generateReportObject( const blockedCookieDataMapping: DataMapping[] = [ { - title: 'Blocked cookies', + title: I18n.getMessage('blockedCookies'), count: cookieStats.blockedCookies.total, data: cookiesStatsComponents.blocked, }, @@ -74,12 +75,15 @@ export default function generateReportObject( const exemptedCookiesDataMapping: DataMapping[] = [ { - title: 'Exempted cookies', + title: I18n.getMessage('exemptedCookies'), count: cookieStats.exemptedCookies.total, data: cookiesStatsComponents.exempted, }, ]; + const locale = I18n.getLocale(); + const translations = await I18n.fetchMessages(locale); + return { cookieClassificationDataMapping, tabCookies, @@ -95,5 +99,7 @@ export default function generateReportObject( showFramesSection: true, showBlockedCategory: false, url, + translations, + source: 'extension', }; } diff --git a/packages/extension/src/utils/getAndParseNetworkCookies.ts b/packages/extension/src/utils/getAndParseNetworkCookies.ts new file mode 100644 index 000000000..6f1f1b9e0 --- /dev/null +++ b/packages/extension/src/utils/getAndParseNetworkCookies.ts @@ -0,0 +1,62 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies + */ +import type { CookieData } from '@google-psat/common'; + +/** + * Internal dependencies + */ +import synchnorousCookieStore from '../store/synchnorousCookieStore'; +import parseNetworkCookies from './parseNetworkCookies'; + +export const getAndParseNetworkCookies = async (tabId: string) => { + if (!synchnorousCookieStore.globalIsUsingCDP) { + return; + } + + let allCookies: CookieData[] = []; + + await Promise.all( + Object.entries( + synchnorousCookieStore.frameIdToResourceMap[tabId] ?? {} + ).map(async ([key, value]) => { + try { + //@ts-ignore + const { cookies = [] } = await chrome.debugger.sendCommand( + { targetId: key }, + 'Network.getCookies', + { urls: Array.from(value) } + ); + + const parsedCookies = parseNetworkCookies( + cookies, + synchnorousCookieStore?.getTabUrl(Number(tabId)) ?? '', + synchnorousCookieStore.cookieDB ?? {}, + key + ); + if (parsedCookies.length > 0) { + allCookies = [...allCookies, ...parsedCookies]; + } + } catch (error) { + //Fail silently. There will be only one reason stating target id not found. + } + }) + ); + + synchnorousCookieStore.update(Number(tabId), allCookies); +}; diff --git a/packages/extension/src/utils/getFramesForCurrentTab.ts b/packages/extension/src/utils/getFramesForCurrentTab.ts index ba9d02427..b0d166cb7 100644 --- a/packages/extension/src/utils/getFramesForCurrentTab.ts +++ b/packages/extension/src/utils/getFramesForCurrentTab.ts @@ -16,51 +16,90 @@ /** * External dependencies */ -import type { TabFrames } from '@ps-analysis-tool/common'; -/** - * Internal dependencies - */ -import { isOnRWS } from '../contentScript/utils'; +import { type TabFrames } from '@google-psat/common'; /** * This function returns tab frames state for frame ids and frame URLs along with rws information from using chrome.webNavigation.getAllFrames. + * @param prevState Previous state to which data will be appended. + * @param currentTabFrames Current frames in the tab. + * @param currentTargets Current debugger targets in the browser. + * @param extraFrameData Extra frames data that has been provided by the serivce worker. + * @param isUsingCDP Determines if cdp is being used. * @returns {TabFrames|null} Tabframes and related details if available else null. */ -export default async function getFramesForCurrentTab() { - const tabId = chrome.devtools.inspectedWindow.tabId; - if (!tabId) { - return null; - } +export default function getFramesForCurrentTab( + prevState: TabFrames | null, + currentTabFrames: chrome.webNavigation.GetAllFrameResultDetails[] | null, + currentTargets: chrome.debugger.TargetInfo[], + extraFrameData: Record, + isUsingCDP: boolean +) { + const modifiedTabFrames: TabFrames = isUsingCDP ? {} : prevState ?? {}; - const regexForFrameUrl = - /^(?:https?:\/\/)?(?:[^@\n]+@)?(?:www\.)?([^:/\n?]+)/; + currentTabFrames?.forEach(({ url, frameType, frameId }) => { + if (url && url.includes('http')) { + const parsedUrl = new URL(url).origin; + if (!parsedUrl) { + return; + } - const currentTabFrames = await chrome.webNavigation.getAllFrames({ - tabId, - }); - const modifiedTabFrames: TabFrames = {}; + const frameIdsFromCDP: string[] = []; - currentTabFrames?.forEach(({ url, frameId, frameType }) => { - if (url && url.includes('http')) { - const parsedUrl = regexForFrameUrl.exec(url); - if (parsedUrl && parsedUrl[0]) { - if (modifiedTabFrames[parsedUrl[0]]) { - modifiedTabFrames[parsedUrl[0]].frameIds.push(frameId); + currentTargets.forEach(({ url: targetUrl, type, id }) => { + if (url === targetUrl && (type === 'page' || type === 'other')) { + frameIdsFromCDP.push(id); + } + }); + + const currentFrameIds = isUsingCDP + ? frameIdsFromCDP + : [frameId.toString()]; + + if (frameIdsFromCDP.length) { + if (modifiedTabFrames[parsedUrl]) { + modifiedTabFrames[parsedUrl].frameIds = Array.from( + new Set([ + ...(modifiedTabFrames[parsedUrl].frameIds ?? []), + ...(prevState?.[parsedUrl]?.frameIds ?? []), + ...(currentFrameIds ?? []), + ]) + ); } else { - modifiedTabFrames[parsedUrl[0]] = { - frameIds: [frameId], + modifiedTabFrames[parsedUrl] = { + frameIds: Array.from( + new Set([ + ...(currentFrameIds ?? []), + ...(prevState?.[parsedUrl]?.frameIds ?? []), + ]) + ), frameType, }; } } } }); - await Promise.all( - Object.keys(modifiedTabFrames).map(async (tabFrame) => { - modifiedTabFrames[tabFrame].isOnRWS = await isOnRWS(tabFrame); - return tabFrame; - }) - ); + + if (isUsingCDP) { + Object.keys(extraFrameData).forEach((key) => { + if (key === 'null') { + return; + } + + if (modifiedTabFrames[key]) { + modifiedTabFrames[key].frameIds = Array.from( + new Set([ + ...(modifiedTabFrames[key].frameIds ?? []), + ...(extraFrameData[key] ?? []), + ]) + ); + } else { + modifiedTabFrames[key] = { + frameIds: extraFrameData[key] ?? [], + frameType: 'sub_frame', + }; + } + }); + } return modifiedTabFrames; } diff --git a/packages/extension/src/utils/listenToNewTab.ts b/packages/extension/src/utils/listenToNewTab.ts new file mode 100644 index 000000000..d0feab56f --- /dev/null +++ b/packages/extension/src/utils/listenToNewTab.ts @@ -0,0 +1,69 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Internal dependencies + */ +import synchnorousCookieStore from '../store/synchnorousCookieStore'; +import { getTab } from './getTab'; + +const listenToNewTab = async (tabId?: number) => { + const newTabId = + tabId?.toString() || chrome.devtools.inspectedWindow.tabId.toString(); + tabId; + + if (!newTabId) { + return ''; + } + + if ( + synchnorousCookieStore.tabMode && + synchnorousCookieStore.tabMode !== 'unlimited' + ) { + const storedTabData = Object.keys(synchnorousCookieStore?.tabsData ?? {}); + + await Promise.all( + storedTabData.map(async (tabIdToDelete) => { + synchnorousCookieStore?.removeTabData(Number(tabIdToDelete)); + try { + await chrome.action.setBadgeText({ + tabId: Number(tabIdToDelete), + text: '', + }); + await chrome.debugger.detach({ tabId: Number(tabIdToDelete) }); + } catch (error) { + // Fail silently + } + return tabIdToDelete; + }) + ); + } + + synchnorousCookieStore.tabToRead = newTabId; + + synchnorousCookieStore.initialiseVariablesForNewTab(newTabId); + + synchnorousCookieStore?.addTabData(Number(newTabId)); + synchnorousCookieStore?.updateDevToolsState(Number(newTabId), true); + synchnorousCookieStore?.updatePopUpState(Number(newTabId), true); + + const currentTab = await getTab(Number(newTabId)); + + synchnorousCookieStore?.updateUrl(Number(newTabId), currentTab?.url ?? ''); + + return newTabId; +}; + +export default listenToNewTab; diff --git a/packages/extension/src/utils/parseHeaders.ts b/packages/extension/src/utils/parseHeaders.ts index cc80bb82a..d7ea1f74c 100644 --- a/packages/extension/src/utils/parseHeaders.ts +++ b/packages/extension/src/utils/parseHeaders.ts @@ -17,12 +17,17 @@ * External dependencies */ import { Protocol } from 'devtools-protocol'; -import { type CookieData, type CookieDatabase } from '@ps-analysis-tool/common'; +import { type CookieData, type CookieDatabase } from '@google-psat/common'; + +/** + * Internal dependencies. + */ import parseResponseCookieHeader from '../serviceWorker/parseResponseCookieHeader'; import canProcessCookies from './canProcessCookies'; import parseRequestCookieHeader from '../serviceWorker/parseRequestCookieHeader'; type CDPCookiesType = { [cookies: string]: Protocol.Network.Cookie[] }; + /** * Common utility function that can parse both request and response headers. * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie @@ -34,7 +39,7 @@ type CDPCookiesType = { [cookies: string]: Protocol.Network.Cookie[] }; * @param {string} url Cookie URL (URL of the server which is setting/updating cookies). * @param {CookieDatabase} cookieDB Dictionary from open cookie database * @param {string | null | undefined} tabUrl top url of the tab from which the request originated. - * @param {number} frameId Id of the frame the cookie is used in. + * @param {string} frameId Id of the frame the cookie is used in. * @param {string} requestId Request id. * @param {chrome.webRequest.HttpHeader} headers The headers that need to be parsed to get cookies. * @returns {CookieData[] | null} returns set of cookies from the header. @@ -48,7 +53,7 @@ export default async function parseHeaders( url: string, cookieDB: CookieDatabase, tabUrl: string | null | undefined, - frameId: number, + frameId: string, requestId: string, headers?: chrome.webRequest.HttpHeader[] ) { diff --git a/packages/extension/src/utils/parseNetworkCookies.ts b/packages/extension/src/utils/parseNetworkCookies.ts new file mode 100644 index 000000000..cedfb2ecd --- /dev/null +++ b/packages/extension/src/utils/parseNetworkCookies.ts @@ -0,0 +1,76 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import type { Protocol } from 'devtools-protocol'; +import { + findAnalyticsMatch, + calculateEffectiveExpiryDate, + isFirstParty, + type CookieData, + type CookieDatabase, +} from '@google-psat/common'; + +/** + * Parse Network.responseReceivedExtraInfo for extra information about a cookie. + * @param {Protocol.Network.Cookie[]} cookies Cookies that have been extracted from the urls. + * @param {string} tabUrl - The top-level URL (URL in the tab's address bar). + * @param {CookieDatabase} cookieDB Cookie database to find analytics from. + * @param {string} frameId - The frameId the following cookies are associated to. + * @returns {CookieData[]} parsed cookies. + */ +export default function parseNetworkCookies( + cookies: Protocol.Network.Cookie[], + tabUrl: string, + cookieDB: CookieDatabase, + frameId: string +) { + const parsedCookies: CookieData[] = []; + + cookies.forEach((cookie) => { + const effectiveExpirationDate = calculateEffectiveExpiryDate( + cookie.expires + ); + const singleCookie: CookieData = { + isBlocked: false, + blockedReasons: [], + parsedCookie: { + ...cookie, + expires: effectiveExpirationDate, + samesite: cookie.sameSite ?? '', + partitionKey: + cookie?.partitionKey && typeof cookie?.partitionKey === 'string' + ? cookie?.partitionKey + : //@ts-ignore This is to handle both stable and canary version of Chrome. + cookie?.partitionKey?.topLevelSite, + }, + networkEvents: { + requestEvents: [], + responseEvents: [], + }, + analytics: cookieDB ? findAnalyticsMatch(cookie.name, cookieDB) : null, + url: '', + isFirstParty: isFirstParty(cookie.domain, tabUrl), + headerType: 'response' as CookieData['headerType'], + frameIdList: [frameId], + }; + + parsedCookies.push(singleCookie); + }); + + return parsedCookies; +} diff --git a/packages/extension/src/utils/processAndStoreDocumentCookies.ts b/packages/extension/src/utils/processAndStoreDocumentCookies.ts index daf343d54..3850009d7 100644 --- a/packages/extension/src/utils/processAndStoreDocumentCookies.ts +++ b/packages/extension/src/utils/processAndStoreDocumentCookies.ts @@ -21,19 +21,19 @@ import { findAnalyticsMatch, type CookieData, type CookieDatabase, -} from '@ps-analysis-tool/common'; +} from '@google-psat/common'; import { type Cookie as ParsedCookie } from 'simple-cookie'; /** * Internal dependencies. */ import { createCookieObject } from '../serviceWorker/createCookieObject'; - import { GET_JS_COOKIES } from '../constants'; interface ProcessAndStoreDucmentCookies { tabUrl: string; tabId: string; + frameId: string; documentCookies: ParsedCookie[]; cookieDB: CookieDatabase; } @@ -41,6 +41,7 @@ interface ProcessAndStoreDucmentCookies { const processAndStoreDocumentCookies = async ({ tabUrl, tabId, + frameId, documentCookies, cookieDB, }: ProcessAndStoreDucmentCookies) => { @@ -84,7 +85,7 @@ const processAndStoreDocumentCookies = async ({ url: tabUrl, headerType: 'javascript', // @todo Update headerType name. isFirstParty: isFirstPartyCookie || null, - frameIdList: [0], + frameIdList: [frameId.toString()], }; } ); diff --git a/packages/extension/src/utils/sendMessageWrapper.ts b/packages/extension/src/utils/sendMessageWrapper.ts new file mode 100644 index 000000000..8af4756b1 --- /dev/null +++ b/packages/extension/src/utils/sendMessageWrapper.ts @@ -0,0 +1,30 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const sendMessageWrapper = async ( + type: string, + payload?: Record +) => { + try { + await chrome.runtime.sendMessage({ + type, + payload, + }); + } catch (error) { + // Fail silently. The only error which will be thrown is receiving end does not exist. + } +}; + +export default sendMessageWrapper; diff --git a/packages/extension/src/utils/test-data/cookieMockData.ts b/packages/extension/src/utils/test-data/cookieMockData.ts index 80652a7e3..c824a7b2d 100644 --- a/packages/extension/src/utils/test-data/cookieMockData.ts +++ b/packages/extension/src/utils/test-data/cookieMockData.ts @@ -17,7 +17,7 @@ * External dependencies. */ import { type Cookie as ParsedCookie } from 'simple-cookie'; -import { emptyAnalytics } from '@ps-analysis-tool/common'; +import { emptyAnalytics } from '@google-psat/common'; /** * Internal dependencies. */ @@ -101,8 +101,7 @@ const data: { category: 'Marketing', name: '__qca', domain: "Advertiser's website domain", - description: - 'This cookie is set by Quantcast, who present targeted advertising. Stores browser and HTTP request information.', + description: 'known1pCookie_description', retention: '1 year', dataController: 'Quantcast', gdprUrl: 'https://www.quantcast.com/privacy/', diff --git a/packages/extension/src/utils/test-data/frameFilteredCookies.ts b/packages/extension/src/utils/test-data/frameFilteredCookies.ts index 0bc359c3f..492abf7ca 100644 --- a/packages/extension/src/utils/test-data/frameFilteredCookies.ts +++ b/packages/extension/src/utils/test-data/frameFilteredCookies.ts @@ -16,7 +16,7 @@ /** * External dependencies */ -import type { CookieTableData } from '@ps-analysis-tool/common'; +import type { CookieTableData } from '@google-psat/common'; export const frameFilteredCookies: { [key: string]: CookieTableData } = { LSOLH: { diff --git a/packages/extension/src/utils/test-data/globalChrome.ts b/packages/extension/src/utils/test-data/globalChrome.ts index 0f2e063f6..d4dd6d159 100644 --- a/packages/extension/src/utils/test-data/globalChrome.ts +++ b/packages/extension/src/utils/test-data/globalChrome.ts @@ -17,7 +17,7 @@ * External dependencies. */ import SinonChrome from 'sinon-chrome'; -import { noop } from '@ps-analysis-tool/common'; +import { noop } from '@google-psat/common'; export default { ...SinonChrome, diff --git a/packages/extension/src/utils/test-data/tabFrames.ts b/packages/extension/src/utils/test-data/tabFrames.ts index a41d81947..0ebd62d52 100644 --- a/packages/extension/src/utils/test-data/tabFrames.ts +++ b/packages/extension/src/utils/test-data/tabFrames.ts @@ -17,7 +17,7 @@ const tabFrames = [ { processId: 1, frameId: 0, - url: 'https://edition.cnn.com', + url: 'https://edition.cnn.com?refresh=1', documentId: '40245632AWDER', documentLifecycle: 'active', errorOccurred: false, @@ -37,7 +37,7 @@ const tabFrames = [ { processId: 1, frameId: 3, - url: 'https://edition.cnn.com', + url: 'https://crxd.net', documentId: '40245632AWDER', documentLifecycle: 'active', errorOccurred: false, @@ -45,4 +45,29 @@ const tabFrames = [ parentFrameId: 0, }, ]; + +export const targetInfo = [ + { + type: 'page', + id: 'SADASQ5546SDT45673', + url: 'https://edition.cnn.com?refresh=1', + tabId: 40245632, + attached: true, + title: 'CNN', + }, + { + type: 'other', + id: 'ASFGHSD2453465645568679FR', + attached: true, + title: 'https://crxd.net', + url: 'https://crxd.net', + }, + { + type: 'other', + id: 'ADFSDGRW4365663SDGF', + attached: true, + title: 'https://crxd.net', + url: 'https://crxd.net', + }, +]; export default tabFrames; diff --git a/packages/extension/src/utils/tests/data.mock.ts b/packages/extension/src/utils/tests/data.mock.ts index 14efa4d97..592c83a79 100644 --- a/packages/extension/src/utils/tests/data.mock.ts +++ b/packages/extension/src/utils/tests/data.mock.ts @@ -7454,5 +7454,7 @@ export const data = { ], showFramesSection: true, url: 'http://example.com', + source: 'extension', showBlockedCategory: false, + translations: {}, }; diff --git a/packages/extension/src/utils/tests/generateReportObject.ts b/packages/extension/src/utils/tests/generateReportObject.ts index 7fea47c62..d69b1151a 100644 --- a/packages/extension/src/utils/tests/generateReportObject.ts +++ b/packages/extension/src/utils/tests/generateReportObject.ts @@ -14,19 +14,44 @@ * limitations under the License. */ +import { I18n } from '@google-psat/i18n'; import generateReportObject from '../generateReportObject'; import { data, libraryMatches, tabCookies, tabFrames } from './data.mock'; describe('generateReport', () => { - it('should generate report object', () => { - expect( - //@ts-ignore - generateReportObject( - tabCookies, - tabFrames, - libraryMatches, - 'http://example.com' - ) - ).toEqual(data); + beforeAll(() => { + I18n.initMessages({ + totalCookies: { + message: 'Total Cookies', + }, + '1stPartyCookies': { + message: '1st Party Cookies', + }, + '3rdPartyCookies': { + message: '3rd Party Cookies', + }, + blockedCookies: { + message: 'Blocked cookies', + }, + exemptedCookies: { + message: 'Exempted Cookies', + }, + }); + globalThis.fetch = jest.fn().mockResolvedValue({ + json: jest.fn().mockResolvedValue({}), + }); + globalThis.chrome.i18n = null; + }); + + it.skip('should generate report object', async () => { + const result = await generateReportObject( + tabCookies, + tabFrames, + libraryMatches, + 'http://example.com' + ); + + expect(result).toEqual(data); + //@ts-ignore }); }); diff --git a/packages/extension/src/utils/tests/getFramesForCurrentTab.ts b/packages/extension/src/utils/tests/getFramesForCurrentTab.ts index ea6e1e415..ec48da0cd 100644 --- a/packages/extension/src/utils/tests/getFramesForCurrentTab.ts +++ b/packages/extension/src/utils/tests/getFramesForCurrentTab.ts @@ -17,7 +17,7 @@ /** * Internal dependencies. */ -import tabFrames from '../test-data/tabFrames'; +import tabFrames, { targetInfo } from '../test-data/tabFrames'; import getFramesForCurrentTab from '../getFramesForCurrentTab'; // @ts-ignore // eslint-disable-next-line import/no-unresolved @@ -37,13 +37,6 @@ describe('getFramesForCurrentTab : ', () => { runtime: { getURL: () => 'data/related_website_sets.json', }, - //@ts-ignore - webNavigation: { - //@ts-ignore - getAllFrames: () => { - return Promise.resolve(tabFrames); - }, - }, }; globalThis.fetch = function () { @@ -57,16 +50,22 @@ describe('getFramesForCurrentTab : ', () => { } as unknown as typeof fetch; }); - it('Should not return tab when tabId is negative', async () => { - expect(await getFramesForCurrentTab()).toStrictEqual({ + it('Should not return tab when tabId is negative', () => { + expect( + getFramesForCurrentTab( + {}, + tabFrames as chrome.webNavigation.GetAllFrameResultDetails[], + targetInfo, + {}, + true + ) + ).toStrictEqual({ 'https://edition.cnn.com': { - frameIds: [0, 3], - isOnRWS: false, + frameIds: ['SADASQ5546SDT45673'], frameType: 'outermost_frame', }, 'https://crxd.net': { - frameIds: [2], - isOnRWS: false, + frameIds: ['ASFGHSD2453465645568679FR', 'ADFSDGRW4365663SDGF'], frameType: 'sub_frame', }, }); diff --git a/packages/extension/src/view/devtools/app.tsx b/packages/extension/src/view/devtools/app.tsx index 744da5d51..452cdf5ad 100644 --- a/packages/extension/src/view/devtools/app.tsx +++ b/packages/extension/src/view/devtools/app.tsx @@ -21,7 +21,8 @@ import { ExtensionReloadNotification, SIDEBAR_ITEMS_KEYS, SidebarProvider, -} from '@ps-analysis-tool/design-system'; +} from '@google-psat/design-system'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. @@ -43,6 +44,11 @@ const App: React.FC = () => { SIDEBAR_ITEMS_KEYS.PRIVACY_SANDBOX ); + const reloadTexts = useRef({ + displayText: I18n.getMessage('extensionUpdated'), + buttonText: I18n.getMessage('refreshPanel'), + }); + useEffect(() => { (async () => { const tabId = chrome.devtools.inspectedWindow.tabId; @@ -72,7 +78,10 @@ const App: React.FC = () => { ) : (
    - +
    )}
    diff --git a/packages/extension/src/view/devtools/components/antiCovertTracking/antiCovertTracking.tsx b/packages/extension/src/view/devtools/components/antiCovertTracking/antiCovertTracking.tsx index 3aeda595e..4d97ac4b5 100644 --- a/packages/extension/src/view/devtools/components/antiCovertTracking/antiCovertTracking.tsx +++ b/packages/extension/src/view/devtools/components/antiCovertTracking/antiCovertTracking.tsx @@ -18,7 +18,8 @@ * External Dependencies */ import React from 'react'; -import { ContentPanel, LandingPage } from '@ps-analysis-tool/design-system'; +import { ContentPanel, LandingPage } from '@google-psat/design-system'; +import { I18n } from '@google-psat/i18n'; const content = [ { @@ -56,7 +57,7 @@ const content = [ const AntiCovertTracking = () => { return ( { return (
    diff --git a/packages/extension/src/view/devtools/components/antiCovertTracking/fingerprinting/index.tsx b/packages/extension/src/view/devtools/components/antiCovertTracking/fingerprinting/index.tsx index 6124271b6..3fc0ea85a 100644 --- a/packages/extension/src/view/devtools/components/antiCovertTracking/fingerprinting/index.tsx +++ b/packages/extension/src/view/devtools/components/antiCovertTracking/fingerprinting/index.tsx @@ -17,13 +17,14 @@ * External dependencies. */ import React from 'react'; -import { LandingPage, PSInfoKey } from '@ps-analysis-tool/design-system'; +import { LandingPage, PSInfoKey } from '@google-psat/design-system'; +import { I18n } from '@google-psat/i18n'; const Fingerprinting = () => { return (
    diff --git a/packages/extension/src/view/devtools/components/antiCovertTracking/tests/index.tsx b/packages/extension/src/view/devtools/components/antiCovertTracking/tests/index.tsx index e6c1c599a..23bc37606 100644 --- a/packages/extension/src/view/devtools/components/antiCovertTracking/tests/index.tsx +++ b/packages/extension/src/view/devtools/components/antiCovertTracking/tests/index.tsx @@ -30,6 +30,7 @@ import AntiCovertTracking from '../antiCovertTracking'; // eslint-disable-next-line import/no-unresolved import PSInfo from 'ps-analysis-tool/data/PSInfo.json'; import { act } from 'react-dom/test-utils'; +import { I18n } from '@google-psat/i18n'; describe('AntiCovertTracking Landing Pages', () => { beforeAll(() => { @@ -43,6 +44,14 @@ describe('AntiCovertTracking Landing Pages', () => { text: () => Promise.resolve({}), }); } as unknown as typeof fetch; + + globalThis.chrome.i18n = null; + + I18n.initMessages({ + trackingProtection: { + message: 'Tracking Protection', + }, + }); }); it('should render BounceTracking', async () => { @@ -68,6 +77,8 @@ describe('AntiCovertTracking Landing Pages', () => { act(() => { render(); }); - expect(await screen.findByText('Tracking Protection')).toBeInTheDocument(); + expect( + await screen.findByText(I18n.getMessage('trackingProtection')) + ).toBeInTheDocument(); }); }); diff --git a/packages/extension/src/view/devtools/components/cookies/cookieLanding/blockedCookiesSection.tsx b/packages/extension/src/view/devtools/components/cookies/cookieLanding/blockedCookiesSection.tsx index 7d23d0054..2bbbe9c5c 100644 --- a/packages/extension/src/view/devtools/components/cookies/cookieLanding/blockedCookiesSection.tsx +++ b/packages/extension/src/view/devtools/components/cookies/cookieLanding/blockedCookiesSection.tsx @@ -19,7 +19,6 @@ import React from 'react'; import { CookiesLandingWrapper, - type DataMapping, prepareCookieStatsComponents, prepareCookiesCount, MatrixContainer, @@ -28,7 +27,9 @@ import { useFiltersMapping, SIDEBAR_ITEMS_KEYS, useSidebar, -} from '@ps-analysis-tool/design-system'; +} from '@google-psat/design-system'; +import { type DataMapping, getLegendDescription } from '@google-psat/common'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies */ @@ -56,12 +57,16 @@ const BlockedCookiesSection = () => { const cookiesStatsComponents = prepareCookieStatsComponents(cookieStats); const blockedCookieDataMapping: DataMapping[] = [ { - title: 'Blocked cookies', + title: I18n.getMessage('blockedCookies'), count: cookieStats.blockedCookies.total, data: cookiesStatsComponents.blocked, onClick: cookieStats.blockedCookies.total > 0 - ? () => selectedItemUpdater('All', 'blockedReasons') + ? () => + selectedItemUpdater( + I18n.getMessage('selectAll'), + 'blockedReasons' + ) : null, }, ]; @@ -70,7 +75,7 @@ const BlockedCookiesSection = () => { const legendDescription = LEGEND_DESCRIPTION[component.label] || ''; return { ...component, - description: legendDescription, + description: getLegendDescription(legendDescription), title: component.label, containerClasses: '', onClick: (title: string) => @@ -80,7 +85,7 @@ const BlockedCookiesSection = () => { const description = !isUsingCDP ? ( <> - Enable PSAT to use CDP via the{' '} + {I18n.getMessage('notUsingCDP')}  .
    - For more information, visit the PSAT  + {I18n.getMessage('visitPSAT')}  - Wiki + {I18n.getMessage('wiki')} . @@ -116,9 +121,9 @@ const BlockedCookiesSection = () => { > {dataComponents.length > 0 && ( )} diff --git a/packages/extension/src/view/devtools/components/cookies/cookieLanding/cookiesSection.tsx b/packages/extension/src/view/devtools/components/cookies/cookieLanding/cookiesSection.tsx index c8699df32..ce1e7c91d 100644 --- a/packages/extension/src/view/devtools/components/cookies/cookieLanding/cookiesSection.tsx +++ b/packages/extension/src/view/devtools/components/cookies/cookieLanding/cookiesSection.tsx @@ -25,7 +25,8 @@ import { prepareCookieStatsComponents, prepareCookiesCount, useFiltersMapping, -} from '@ps-analysis-tool/design-system'; +} from '@google-psat/design-system'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies */ @@ -64,18 +65,14 @@ const CookiesSection = () => { - Please setup the{' '} - - evaluation environment - {' '} - before analyzing cookies. - +

    ', + '', + ]), + }} + /> } testId="cookies-insights" > @@ -83,8 +80,8 @@ const CookiesSection = () => { (cookieStats?.firstParty.total === 0 && cookieStats?.thirdParty.total === 0 && ( ))} { const { tabCookies, tabFrames } = useCookie(({ state }) => ({ @@ -38,10 +40,6 @@ const ExemptedCookiesSection = () => { tabFrames: state.tabFrames, })); - const { isUsingCDP } = useSettings(({ state }) => ({ - isUsingCDP: state.isUsingCDP, - })); - const { selectedItemUpdater } = useFiltersMapping(tabFrames || {}); const cookieStats = prepareCookiesCount(tabCookies); @@ -52,7 +50,7 @@ const ExemptedCookiesSection = () => { const legendDescription = LEGEND_DESCRIPTION[component.label] || ''; return { ...component, - description: legendDescription, + description: getLegendDescription(legendDescription), title: component.label, containerClasses: '', onClick: (title: string) => { @@ -62,28 +60,27 @@ const ExemptedCookiesSection = () => { }); const exemptedCookiesDataMapping: DataMapping[] = [ { - title: 'Exempted cookies', + title: I18n.getMessage('exemptedCookies'), count: cookieStats.exemptedCookies.total, data: cookiesStatsComponents.exempted, - onClick: () => selectedItemUpdater('All', 'exemptionReason'), + onClick: + cookieStats.exemptedCookies.total > 0 + ? () => + selectedItemUpdater( + I18n.getMessage('selectAll'), + 'exemptionReason' + ) + : null, }, ]; - const description = !isUsingCDP ? ( - <> - To gather data and insights regarding blocked cookies and exempted - cookies, please enable PSAT to use the Chrome DevTools protocol. You can - do this in the Settings page or in the extension popup. For more - information check the PSAT  - - Wiki - - + const description = !cookieStats.exemptedCookies.total ? ( +

    + No cookies were exempted by the browser. + + + +
    ) : ( '' ); @@ -96,9 +93,9 @@ const ExemptedCookiesSection = () => { > {dataComponents.length > 0 && ( )}
    diff --git a/packages/extension/src/view/devtools/components/cookies/cookieLanding/framesSection.tsx b/packages/extension/src/view/devtools/components/cookies/cookieLanding/framesSection.tsx index 820af36d3..b550dcb10 100644 --- a/packages/extension/src/view/devtools/components/cookies/cookieLanding/framesSection.tsx +++ b/packages/extension/src/view/devtools/components/cookies/cookieLanding/framesSection.tsx @@ -23,7 +23,9 @@ import { prepareFrameStatsComponent, type MatrixComponentProps, LEGEND_DESCRIPTION, -} from '@ps-analysis-tool/design-system'; +} from '@google-psat/design-system'; +import { getLegendDescription } from '@google-psat/common'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies */ @@ -38,10 +40,11 @@ const FramesSection = () => { const framesStats = prepareFrameStatsComponent(tabFrames, tabCookies); const dataComponents: MatrixComponentProps[] = framesStats.legend.map( (component) => { - const legendDescription = LEGEND_DESCRIPTION[component.label] || ''; + const legendDescription = + LEGEND_DESCRIPTION[component.descriptionKey] || ''; return { ...component, - description: legendDescription, + description: getLegendDescription(legendDescription), title: component.label, containerClasses: '', }; @@ -54,9 +57,9 @@ const FramesSection = () => { testId="frames-insights" > ); diff --git a/packages/extension/src/view/devtools/components/cookies/cookieLanding/index.tsx b/packages/extension/src/view/devtools/components/cookies/cookieLanding/index.tsx index b573a2112..98e280227 100644 --- a/packages/extension/src/view/devtools/components/cookies/cookieLanding/index.tsx +++ b/packages/extension/src/view/devtools/components/cookies/cookieLanding/index.tsx @@ -20,20 +20,20 @@ import React, { useMemo } from 'react'; import { LibraryDetection, useLibraryDetectionContext, -} from '@ps-analysis-tool/library-detection'; +} from '@google-psat/library-detection'; import { MenuBar, type CookiesLandingSection, type MenuData, - prepareCookiesCount, -} from '@ps-analysis-tool/design-system'; +} from '@google-psat/design-system'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies */ import CookiesSection from './cookiesSection'; import FramesSection from './framesSection'; import BlockedCookiesSection from './blockedCookiesSection'; -import { useCookie } from '../../../stateProviders'; +import { useCookie, useSettings } from '../../../stateProviders'; import downloadReport from '../../../../../utils/downloadReport'; import ExemptedCookiesSection from './exemptedCookiesSection'; @@ -44,6 +44,8 @@ const AssembledCookiesLanding = () => { url: state.tabUrl, })); + const isUsingCDP = useSettings(({ state }) => state.isUsingCDP); + const { libraryMatches, showLoader } = useLibraryDetectionContext( ({ state }) => ({ libraryMatches: state.libraryMatches, @@ -51,49 +53,50 @@ const AssembledCookiesLanding = () => { }) ); - const cookieStats = prepareCookiesCount(tabCookies); const sections: Array = useMemo(() => { const defaultSections = [ { - name: 'Cookies', + name: I18n.getMessage('cookies'), link: 'cookies', panel: { Element: CookiesSection, }, }, { - name: 'Blocked Cookies', + name: I18n.getMessage('blockedCookies'), link: 'blocked-cookies', panel: { Element: BlockedCookiesSection, }, }, { - name: 'Library Detection', + name: I18n.getMessage('libraryDetection'), link: 'library-detection', panel: { Element: LibraryDetection, }, }, { - name: 'Frames', + name: I18n.getMessage('frames'), link: 'frames', panel: { Element: FramesSection, }, }, ]; - if (cookieStats.exemptedCookies.total > 0) { + + if (isUsingCDP) { defaultSections.splice(2, 0, { - name: 'Exemption Reason', + name: I18n.getMessage('exemptionReasons'), link: 'exemption-reasons', panel: { Element: ExemptedCookiesSection, }, }); } + return defaultSections; - }, [cookieStats.exemptedCookies.total]); + }, [isUsingCDP]); const menuData: MenuData = useMemo( () => sections.map(({ name, link }) => ({ name, link })), diff --git a/packages/extension/src/view/devtools/components/cookies/cookiesListing/index.tsx b/packages/extension/src/view/devtools/components/cookies/cookiesListing/index.tsx index e45c64cb7..e5c890d4f 100644 --- a/packages/extension/src/view/devtools/components/cookies/cookiesListing/index.tsx +++ b/packages/extension/src/view/devtools/components/cookies/cookiesListing/index.tsx @@ -21,8 +21,8 @@ import { Resizable } from 're-resizable'; import { filterCookiesByFrame, type CookieTableData, -} from '@ps-analysis-tool/common'; -import { CookieDetails, CookieTable } from '@ps-analysis-tool/design-system'; +} from '@google-psat/common'; +import { CookieDetails, CookieTable } from '@google-psat/design-system'; /** * Internal dependencies. @@ -113,6 +113,7 @@ const CookiesListing = ({ setFilteredCookies }: CookiesListingProps) => { extraInterfaceToTopBar={extraInterfaceToTopBar} onRowContextMenu={rowContextMenuRef.current?.onRowContextMenu} ref={cookieTableRef} + hostname={tabUrl ? new URL(tabUrl).hostname : ''} /> { + const handleFilterClick = useCallback(() => { + const filter = `cookie-domain:${domain} cookie-name:${name}`; + + // @ts-ignore + if (chrome.devtools.panels?.network?.show) { + // @ts-ignore + chrome.devtools.panels.network.show({ filter }); + setContextMenuOpen(false); + return; + } + try { // Need to do this since chrome doesnt allow the clipboard access in extension. const copyFrom = document.createElement('textarea'); - copyFrom.textContent = `cookie-domain:${domain} cookie-name:${name}`; + copyFrom.textContent = filter; document.body.appendChild(copyFrom); copyFrom.select(); document.execCommand('copy'); @@ -193,10 +204,17 @@ const RowContextMenu = forwardRef< }} > {isDomainInAllowList && parentDomain ? ( @@ -204,7 +222,11 @@ const RowContextMenu = forwardRef< onClick={handleAllowListWithParentDomainClick} className="w-full text-xs rounded px-1 py-[3px] flex items-center hover:bg-royal-blue hover:text-white cursor-default" > - Remove `{parentDomain}` From Allow List + + {I18n.getMessage('removeParentDomainFromAllowList', [ + parentDomain, + ])} + ) : ( )} diff --git a/packages/extension/src/view/devtools/components/cookies/cookiesListing/useCookieListing/index.tsx b/packages/extension/src/view/devtools/components/cookies/cookiesListing/useCookieListing/index.tsx index 0f6c293bc..d98b23de6 100644 --- a/packages/extension/src/view/devtools/components/cookies/cookiesListing/useCookieListing/index.tsx +++ b/packages/extension/src/view/devtools/components/cookies/cookiesListing/useCookieListing/index.tsx @@ -22,7 +22,7 @@ import { type CookieTableData, type TabCookies, BLOCK_STATUS, -} from '@ps-analysis-tool/common'; +} from '@google-psat/common'; import { RefreshButton, type InfoType, @@ -37,7 +37,8 @@ import { type TableData, InfoIcon, calculateExemptionReason, -} from '@ps-analysis-tool/design-system'; +} from '@google-psat/design-system'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies @@ -77,7 +78,7 @@ const useCookieListing = (domainsInAllowList: Set) => { const tableColumns = useMemo( () => [ { - header: 'Name', + header: I18n.getMessage('name'), accessorKey: 'parsedCookie.name', cell: (info: InfoType) => info, enableHiding: false, @@ -101,47 +102,46 @@ const useCookieListing = (domainsInAllowList: Set) => { }, }, { - header: 'Scope', + header: I18n.getMessage('scope'), accessorKey: 'isFirstParty', - cell: (info: InfoType) => ( -

    - {!info ? 'Third Party' : 'First Party'} -

    - ), + cell: (info: InfoType) => + I18n.getMessage(!info ? 'thirdParty' : 'firstParty'), widthWeightagePercentage: 6, }, { - header: 'Domain', + header: I18n.getMessage('domain'), accessorKey: 'parsedCookie.domain', cell: (info: InfoType) => info, widthWeightagePercentage: 8, }, { - header: 'Partition Key', + header: I18n.getMessage('partitionKey'), accessorKey: 'parsedCookie.partitionKey', cell: (info: InfoType) => info, widthWeightagePercentage: 8, }, { - header: 'SameSite', + header: I18n.getMessage('sameSite'), accessorKey: 'parsedCookie.samesite', - cell: (info: InfoType) => {info}, + cell: (info: InfoType) => + I18n.getMessage((info?.toString() || '').toLowerCase()), widthWeightagePercentage: 6, }, { - header: 'Category', + header: I18n.getMessage('category'), accessorKey: 'analytics.category', - cell: (info: InfoType) => info, + cell: (info: InfoType) => + I18n.getMessage((info as string).toLowerCase() || 'uncategorized'), widthWeightagePercentage: 7.5, }, { - header: 'Platform', + header: I18n.getMessage('platform'), accessorKey: 'analytics.platform', - cell: (info: InfoType) => {info ? info : 'Unknown'}, + cell: (info: InfoType) => (info ? info : I18n.getMessage('unknown')), widthWeightagePercentage: 7.5, }, { - header: 'HttpOnly', + header: I18n.getMessage('httpOnly'), accessorKey: 'parsedCookie.httponly', cell: (info: InfoType) => (

    @@ -151,7 +151,7 @@ const useCookieListing = (domainsInAllowList: Set) => { widthWeightagePercentage: 4, }, { - header: 'Secure', + header: I18n.getMessage('secure'), accessorKey: 'parsedCookie.secure', cell: (info: InfoType) => (

    @@ -161,39 +161,40 @@ const useCookieListing = (domainsInAllowList: Set) => { widthWeightagePercentage: 4, }, { - header: 'Value', + header: I18n.getMessage('value'), accessorKey: 'parsedCookie.value', cell: (info: InfoType) => info, widthWeightagePercentage: 7, }, { - header: 'Path', + header: I18n.getMessage('path'), accessorKey: 'parsedCookie.path', cell: (info: InfoType) => info, widthWeightagePercentage: 3.5, }, { - header: 'Expires / Max-Age', + header: I18n.getMessage('expires'), accessorKey: 'parsedCookie.expires', - cell: (info: InfoType) => (info ? info : 'Session'), + cell: (info: InfoType) => + info === 'Session' || !info ? I18n.getMessage('session') : info, widthWeightagePercentage: 6, }, { - header: 'Priority', + header: I18n.getMessage('priority'), accessorKey: 'parsedCookie.priority', isHiddenByDefault: true, cell: (info: InfoType) => info, widthWeightagePercentage: 4, }, { - header: 'Size', + header: I18n.getMessage('size'), accessorKey: 'parsedCookie.size', isHiddenByDefault: true, cell: (info: InfoType) => info, widthWeightagePercentage: 3, }, { - header: 'Mapping', + header: I18n.getMessage('mapping'), accessorKey: 'frameIdList', isHiddenByDefault: true, cell: (info: InfoType) => ( @@ -202,7 +203,7 @@ const useCookieListing = (domainsInAllowList: Set) => { widthWeightagePercentage: 6.6, }, { - header: 'Blocking Status', + header: I18n.getMessage('blockingStatus'), accessorKey: 'isBlocked', isHiddenByDefault: true, widthWeightagePercentage: 5.4, @@ -230,10 +231,14 @@ const useCookieListing = (domainsInAllowList: Set) => { return ( - - Undetermined + + + + + {I18n.getMessage('undetermined')} + ); } else { @@ -248,28 +253,36 @@ const useCookieListing = (domainsInAllowList: Set) => { const filters = useMemo( () => ({ 'analytics.category': { - title: 'Category', + title: I18n.getMessage('category'), hasStaticFilterValues: true, hasPrecalculatedFilterValues: true, filterValues: calculateDynamicFilterValues( 'analytics.category', Object.values(cookies), parsedQuery?.filter?.['analytics.category'], - clearActivePanelQuery + clearActivePanelQuery, + true ), sortValues: true, useGenericPersistenceKey: true, + comparator: (value: InfoType, filterValue: string) => { + const val = value as string; + return ( + I18n.getMessage(val?.toLowerCase() || 'uncategorized') === + filterValue + ); + }, }, isFirstParty: { - title: 'Scope', + title: I18n.getMessage('scope'), hasStaticFilterValues: true, hasPrecalculatedFilterValues: true, filterValues: evaluateStaticFilterValues( { - 'First Party': { + [I18n.getMessage('firstParty')]: { selected: false, }, - 'Third Party': { + [I18n.getMessage('thirdParty')]: { selected: false, }, }, @@ -280,40 +293,40 @@ const useCookieListing = (domainsInAllowList: Set) => { useGenericPersistenceKey: true, comparator: (value: InfoType, filterValue: string) => { const val = Boolean(value); - return val === (filterValue === 'First Party'); + return val === (filterValue === I18n.getMessage('firstParty')); }, }, 'parsedCookie.domain': { - title: 'Domain', + title: I18n.getMessage('domain'), }, 'parsedCookie.httponly': { - title: 'HttpOnly', + title: I18n.getMessage('httpOnly'), hasStaticFilterValues: true, filterValues: { - True: { + [I18n.getMessage('true')]: { selected: false, }, - False: { + [I18n.getMessage('false')]: { selected: false, }, }, useGenericPersistenceKey: true, comparator: (value: InfoType, filterValue: string) => { const val = Boolean(value); - return val === (filterValue === 'True'); + return val === (filterValue === I18n.getMessage('true')); }, }, 'parsedCookie.samesite': { - title: 'SameSite', + title: I18n.getMessage('sameSite'), hasStaticFilterValues: true, filterValues: { - None: { + [I18n.getMessage('none')]: { selected: false, }, - Lax: { + [I18n.getMessage('lax')]: { selected: false, }, - Strict: { + [I18n.getMessage('strict')]: { selected: false, }, }, @@ -324,42 +337,42 @@ const useCookieListing = (domainsInAllowList: Set) => { }, }, 'parsedCookie.secure': { - title: 'Secure', + title: I18n.getMessage('secure'), hasStaticFilterValues: true, filterValues: { - True: { + [I18n.getMessage('true')]: { selected: false, }, - False: { + [I18n.getMessage('false')]: { selected: false, }, }, useGenericPersistenceKey: true, comparator: (value: InfoType, filterValue: string) => { const val = Boolean(value); - return val === (filterValue === 'True'); + return val === (filterValue === I18n.getMessage('true')); }, }, 'parsedCookie.path': { - title: 'Path', + title: I18n.getMessage('path'), }, 'parsedCookie.expires': { - title: 'Retention Period', + title: I18n.getMessage('retentionPeriod'), hasStaticFilterValues: true, filterValues: { - Session: { + [I18n.getMessage('session')]: { selected: false, }, - 'Short Term (< 24h)': { + [I18n.getMessage('shortTerm')]: { selected: false, }, - 'Medium Term (24h - 1 week)': { + [I18n.getMessage('mediumTerm')]: { selected: false, }, - 'Long Term (1 week - 1 month)': { + [I18n.getMessage('longTerm')]: { selected: false, }, - 'Extended Term (> 1 month)': { + [I18n.getMessage('extentedTerm')]: { selected: false, }, }, @@ -368,22 +381,22 @@ const useCookieListing = (domainsInAllowList: Set) => { let diff = 0; const val = value as string; switch (filterValue) { - case 'Session': + case I18n.getMessage('session'): return val === 'Session'; - case 'Short Term (< 24h)': + case I18n.getMessage('shortTerm'): diff = Date.parse(val) - Date.now(); return diff < 86400000; - case 'Medium Term (24h - 1 week)': + case I18n.getMessage('mediumTerm'): diff = Date.parse(val) - Date.now(); return diff >= 86400000 && diff < 604800000; - case 'Long Term (1 week - 1 month)': + case I18n.getMessage('longTerm'): diff = Date.parse(val) - Date.now(); return diff >= 604800000 && diff < 2629743833; - case 'Extended Term (> 1 month)': + case I18n.getMessage('extentedTerm'): diff = Date.parse(val) - Date.now(); return diff >= 2629743833; @@ -393,7 +406,7 @@ const useCookieListing = (domainsInAllowList: Set) => { }, }, 'analytics.platform': { - title: 'Platform', + title: I18n.getMessage('platform'), hasStaticFilterValues: true, hasPrecalculatedFilterValues: true, filterValues: calculateDynamicFilterValues( @@ -406,7 +419,7 @@ const useCookieListing = (domainsInAllowList: Set) => { useGenericPersistenceKey: true, }, blockedReasons: { - title: 'Blocked Reasons', + title: I18n.getMessage('blockedReasons'), hasStaticFilterValues: true, hasPrecalculatedFilterValues: true, enableSelectAllOption: true, @@ -427,39 +440,41 @@ const useCookieListing = (domainsInAllowList: Set) => { }, }, 'parsedCookie.partitionKey': { - title: 'Partition Key', + title: I18n.getMessage('partitionKey'), hasStaticFilterValues: true, filterValues: { - Set: { + [I18n.getMessage('set')]: { selected: false, }, - 'Not Set': { + [I18n.getMessage('notSet')]: { selected: false, }, }, useGenericPersistenceKey: true, comparator: (value: InfoType, filterValue: string) => { const val = value as string; - return val ? filterValue === 'Set' : filterValue === 'Not Set'; + return val + ? filterValue === I18n.getMessage('set') + : filterValue === I18n.getMessage('notSet'); }, }, headerType: { - title: 'Set Via', + title: I18n.getMessage('setVia'), hasStaticFilterValues: true, filterValues: { - HTTP: { + [I18n.getMessage('http')]: { selected: false, }, - JS: { + [I18n.getMessage('jS')]: { selected: false, }, }, useGenericPersistenceKey: true, comparator: (value: InfoType, filterValue: string) => { switch (filterValue) { - case 'JS': + case I18n.getMessage('jS'): return value === 'javascript'; - case 'HTTP': + case I18n.getMessage('http'): return value === 'request' || value === 'response'; default: return true; @@ -467,7 +482,7 @@ const useCookieListing = (domainsInAllowList: Set) => { }, }, 'parsedCookie.priority': { - title: 'Priority', + title: I18n.getMessage('priority'), hasStaticFilterValues: true, filterValues: { Low: { @@ -483,7 +498,7 @@ const useCookieListing = (domainsInAllowList: Set) => { useGenericPersistenceKey: true, }, exemptionReason: { - title: 'Exemption Reason', + title: I18n.getMessage('exemptionReasons'), hasStaticFilterValues: true, hasPrecalculatedFilterValues: true, enableSelectAllOption: true, @@ -523,7 +538,7 @@ const useCookieListing = (domainsInAllowList: Set) => { return ( ); }, [getCookiesSetByJavascript]); diff --git a/packages/extension/src/view/devtools/components/cookies/cookiesListing/useCookieListing/namePrefixIconSelector.tsx b/packages/extension/src/view/devtools/components/cookies/cookiesListing/useCookieListing/namePrefixIconSelector.tsx index 9bb02a872..220fd939a 100644 --- a/packages/extension/src/view/devtools/components/cookies/cookiesListing/useCookieListing/namePrefixIconSelector.tsx +++ b/packages/extension/src/view/devtools/components/cookies/cookiesListing/useCookieListing/namePrefixIconSelector.tsx @@ -18,14 +18,14 @@ * External dependencies. */ import React from 'react'; -import { BLOCK_STATUS, type CookieTableData } from '@ps-analysis-tool/common'; +import { BLOCK_STATUS, type CookieTableData } from '@google-psat/common'; import { GreenTick, InboundIcon, OutboundIcon, OutboundInboundIcon, OutboundInboundColoredIcon, -} from '@ps-analysis-tool/design-system'; +} from '@google-psat/design-system'; interface NamePrefixIconSelectorProps { originalData: CookieTableData; diff --git a/packages/extension/src/view/devtools/components/cookies/cookiesListing/useCookieListing/orphanedUnMappedInfoDisplay.tsx b/packages/extension/src/view/devtools/components/cookies/cookiesListing/useCookieListing/orphanedUnMappedInfoDisplay.tsx index 59852aca6..37d2bf406 100644 --- a/packages/extension/src/view/devtools/components/cookies/cookiesListing/useCookieListing/orphanedUnMappedInfoDisplay.tsx +++ b/packages/extension/src/view/devtools/components/cookies/cookiesListing/useCookieListing/orphanedUnMappedInfoDisplay.tsx @@ -18,7 +18,8 @@ * External dependencies. */ import React from 'react'; -import { InfoIcon } from '@ps-analysis-tool/design-system'; +import { InfoIcon } from '@google-psat/design-system'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies @@ -41,11 +42,13 @@ const OrphanedUnMappedInfoDisplay = ({ if (frameIdList.length === 0) { return ( - - Unmapped + + + + {I18n.getMessage('unmapped')} ); } @@ -73,11 +76,13 @@ const OrphanedUnMappedInfoDisplay = ({ if (!hasFrame) { return ( - - Orphaned + + + + {I18n.getMessage('orphaned')} ); } diff --git a/packages/extension/src/view/devtools/components/cookies/cookiesListing/useCookieListing/tests/useCookieListing.tsx b/packages/extension/src/view/devtools/components/cookies/cookiesListing/useCookieListing/tests/useCookieListing.tsx index 2dcfd1689..bc6f6ddeb 100644 --- a/packages/extension/src/view/devtools/components/cookies/cookiesListing/useCookieListing/tests/useCookieListing.tsx +++ b/packages/extension/src/view/devtools/components/cookies/cookiesListing/useCookieListing/tests/useCookieListing.tsx @@ -19,6 +19,7 @@ import React from 'react'; import SinonChrome from 'sinon-chrome'; import { renderHook } from '@testing-library/react'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. @@ -51,6 +52,47 @@ describe('useCookieListing', () => { }, }; + globalThis.chrome.i18n = null; + + I18n.initMessages({ + name: { + message: 'Name', + }, + scope: { + message: 'Scope', + }, + firstParty: { + message: 'First Party', + }, + category: { + message: 'Category', + }, + marketing: { + message: 'Marketing', + }, + uncategorized: { + message: 'Uncategorized', + }, + true: { + message: 'True', + }, + session: { + message: 'Session', + }, + shortTerm: { + message: 'Short Term (< 24h)', + }, + mediumTerm: { + message: 'Medium Term (24h - 1 week)', + }, + longTerm: { + message: 'Long Term (1 week - 1 month)', + }, + extentedTerm: { + message: 'Extended Term (>1 month)', + }, + }); + it('should return the correct values', () => { mockUseCookieStore.mockReturnValue({ cookies: Object.values(mock.default.tabCookies), @@ -118,7 +160,7 @@ describe('useCookieListing', () => { result.current.filters['parsedCookie.expires'].comparator( // 2 months ahead new Date(new Date().getTime() + 5184000000).toUTCString(), - 'Extended Term (> 1 month)' + 'Extended Term (>1 month)' ) ).toBe(true); diff --git a/packages/extension/src/view/devtools/components/cookies/cookiesListing/useCookieListing/useHighlighting.tsx b/packages/extension/src/view/devtools/components/cookies/cookiesListing/useCookieListing/useHighlighting.tsx index d12a7eb38..8a9722648 100644 --- a/packages/extension/src/view/devtools/components/cookies/cookiesListing/useCookieListing/useHighlighting.tsx +++ b/packages/extension/src/view/devtools/components/cookies/cookiesListing/useCookieListing/useHighlighting.tsx @@ -17,7 +17,7 @@ * External dependencies. */ import { useCallback, useEffect } from 'react'; -import { getCookieKey, type TabCookies } from '@ps-analysis-tool/common'; +import { getCookieKey, type TabCookies } from '@google-psat/common'; /** * Internal dependencies. diff --git a/packages/extension/src/view/devtools/components/cookies/index.tsx b/packages/extension/src/view/devtools/components/cookies/index.tsx index f6ec6b029..a8972890f 100644 --- a/packages/extension/src/view/devtools/components/cookies/index.tsx +++ b/packages/extension/src/view/devtools/components/cookies/index.tsx @@ -21,8 +21,9 @@ import { Button, CookiesLanding, ProgressBar, -} from '@ps-analysis-tool/design-system'; -import { type CookieTableData } from '@ps-analysis-tool/common'; +} from '@google-psat/design-system'; +import { type CookieTableData } from '@google-psat/common'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. @@ -95,7 +96,10 @@ const Cookies = ({ setFilteredCookies }: CookiesProps) => { return (

    -
    ); diff --git a/packages/extension/src/view/devtools/components/cookies/tests/cookieTab.tsx b/packages/extension/src/view/devtools/components/cookies/tests/cookieTab.tsx index ee9ad49e3..a76cbf078 100644 --- a/packages/extension/src/view/devtools/components/cookies/tests/cookieTab.tsx +++ b/packages/extension/src/view/devtools/components/cookies/tests/cookieTab.tsx @@ -30,8 +30,9 @@ import { CookieDetails, Details, useTablePersistentSettingsStore, -} from '@ps-analysis-tool/design-system'; -import { noop } from '@ps-analysis-tool/common'; +} from '@google-psat/design-system'; +import { noop } from '@google-psat/common'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. */ @@ -118,6 +119,35 @@ describe('CookieTab', () => { }; globalThis.location.protocol = 'chrome-extension://absda'; globalThis.Promise = Promise; + + I18n.initMessages({ + noDescription: { + message: 'No description available.', + }, + description: { + message: 'Description', + }, + known1pCookie_description: { + message: + 'This cookie is set by Quantcast, who present targeted advertising. Stores browser and HTTP request information.', + }, + selectCookie: { + message: 'Select cookies to preview its value', + }, + value: { + message: 'Value', + }, + toggleAll: { + message: 'Toggle All', + }, + uncategorized: { + message: 'Uncategorized', + }, + marketing: { + message: 'Marketing', + }, + }); + globalThis.chrome.i18n = null; }); it('should render a list of cookies with analytics', async () => { @@ -310,8 +340,7 @@ describe('CookieTab', () => { const card = await screen.findByTestId('cookie-card'); const description = - mockResponse.tabCookies?.[known1pCookie.name]?.analytics?.description || - 'No description available.'; + 'This cookie is set by Quantcast, who present targeted advertising. Stores browser and HTTP request information.'; expect(await within(card).findByText(description)).toBeInTheDocument(); }); diff --git a/packages/extension/src/view/devtools/components/cookies/tests/cookiesLanding.tsx b/packages/extension/src/view/devtools/components/cookies/tests/cookiesLanding.tsx index 6015e6428..a00cc74e4 100644 --- a/packages/extension/src/view/devtools/components/cookies/tests/cookiesLanding.tsx +++ b/packages/extension/src/view/devtools/components/cookies/tests/cookiesLanding.tsx @@ -20,7 +20,7 @@ import React from 'react'; import { render } from '@testing-library/react'; import '@testing-library/jest-dom/extend-expect'; import SinonChrome from 'sinon-chrome'; -import { CookiesLanding } from '@ps-analysis-tool/design-system'; +import { CookiesLanding } from '@google-psat/design-system'; /** * Internal dependencies. */ @@ -157,6 +157,5 @@ describe('CookiesLanding', () => { expect(getByTestId('cookies-landing')).toBeInTheDocument(); expect(getAllByTestId('cookies-landing-header')[0]).toBeInTheDocument(); - expect(getByTestId('cookies-matrix-Categories')).toBeInTheDocument(); }); }); diff --git a/packages/extension/src/view/devtools/components/cookies/tests/rowContextMenu.tsx b/packages/extension/src/view/devtools/components/cookies/tests/rowContextMenu.tsx index 7c5842f00..e4618cb92 100644 --- a/packages/extension/src/view/devtools/components/cookies/tests/rowContextMenu.tsx +++ b/packages/extension/src/view/devtools/components/cookies/tests/rowContextMenu.tsx @@ -19,12 +19,13 @@ import React from 'react'; import { act, render, screen } from '@testing-library/react'; import '@testing-library/jest-dom'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. */ import RowContextMenu from '../cookiesListing/rowContextMenu'; -import type { TableRow } from '@ps-analysis-tool/design-system'; +import type { TableRow } from '@google-psat/design-system'; import SinonChrome from 'sinon-chrome'; const rowContextMenuProp = { @@ -44,6 +45,15 @@ globalThis.chrome = { }, }, }; +globalThis.chrome.i18n = null; +I18n.initMessages({ + removeDomainFromAllowList: { + message: 'Remove Domain from Allow List', + }, + allowDomin: { + message: 'Allow Domain During Session', + }, +}); describe('RowContextMenu', () => { it('should render Row Context Menu component', async () => { @@ -123,4 +133,57 @@ describe('RowContextMenu', () => { expect(rowContextMenu).toBeVisible(); }); + + it('should call chrome.devtools.panels.network.show with filter when filter button is clicked', async () => { + globalThis.chrome.devtools.panels = { + ...globalThis.chrome.devtools.panels, + network: { + show: jest.fn(), + }, + }; + + const ref = React.createRef<{ + onRowContextMenu: ( + e: React.MouseEvent, + row: TableRow + ) => void; + }>(); + + render(); + + act(() => { + ref.current?.onRowContextMenu( + // @ts-ignore + { + clientX: 0, + clientY: 0, + preventDefault: jest.fn(), + } as React.MouseEvent, + // @ts-ignore + { + originalData: { + // @ts-ignore + parsedCookie: { + name: 'AWSALB', + domain: 'bbc.com', + }, + }, + } + ); + }); + + const filterButton = await screen.findByText( + 'Show Requests With This Cookie' + ); + + act(() => { + filterButton.click(); + }); + + expect(globalThis.chrome.devtools.panels.network.show).toHaveBeenCalledWith( + { + filter: 'cookie-domain:bbc.com cookie-name:AWSALB', + } + ); + }); }); diff --git a/packages/extension/src/view/devtools/components/facilitatedTesting/faciliatedTestingContent/infoCards.tsx b/packages/extension/src/view/devtools/components/facilitatedTesting/faciliatedTestingContent/infoCards.tsx index e40b11902..c1995c50c 100644 --- a/packages/extension/src/view/devtools/components/facilitatedTesting/faciliatedTestingContent/infoCards.tsx +++ b/packages/extension/src/view/devtools/components/facilitatedTesting/faciliatedTestingContent/infoCards.tsx @@ -18,50 +18,60 @@ * External dependencies. */ import React from 'react'; -import { addUTMParams } from '@ps-analysis-tool/common'; +import { addUTMParams } from '@google-psat/common'; +import { I18n } from '@google-psat/i18n'; -const EXPERIMENT_GROUP = 'Membership in Experiment Group'; +const EXPERIMENT_GROUP = () => I18n.getMessage('memberShipInExperimentGroup'); const INFO_CARDS_DATA = [ { - heading: 'Third-Party Cookie Depreciation Readiness', - content: + heading: () => 'Third-Party Cookie Depreciation Readiness', + content: () => 'Discover how companies across the web are gearing up for third-party cookie deprecation. This comprehensive list is compiled with insights from participants who have voluntarily shared their preparations.', }, { heading: EXPERIMENT_GROUP, - content: `To prepare for third-party cookie deprecation, we will be providing Chrome-facilitated testing modes that allow sites to preview how site behavior and functionality work without third-party cookies. Check this guide to learn more.`, + content: () => + I18n.getMessage('memberShipInExperimentGroupNote', [ + ``, + '', + ]), }, { - heading: 'Request Additional Migration Time', - content: `For an easier transition through the deprecation process, Chrome is providing deprecation trials which allows top-level sites and embedded services to request additional time to migrate away from third-party cookie dependencies for non-advertising use cases. To learn more, please check this information regarding 3P and 1P deprecation trials.`, + heading: () => I18n.getMessage('requestAdditionalMigrationTime'), + content: () => + I18n.getMessage('requestAdditionalMigrationTimeNote', [ + ``, + '', + ``, + '', + ]), }, { - heading: 'Attestation Enrollment', - content: `To access the Privacy Sandbox relevance and measurement APIs on Chrome and Android, developers need to enroll with the privacy sandbox as a mechanism to verify the entities that call these APIs, and to gather the developer-specific data needed for the proper configuration and use of the APIs. To learn more about this process and how to enroll please check this documentation.`, + heading: () => I18n.getMessage('attestationEnrollment'), + content: () => + I18n.getMessage('attestationEnrollmentNote', [ + ``, + '', + ]), }, { - heading: 'Reporting Breakages', - content: `If your site or a service you depend on is breaking with third-party cookies disabled, you should file an issue here. And if you have questions or feedback about Privacy Sandbox, you can raise a new issue here using the third-party cookie deprecation.`, + heading: () => I18n.getMessage('reportingBreakages'), + content: () => + I18n.getMessage('reportingBreakagesNote', [ + '', + '', + '', + '', + ]), }, ]; @@ -73,19 +83,15 @@ const InfoCards = () => {

    - {card.heading} + {card.heading()}

    - {card.heading === EXPERIMENT_GROUP && ( + {card.heading() === EXPERIMENT_GROUP() && (

    -

    - For browsers in the 1% group, users will get a new - chrome://settings/trackingProtection page instead of - chrome://settings/cookies -

    +

    {I18n.getMessage('partOfExperimentGroup')}

    )}
    diff --git a/packages/extension/src/view/devtools/components/facilitatedTesting/faciliatedTestingContent/restrictionInfoContainer.tsx b/packages/extension/src/view/devtools/components/facilitatedTesting/faciliatedTestingContent/restrictionInfoContainer.tsx index d890a8e3f..8dc14e944 100644 --- a/packages/extension/src/view/devtools/components/facilitatedTesting/faciliatedTestingContent/restrictionInfoContainer.tsx +++ b/packages/extension/src/view/devtools/components/facilitatedTesting/faciliatedTestingContent/restrictionInfoContainer.tsx @@ -18,39 +18,44 @@ * Internal dependencies. */ import React from 'react'; -import { PSTimelineIcon } from '@ps-analysis-tool/design-system'; -import { addUTMParams } from '@ps-analysis-tool/common'; - -/** - * Internal dependencies. - */ -import Link from './link'; +import { PSTimelineIcon } from '@google-psat/design-system'; +import { addUTMParams } from '@google-psat/common'; +import { I18n } from '@google-psat/i18n'; const RestrictionInfoContainer = () => (
    -

    - To facilitate testing,{' '} - -

    +

    `, + '', + ]), + }} + />

    -

    - During this testing period, it is important for sites and services to{' '} - {' '} - for third-party cookie restrictions, including moving to more private - alternatives. -

    + )}">`, + '', + ]), + }} + />
    ); diff --git a/packages/extension/src/view/devtools/components/facilitatedTesting/index.tsx b/packages/extension/src/view/devtools/components/facilitatedTesting/index.tsx index 157aee7b5..6f18891e4 100644 --- a/packages/extension/src/view/devtools/components/facilitatedTesting/index.tsx +++ b/packages/extension/src/view/devtools/components/facilitatedTesting/index.tsx @@ -18,7 +18,8 @@ * External dependencies. */ import React from 'react'; -import { LandingPage } from '@ps-analysis-tool/design-system'; +import { I18n } from '@google-psat/i18n'; +import { LandingPage } from '@google-psat/design-system'; /** * Internal dependencies. @@ -28,7 +29,7 @@ import FacilitatedTestingContent from './faciliatedTestingContent'; const FacilitatedTesting = () => (
    } extraClasses="2xl:max-w-6xl xl:max-w-4xl max-w-2xl h-fit" /> diff --git a/packages/extension/src/view/devtools/components/layout.tsx b/packages/extension/src/view/devtools/components/layout.tsx index be014128d..3eae6b396 100644 --- a/packages/extension/src/view/devtools/components/layout.tsx +++ b/packages/extension/src/view/devtools/components/layout.tsx @@ -18,7 +18,7 @@ * External dependencies. */ import React, { useCallback, useEffect, useRef, useState } from 'react'; -import { type CookieTableData } from '@ps-analysis-tool/common'; +import { type CookieTableData } from '@google-psat/common'; import { Sidebar, useSidebar, @@ -28,8 +28,9 @@ import { InspectButton, ToastMessage, SIDEBAR_ITEMS_KEYS, -} from '@ps-analysis-tool/design-system'; +} from '@google-psat/design-system'; import { Resizable } from 're-resizable'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. @@ -104,7 +105,7 @@ const Layout = ({ setSidebarData }: LayoutProps) => { psData.children[SIDEBAR_ITEMS_KEYS.COOKIES].children = Object.keys( tabFrames || {} ).reduce((acc, url) => { - const popupTitle = `Cookies used by frames from ${url}`; + const popupTitle = I18n.getMessage('cookiesUsedByFrame', [url]); acc[url] = { title: url, @@ -187,7 +188,10 @@ const Layout = ({ setSidebarData }: LayoutProps) => { const lastUrl = useRef(tabUrl); useEffect(() => { - if (lastUrl.current === tabUrl || lastUrl.current === null) { + if ( + lastUrl.current === null || + new URL(lastUrl.current).hostname === new URL(tabUrl || '').hostname + ) { lastUrl.current = tabUrl; return; } @@ -241,7 +245,7 @@ const Layout = ({ setSidebarData }: LayoutProps) => { {settingsChanged && ( diff --git a/packages/extension/src/view/devtools/components/privacySandbox/index.tsx b/packages/extension/src/view/devtools/components/privacySandbox/index.tsx index da7ae40a0..f8188f316 100644 --- a/packages/extension/src/view/devtools/components/privacySandbox/index.tsx +++ b/packages/extension/src/view/devtools/components/privacySandbox/index.tsx @@ -17,7 +17,7 @@ * External dependencies. */ import React from 'react'; -import { LandingPage } from '@ps-analysis-tool/design-system'; +import { LandingPage } from '@google-psat/design-system'; const PrivacySandbox = () => (
    diff --git a/packages/extension/src/view/devtools/components/privateAdvertising/attribution/index.tsx b/packages/extension/src/view/devtools/components/privateAdvertising/attribution/index.tsx index 0f5aa5b4b..b9b1b21e3 100644 --- a/packages/extension/src/view/devtools/components/privateAdvertising/attribution/index.tsx +++ b/packages/extension/src/view/devtools/components/privateAdvertising/attribution/index.tsx @@ -17,13 +17,14 @@ * External dependencies. */ import React from 'react'; -import { LandingPage, PSInfoKey } from '@ps-analysis-tool/design-system'; +import { LandingPage, PSInfoKey } from '@google-psat/design-system'; +import { I18n } from '@google-psat/i18n'; const Attribution = () => { return (
    diff --git a/packages/extension/src/view/devtools/components/privateAdvertising/privateAdvertising.tsx b/packages/extension/src/view/devtools/components/privateAdvertising/privateAdvertising.tsx index 479e02f9f..7e544c144 100644 --- a/packages/extension/src/view/devtools/components/privateAdvertising/privateAdvertising.tsx +++ b/packages/extension/src/view/devtools/components/privateAdvertising/privateAdvertising.tsx @@ -18,7 +18,8 @@ * External Dependencies */ import React from 'react'; -import { ContentPanel, LandingPage } from '@ps-analysis-tool/design-system'; +import { ContentPanel, LandingPage } from '@google-psat/design-system'; +import { I18n } from '@google-psat/i18n'; const content = [ { @@ -50,7 +51,7 @@ const content = [ const PrivateAdvertising = () => { return ( { + return ( +
    + +
    + ); +}; + +export default ProtectedAudience; diff --git a/packages/extension/src/view/devtools/components/privateAdvertising/tests/index.tsx b/packages/extension/src/view/devtools/components/privateAdvertising/tests/index.tsx index ccf9d90b3..e2b5e3d30 100644 --- a/packages/extension/src/view/devtools/components/privateAdvertising/tests/index.tsx +++ b/packages/extension/src/view/devtools/components/privateAdvertising/tests/index.tsx @@ -30,6 +30,7 @@ import Topics from '../topics'; import PSInfo from 'ps-analysis-tool/data/PSInfo.json'; import { act } from 'react-dom/test-utils'; import PrivateAdvertising from '../privateAdvertising'; +import { I18n } from '@google-psat/i18n'; describe('Private advertising Landing Pages', () => { beforeAll(() => { @@ -43,6 +44,14 @@ describe('Private advertising Landing Pages', () => { text: () => Promise.resolve({}), }); } as unknown as typeof fetch; + + globalThis.chrome.i18n = null; + + I18n.initMessages({ + privateAdvertising: { + message: 'Private Advertising', + }, + }); }); it('should render Attribution', async () => { @@ -60,7 +69,9 @@ describe('Private advertising Landing Pages', () => { render(); }); - expect(await screen.findByText('Private Advertising')).toBeInTheDocument(); + expect( + await screen.findByText(I18n.getMessage('privateAdvertising')) + ).toBeInTheDocument(); }); it('should render Topics', async () => { diff --git a/packages/extension/src/view/devtools/components/privateAdvertising/topics/index.tsx b/packages/extension/src/view/devtools/components/privateAdvertising/topics/index.tsx index a59870f33..439d6ca13 100644 --- a/packages/extension/src/view/devtools/components/privateAdvertising/topics/index.tsx +++ b/packages/extension/src/view/devtools/components/privateAdvertising/topics/index.tsx @@ -17,13 +17,14 @@ * External dependencies. */ import React from 'react'; -import { LandingPage, PSInfoKey } from '@ps-analysis-tool/design-system'; +import { LandingPage, PSInfoKey } from '@google-psat/design-system'; +import { I18n } from '@google-psat/i18n'; const Topics = () => { return (
    diff --git a/packages/extension/src/view/devtools/components/settings/components/informationContainer.tsx b/packages/extension/src/view/devtools/components/settings/components/informationContainer.tsx index ac9254ea1..8d5898485 100644 --- a/packages/extension/src/view/devtools/components/settings/components/informationContainer.tsx +++ b/packages/extension/src/view/devtools/components/settings/components/informationContainer.tsx @@ -17,8 +17,9 @@ * External dependencies. */ import React, { useCallback, useEffect, useRef, useState } from 'react'; -import { ArrowUp, Copy } from '@ps-analysis-tool/design-system'; +import { ArrowUp, Copy } from '@google-psat/design-system'; import classNames from 'classnames'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies */ @@ -122,7 +123,7 @@ const InformationContainer = () => {
    - System Information + {I18n.getMessage('systemInformation')}
    {
    - Open Tabs + + {I18n.getMessage('openTabs')} + {currentTabs}
    - Chrome version + {I18n.getMessage('chromeVersion')} {browserInformation} @@ -164,7 +167,7 @@ const InformationContainer = () => {
    - PSAT version + {I18n.getMessage('pSATVersion')} {PSATVersion} @@ -172,7 +175,7 @@ const InformationContainer = () => {
    - OS - System Architecture + {I18n.getMessage('systemArchitecture')} {OSInformation} @@ -182,7 +185,7 @@ const InformationContainer = () => {
    - Active Extensions + {I18n.getMessage('activeExtensions')}
      {currentExtensions?.map((extension, index) => { diff --git a/packages/extension/src/view/devtools/components/settings/components/settingOption.tsx b/packages/extension/src/view/devtools/components/settings/components/settingOption.tsx index 9dabdec6f..94bbe2c60 100644 --- a/packages/extension/src/view/devtools/components/settings/components/settingOption.tsx +++ b/packages/extension/src/view/devtools/components/settings/components/settingOption.tsx @@ -16,7 +16,7 @@ /** * External dependencies. */ -import { ToggleSwitch } from '@ps-analysis-tool/design-system'; +import { ToggleSwitch } from '@google-psat/design-system'; import React from 'react'; interface SettingOptionProps { diff --git a/packages/extension/src/view/devtools/components/settings/components/settingsContainer.tsx b/packages/extension/src/view/devtools/components/settings/components/settingsContainer.tsx index d64377130..cd098e4ca 100644 --- a/packages/extension/src/view/devtools/components/settings/components/settingsContainer.tsx +++ b/packages/extension/src/view/devtools/components/settings/components/settingsContainer.tsx @@ -17,6 +17,7 @@ * External dependencies. */ import React, { useMemo } from 'react'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies @@ -52,6 +53,8 @@ const SettingsContainer = () => { case 'enableCDP': settingsToReturn.push({ ...setting, + heading: setting.heading(), + description: setting.description(), changeSwitchState: setIsUsingCDP, switchState: isUsingCDP, }); @@ -59,6 +62,8 @@ const SettingsContainer = () => { case 'multitabDebugging': settingsToReturn.push({ ...setting, + heading: setting.heading(), + description: setting.description(), changeSwitchState: setProcessingMode, switchState: allowedNumberOfTabs === 'unlimited', }); @@ -77,7 +82,7 @@ const SettingsContainer = () => {
      - PSAT Extension Settings + {I18n.getMessage('pSATSettings')}
      diff --git a/packages/extension/src/view/devtools/components/settings/components/tests/informationContainer.tsx b/packages/extension/src/view/devtools/components/settings/components/tests/informationContainer.tsx index f87c2ce9f..65d469ef4 100644 --- a/packages/extension/src/view/devtools/components/settings/components/tests/informationContainer.tsx +++ b/packages/extension/src/view/devtools/components/settings/components/tests/informationContainer.tsx @@ -19,6 +19,7 @@ import React, { act } from 'react'; import { render, screen } from '@testing-library/react'; import '@testing-library/jest-dom'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies */ @@ -32,6 +33,34 @@ jest.mock('../../../../stateProviders/settings', () => ({ const mockUseSettingsStore = useSettings as jest.Mock; describe('InformationContainer', () => { + beforeAll(() => { + globalThis.chrome.i18n = null; + + I18n.initMessages({ + openTabs: { + message: 'Open Tabs', + }, + activeExtensions: { + message: 'Active Extensions', + }, + chromeVersion: { + message: 'Chrome Version', + }, + pSATVersion: { + message: 'PSAT Version', + }, + systemArchitecture: { + message: 'OS - System Architecture', + }, + systemInformation: { + message: 'System Information', + }, + copyToClipboard: { + message: 'Copy to clipboard', + }, + }); + }); + it('should render the component', () => { mockUseSettingsStore.mockReturnValue({ currentTabs: 0, diff --git a/packages/extension/src/view/devtools/components/settings/components/tests/settingsContainer.tsx b/packages/extension/src/view/devtools/components/settings/components/tests/settingsContainer.tsx index 603e6bed7..f009a50ac 100644 --- a/packages/extension/src/view/devtools/components/settings/components/tests/settingsContainer.tsx +++ b/packages/extension/src/view/devtools/components/settings/components/tests/settingsContainer.tsx @@ -25,7 +25,7 @@ import '@testing-library/jest-dom'; */ import SettingsContainer from '../settingsContainer'; import { useSettings } from '../../../../stateProviders'; -import { noop } from '@ps-analysis-tool/common'; +import { noop } from '@google-psat/common'; jest.mock('../../../../stateProviders', () => ({ useSettings: jest.fn(), diff --git a/packages/extension/src/view/devtools/components/settings/index.tsx b/packages/extension/src/view/devtools/components/settings/index.tsx index 6163cb471..1510bfbc0 100644 --- a/packages/extension/src/view/devtools/components/settings/index.tsx +++ b/packages/extension/src/view/devtools/components/settings/index.tsx @@ -18,7 +18,8 @@ */ import React, { useState } from 'react'; import classNames from 'classnames'; -import { ArrowUp } from '@ps-analysis-tool/design-system'; +import { ArrowUp } from '@google-psat/design-system'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies @@ -46,7 +47,7 @@ const Settings = () => { className="flex gap-2 text-2xl font-bold items-baseline dark:text-bright-gray cursor-pointer" onClick={() => setOpen((prevOpen) => !prevOpen)} > -

      Settings

      +

      {I18n.getMessage('settings')}

      { return (
      diff --git a/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/index.tsx b/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/index.tsx index 2e10c10fc..cb4bf84d5 100644 --- a/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/index.tsx +++ b/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/index.tsx @@ -17,7 +17,8 @@ * External dependencies. */ import React from 'react'; -import { LandingPage, PSInfoKey } from '@ps-analysis-tool/design-system'; +import { LandingPage, PSInfoKey } from '@google-psat/design-system'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. @@ -29,7 +30,7 @@ const RelatedWebsiteSets = () => { return (
      diff --git a/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/insights/index.tsx b/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/insights/index.tsx index 1b1935d66..d063eec38 100644 --- a/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/insights/index.tsx +++ b/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/insights/index.tsx @@ -19,6 +19,7 @@ */ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { getDomain } from 'tldts'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies @@ -82,22 +83,22 @@ const Insights = () => {
      {loading ? (
      -

      Loading...

      +

      {I18n.getMessage('loading')}...

      ) : (

      - Related Website Sets Membership + {I18n.getMessage('membership')}

      {insightsData?.isURLInRWS ? ( <>

      - This site - belongs to a "Related Website Set" + {' '} + {I18n.getMessage('belongsToRWS')}

      - Primary Domain:{' '} + {I18n.getMessage('primaryDomain')}:{' '} { <> {insightsData.isccTLD ? (

      - This site is a ccTLD of{' '} + {I18n.getMessage('siteccTldOf')} { > {insightsData.relatedWebsiteSet?.ccTLDParent} - .

      ) : ( <> @@ -135,26 +135,24 @@ const Insights = () => { ) .map(([domain, value]) => (

      - Rationale:{' '} - {value as string} + {I18n.getMessage('rationale')}: + {I18n.getMessage(value)}

      ))} )} ) : ( -

      - This site is the primary domain of the Related Website Set. -

      +

      {I18n.getMessage('rWSPrimaryDomain')}

      )}
      @@ -163,7 +161,7 @@ const Insights = () => { ) : (

      - This site does not belong to a "Related Website Set" + {I18n.getMessage('notBelongToRWS')}

      )}
      diff --git a/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/insights/tests/insights.tsx b/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/insights/tests/insights.tsx index 2ea4c6e6f..3a60c68d3 100644 --- a/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/insights/tests/insights.tsx +++ b/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/insights/tests/insights.tsx @@ -20,7 +20,7 @@ import React from 'react'; import { render, screen, waitFor } from '@testing-library/react'; import '@testing-library/jest-dom'; -import { noop } from '@ps-analysis-tool/design-system'; +import { noop } from '@google-psat/design-system'; /** * Internal dependencies. diff --git a/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/associatedSites/index.tsx b/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/associatedSites/index.tsx index 8e3b61816..ff6ba06c9 100644 --- a/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/associatedSites/index.tsx +++ b/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/associatedSites/index.tsx @@ -18,7 +18,8 @@ * External dependencies. */ import React from 'react'; -import { InfoIcon } from '@ps-analysis-tool/design-system'; +import { InfoIcon } from '@google-psat/design-system'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. @@ -46,8 +47,8 @@ const AssociatedSites = ({

      - Associated Subset - + {I18n.getMessage('associatedSubset')} +

      @@ -59,7 +60,9 @@ const AssociatedSites = ({
      { @@ -75,9 +78,11 @@ const AssociatedSites = ({
      { setAssociatedSites({ idx, @@ -88,10 +93,7 @@ const AssociatedSites = ({ error={rationaleError} formValidationFailed={formValidationFailed} /> - - How is the affiliation across domains presented and why users - would expect it - + {I18n.getMessage('affiliationNote')}
      removeAssociatedSite(idx)} /> diff --git a/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/components/button/addButton.tsx b/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/components/button/addButton.tsx index a651e96d7..b65c9cd73 100644 --- a/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/components/button/addButton.tsx +++ b/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/components/button/addButton.tsx @@ -18,7 +18,7 @@ * External dependencies. */ import React from 'react'; -import { Add, Button } from '@ps-analysis-tool/design-system'; +import { Add, Button } from '@google-psat/design-system'; interface AddButtonProps { onClick: () => void; diff --git a/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/components/button/removeButton.tsx b/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/components/button/removeButton.tsx index ed9f4ed23..a5d134036 100644 --- a/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/components/button/removeButton.tsx +++ b/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/components/button/removeButton.tsx @@ -22,7 +22,7 @@ import React from 'react'; /** * Internal dependencies. */ -import { Button, Cross } from '@ps-analysis-tool/design-system'; +import { Button, Cross } from '@google-psat/design-system'; interface RemoveButtonProps { onClick: () => void; diff --git a/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/contactEmail/index.tsx b/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/contactEmail/index.tsx index c981201a9..9afdda45e 100644 --- a/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/contactEmail/index.tsx +++ b/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/contactEmail/index.tsx @@ -18,6 +18,7 @@ * External dependencies. */ import React from 'react'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies @@ -39,8 +40,8 @@ const ContactEmail = ({ return (
      setContact(e.target.value)} error={contact.emailError} diff --git a/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/countrySites/index.tsx b/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/countrySites/index.tsx index 938f3dbca..4e97aa5bb 100644 --- a/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/countrySites/index.tsx +++ b/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/countrySites/index.tsx @@ -18,6 +18,7 @@ * External dependencies. */ import React, { useEffect } from 'react'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. @@ -64,9 +65,7 @@ const CountrySites = ({ return (
      -

      - Country Code Top-level Domains (ccTLDs) -

      +

      {I18n.getMessage('countryCodeHeading')}

      @@ -74,12 +73,12 @@ const CountrySites = ({
      { setCountrySites({ idx, key: 'site', value: e.target.value }); }} - defaultOption="Select a site" + defaultOption={I18n.getMessage('selectSite')} options={availableSites} error={siteError} formValidationFailed={formValidationFailed} @@ -87,7 +86,9 @@ const CountrySites = ({
      { @@ -96,7 +97,7 @@ const CountrySites = ({ error={cctldError} formValidationFailed={formValidationFailed} /> - Country code top-level domain related to the site + {I18n.getMessage('countryCodeNote')}
      removeCountrySite(idx)} /> diff --git a/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/index.tsx b/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/index.tsx index 55250c1f6..f589993f6 100644 --- a/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/index.tsx +++ b/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/index.tsx @@ -18,7 +18,8 @@ * External dependencies. */ import React from 'react'; -import { Button } from '@ps-analysis-tool/design-system'; +import { Button } from '@google-psat/design-system'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. @@ -66,26 +67,26 @@ const RWSJsonGenerator = () => {

      - Related Website Sets JSON Generator + {I18n.getMessage('rwsJsonGenerator')}

      -

      - This tool generates the JSON resources needed to make a Related - Website Sets(RWS) submission. It does not perform all the - required technical validations (see full requirements{' '} - - here - - ). -

      + >`, + '', + ]), + }} + />

      - Enter your Related Website Sets details below: + {I18n.getMessage('enterRwsDetails')}

      - Here are your JSON resources: + {I18n.getMessage('rWSJSONHeading')}:
      - Please follow the steps below to submit your Related Website Set - to the canonical list. + {I18n.getMessage('followInstructions', ['Related Website Set'])}

      -
        {primaryWellKnownOutput && primaryWellKnownOutput.associatedSites && (
        -

        Associated Sites

        +

        {I18n.getMessage('associatedSites')}

        {primaryWellKnownOutput.associatedSites.map((url) => (
      • @@ -65,7 +62,7 @@ const OtherDomainOutput = ({ )} {primaryWellKnownOutput && primaryWellKnownOutput.serviceSites && (
        -

        Service Sites

        +

        {I18n.getMessage('serviceSites')}

        {primaryWellKnownOutput.serviceSites.map((url) => (
      • @@ -77,7 +74,7 @@ const OtherDomainOutput = ({ )} {primaryWellKnownOutput && primaryWellKnownOutput.ccTLDs && (
        -

        Country Sites

        +

        {I18n.getMessage('countrySites')}

        {Object.values(primaryWellKnownOutput.ccTLDs).map((cctlds) => cctlds.map((cctld) => ( diff --git a/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/jsonOutput/output.tsx b/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/jsonOutput/output.tsx index 80b77ce63..1436a93ef 100644 --- a/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/jsonOutput/output.tsx +++ b/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/jsonOutput/output.tsx @@ -19,6 +19,7 @@ */ import React, { useState } from 'react'; import CopyToClipboard from 'react-copy-to-clipboard'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. @@ -44,7 +45,9 @@ const Output = ({ data }: OutputProps) => { setTimeout(() => setCopied(false), 2000); }} > - + {data && JSON.stringify(data, null, 2)} diff --git a/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/jsonOutput/primaryDomainOutput.tsx b/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/jsonOutput/primaryDomainOutput.tsx index f2e083ff3..0f1f2d6ce 100644 --- a/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/jsonOutput/primaryDomainOutput.tsx +++ b/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/jsonOutput/primaryDomainOutput.tsx @@ -24,6 +24,7 @@ import React from 'react'; */ import type { PrimaryWellKnownOutputType } from '../types'; import Output from './output'; +import { I18n } from '@google-psat/i18n'; interface PrimaryDomainOutputProps { primaryWellKnownOutput: PrimaryWellKnownOutputType | null; @@ -38,11 +39,7 @@ const PrimaryDomainOutput = ({

        1

        -

        - Add the file related-website-set.json in the directory - .well-known of the primary domain with the - following content: -

        +

        {I18n.getMessage('addToRws')}

      • diff --git a/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/primaryDomain/index.tsx b/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/primaryDomain/index.tsx index a5ff448ee..fcae3e0e7 100644 --- a/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/primaryDomain/index.tsx +++ b/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/primaryDomain/index.tsx @@ -18,6 +18,7 @@ * External dependencies. */ import React from 'react'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. @@ -39,7 +40,7 @@ const PrimaryDomain = ({ return (
        setPrimaryDomain(e.target.value)} diff --git a/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/serviceSites/index.tsx b/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/serviceSites/index.tsx index 9f3000718..3cd960b7b 100644 --- a/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/serviceSites/index.tsx +++ b/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/serviceSites/index.tsx @@ -18,6 +18,7 @@ * External dependencies. */ import React from 'react'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. @@ -44,7 +45,7 @@ const ServiceSites = ({ return (
        -

        Service Subset

        +

        {I18n.getMessage('serviceSubset')}

        @@ -53,7 +54,9 @@ const ServiceSites = ({
        { @@ -65,8 +68,10 @@ const ServiceSites = ({
        { setServiceSites({ @@ -78,10 +83,7 @@ const ServiceSites = ({ error={rationaleError} formValidationFailed={formValidationFailed} /> - - How is the affiliation across domains presented and why users - would expect it - + {I18n.getMessage('affiliationNote')}
        removeServiceSite(idx)} /> diff --git a/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/tests/jsonGenerator.tsx b/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/tests/jsonGenerator.tsx index 390752b4d..5a6ad2cf9 100644 --- a/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/tests/jsonGenerator.tsx +++ b/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/tests/jsonGenerator.tsx @@ -21,7 +21,8 @@ import React from 'react'; import { fireEvent, render } from '@testing-library/react'; import '@testing-library/jest-dom'; import '@testing-library/user-event'; -import { noop } from '@ps-analysis-tool/design-system'; +import { noop } from '@google-psat/design-system'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. @@ -38,6 +39,21 @@ import JsonOutput from '../jsonOutput'; describe('RWSJsonGenerator', () => { beforeAll(() => { window.HTMLElement.prototype.scrollIntoView = jest.fn(); + globalThis.chrome.i18n = null; + I18n.initMessages({ + contactNote: { + message: 'Email address or group alias if available', + }, + shouldMatchFormat: { + message: 'should be matching the format https://example.com', + }, + affiliationHeading: { + message: 'Affiliation to primary domain', + }, + rwsJsonGenerator: { + message: 'Related Website Sets JSON Generator', + }, + }); }); it('should render form', () => { @@ -73,7 +89,7 @@ describe('RWSJsonGenerator', () => { expect( await screen.findByText( - 'Url should be matching the format https://' + 'Url should be matching the format https://example.com' ) ).toBeInTheDocument(); @@ -104,7 +120,7 @@ describe('RWSJsonGenerator', () => { expect( ( await screen.findAllByText( - 'Url should be matching the format https://' + 'Url should be matching the format https://example.com' ) )[0] ).toBeInTheDocument(); @@ -157,7 +173,7 @@ describe('RWSJsonGenerator', () => { expect( ( await screen.findAllByText( - 'Url should be matching the format https://' + 'Url should be matching the format https://example.com' ) )[1] ).toBeInTheDocument(); @@ -206,7 +222,7 @@ describe('RWSJsonGenerator', () => { expect( ( await screen.findAllByText( - 'Url should be matching the format https://' + 'Url should be matching the format https://example.com' ) )[1] ).toBeInTheDocument(); @@ -214,7 +230,7 @@ describe('RWSJsonGenerator', () => { expect( ( await screen.findAllByText( - 'Url should be matching the format https://' + 'Url should be matching the format https://example.com' ) )[2] ).toBeInTheDocument(); diff --git a/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/utils/validateUrl.ts b/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/utils/validateUrl.ts index 65e300f60..3215de69d 100644 --- a/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/utils/validateUrl.ts +++ b/packages/extension/src/view/devtools/components/siteBoundaries/relatedWebsiteSets/jsonGenerator/utils/validateUrl.ts @@ -17,6 +17,7 @@ /** * External dependencies. */ +import { I18n } from '@google-psat/i18n'; import { validate } from 'validate.js'; const validateUrl = (url: string) => { @@ -25,7 +26,7 @@ const validateUrl = (url: string) => { { url: { url: { - message: 'should be matching the format https://', + message: I18n.getMessage('shouldMatchFormat'), schemes: ['https'], }, }, diff --git a/packages/extension/src/view/devtools/components/siteBoundaries/siteBoundaries.tsx b/packages/extension/src/view/devtools/components/siteBoundaries/siteBoundaries.tsx index 59f6643f9..2c599feb4 100644 --- a/packages/extension/src/view/devtools/components/siteBoundaries/siteBoundaries.tsx +++ b/packages/extension/src/view/devtools/components/siteBoundaries/siteBoundaries.tsx @@ -17,7 +17,8 @@ * External Dependencies */ import React from 'react'; -import { ContentPanel, LandingPage } from '@ps-analysis-tool/design-system'; +import { ContentPanel, LandingPage } from '@google-psat/design-system'; +import { I18n } from '@google-psat/i18n'; const content = [ { @@ -48,7 +49,7 @@ const content = [ const SiteBoundaries = () => { return ( { - try { - if (!connectedToPort && !canStartInspecting) { - connectToPort(); - } + (async () => { + try { + if (!connectedToPort && !canStartInspecting) { + connectToPort(); + } - if (!isInspecting && portRef.current && canStartInspecting) { - portRef.current.postMessage({ - isInspecting: false, - }); + if (!isInspecting && portRef.current && canStartInspecting) { + portRef.current.postMessage({ + isInspecting: false, + }); - return; - } + return; + } - if ( - chrome.runtime?.id && - portRef.current && - tabFrames && - canStartInspecting - ) { - const thirdPartyCookies = filteredCookies - ? filteredCookies.filter((cookie) => !cookie.isFirstParty) - : []; - const firstPartyCookies = filteredCookies - ? filteredCookies.filter((cookie) => cookie.isFirstParty) - : []; - const blockedCookies = filteredCookies - ? filteredCookies.filter( - (cookie) => - cookie.isBlocked || - (cookie.blockedReasons?.length !== undefined && - cookie.blockedReasons?.length > 0) - ) - : []; - const blockedReasons = filteredCookies - ? filteredCookies - .filter((cookie) => cookie.isBlocked) - .reduce((previousReasons: string[], cookie) => { - if ( - cookie.blockedReasons?.length !== undefined && - cookie.blockedReasons?.length > 0 - ) { - return [ - ...new Set([ - ...previousReasons, - ...(cookie.blockedReasons || []), - ]), - ]; - } - return [...new Set([...previousReasons])]; - }, []) - : []; - portRef.current?.postMessage({ - selectedFrame, - removeAllFramePopovers: isFrameSelectedFromDevTool, - thirdPartyCookies: thirdPartyCookies.length, - firstPartyCookies: firstPartyCookies.length, - blockedCookies: blockedCookies.length, - blockedReasons: blockedReasons.join(', '), - isInspecting, - isOnRWS: selectedFrame ? tabFrames[selectedFrame]?.isOnRWS : false, - }); + if ( + chrome.runtime?.id && + portRef.current && + tabFrames && + canStartInspecting + ) { + const thirdPartyCookies = filteredCookies + ? filteredCookies.filter((cookie) => !cookie.isFirstParty) + : []; + const firstPartyCookies = filteredCookies + ? filteredCookies.filter((cookie) => cookie.isFirstParty) + : []; + const blockedCookies = filteredCookies + ? filteredCookies.filter( + (cookie) => + cookie.isBlocked || + (cookie.blockedReasons?.length !== undefined && + cookie.blockedReasons?.length > 0) + ) + : []; + const blockedReasons = filteredCookies + ? filteredCookies + .filter((cookie) => cookie.isBlocked) + .reduce((previousReasons: string[], cookie) => { + if ( + cookie.blockedReasons?.length !== undefined && + cookie.blockedReasons?.length > 0 + ) { + return [ + ...new Set([ + ...previousReasons, + ...(cookie.blockedReasons || []), + ]), + ]; + } + return [...new Set([...previousReasons])]; + }, []) + : []; + + const isFrameOnRWS = selectedFrame + ? await isOnRWS(selectedFrame) + : false; + + portRef.current?.postMessage({ + selectedFrame, + removeAllFramePopovers: isFrameSelectedFromDevTool, + thirdPartyCookies: thirdPartyCookies.length, + firstPartyCookies: firstPartyCookies.length, + blockedCookies: blockedCookies.length, + blockedReasons: blockedReasons.join(', '), + isInspecting, + isOnRWS: isFrameOnRWS, + }); + } + } catch (error) { + // Silently fail. } - } catch (error) { - // Silently fail. - } + })(); }, [ canStartInspecting, connectToPort, diff --git a/packages/extension/src/view/devtools/index.tsx b/packages/extension/src/view/devtools/index.tsx index a880df08d..54207df66 100644 --- a/packages/extension/src/view/devtools/index.tsx +++ b/packages/extension/src/view/devtools/index.tsx @@ -22,8 +22,8 @@ import { ErrorBoundary } from 'react-error-boundary'; import { ErrorFallback, Provider as TablePersistentSettingsProvider, -} from '@ps-analysis-tool/design-system'; -import { LibraryDetectionProvider } from '@ps-analysis-tool/library-detection'; +} from '@google-psat/design-system'; +import { LibraryDetectionProvider } from '@google-psat/library-detection'; /** * Internal dependencies. diff --git a/packages/extension/src/view/devtools/stateProviders/allowedList/context.ts b/packages/extension/src/view/devtools/stateProviders/allowedList/context.ts index 08a305a3a..8cd054e5d 100644 --- a/packages/extension/src/view/devtools/stateProviders/allowedList/context.ts +++ b/packages/extension/src/view/devtools/stateProviders/allowedList/context.ts @@ -17,7 +17,7 @@ /** * External dependencies. */ -import { noop, createContext } from '@ps-analysis-tool/common'; +import { noop, createContext } from '@google-psat/common'; export interface AllowedListStoreContext { state: { diff --git a/packages/extension/src/view/devtools/stateProviders/allowedList/useAllowedList.ts b/packages/extension/src/view/devtools/stateProviders/allowedList/useAllowedList.ts index 6e801a492..cccd05820 100644 --- a/packages/extension/src/view/devtools/stateProviders/allowedList/useAllowedList.ts +++ b/packages/extension/src/view/devtools/stateProviders/allowedList/useAllowedList.ts @@ -17,7 +17,7 @@ /** * External dependencies. */ -import { useContextSelector } from '@ps-analysis-tool/common'; +import { useContextSelector } from '@google-psat/common'; /** * Internal dependencies. */ diff --git a/packages/extension/src/view/devtools/stateProviders/cookie/context.ts b/packages/extension/src/view/devtools/stateProviders/cookie/context.ts index c2fedb938..0b541ca98 100644 --- a/packages/extension/src/view/devtools/stateProviders/cookie/context.ts +++ b/packages/extension/src/view/devtools/stateProviders/cookie/context.ts @@ -21,7 +21,7 @@ import { createContext, type TabCookies, type TabFrames, -} from '@ps-analysis-tool/common'; +} from '@google-psat/common'; export interface CookieStoreContext { state: { diff --git a/packages/extension/src/view/devtools/stateProviders/cookie/cookieProvider.tsx b/packages/extension/src/view/devtools/stateProviders/cookie/cookieProvider.tsx index d1f1bd8a1..50b190f75 100644 --- a/packages/extension/src/view/devtools/stateProviders/cookie/cookieProvider.tsx +++ b/packages/extension/src/view/devtools/stateProviders/cookie/cookieProvider.tsx @@ -24,7 +24,7 @@ import React, { useRef, useMemo, } from 'react'; -import { type TabCookies } from '@ps-analysis-tool/common'; +import { type TabCookies } from '@google-psat/common'; /** * Internal dependencies. @@ -72,25 +72,40 @@ const Provider = ({ children }: PropsWithChildren) => { // This was converted to useRef because setting state was creating a race condition in rerendering the provider. const isCurrentTabBeingListenedToRef = useRef(false); - const { allowedNumberOfTabs, setSettingsChanged } = useSettings( + const { allowedNumberOfTabs, setSettingsChanged, isUsingCDP } = useSettings( ({ state, actions }) => ({ allowedNumberOfTabs: state.allowedNumberOfTabs, setSettingsChanged: actions.setSettingsChanged, + isUsingCDP: state.isUsingCDP, }) ); /** * Set tab frames state for frame ids and frame URLs from using chrome.webNavigation.getAllFrames */ - const getAllFramesForCurrentTab = useCallback(async () => { - const _tabFrames = await getFramesForCurrentTab(); - setTabFrames(_tabFrames); - }, []); + const getAllFramesForCurrentTab = useCallback( + async (extraFrameData?: Record) => { + const currentTabFrames = await chrome.webNavigation.getAllFrames({ + tabId: chrome.devtools.inspectedWindow.tabId, + }); + + const currentTargets = await chrome.debugger.getTargets(); + + setTabFrames((prevState) => + getFramesForCurrentTab( + prevState, + currentTabFrames, + currentTargets, + extraFrameData ?? {}, + isUsingCDP + ) + ); + }, + [isUsingCDP] + ); /** * Stores object with frame URLs as keys and boolean values indicating if the frame contains cookies. - * - * TODO: Can be moved to a utility function. */ const frameHasCookies = useMemo(() => { if (!tabCookies) { @@ -101,6 +116,9 @@ const Provider = ({ children }: PropsWithChildren) => { Record >((acc, [url, frame]) => { frame.frameIds?.forEach((id) => { + if (!id) { + return; + } acc[id] = url; }); @@ -118,24 +136,6 @@ const Provider = ({ children }: PropsWithChildren) => { } }); - if (cookie.frameIdList?.length === 0) { - if ( - cookie.frameIdList && - cookie.frameIdList.length === 0 && - cookie.parsedCookie.domain - ) { - const domainToCheck = cookie.parsedCookie.domain.startsWith('.') - ? cookie.parsedCookie.domain.slice(1) - : cookie.parsedCookie.domain; - - Object.values(tabFramesIdsWithURL).forEach((frameUrl) => { - if (frameUrl.includes(domainToCheck)) { - acc[frameUrl] = true; - } - }); - } - } - return acc; }, {}); @@ -216,6 +216,16 @@ const Provider = ({ children }: PropsWithChildren) => { } }, [allowedNumberOfTabs, tabFrames]); + useEffect(() => { + chrome.runtime.sendMessage({ + type: 'GET_REST_DATA_FROM_URL', + payload: { + tabId: chrome.devtools.inspectedWindow.tabId, + selectedFrame, + }, + }); + }, [selectedFrame]); + const messagePassingListener = useCallback( // eslint-disable-next-line complexity async (message: { @@ -225,6 +235,9 @@ const Provider = ({ children }: PropsWithChildren) => { cookieData?: TabCookies; tabToRead?: string; tabMode?: string; + extraData?: { + extraFrameData?: Record; + }; psatOpenedAfterPageLoad?: boolean; }; }) => { @@ -279,12 +292,13 @@ const Provider = ({ children }: PropsWithChildren) => { message?.payload?.cookieData ) { const data = message.payload.cookieData; + const frameData = message.payload.extraData?.extraFrameData ?? {}; if (tabId.toString() === message.payload.tabId.toString()) { if (isCurrentTabBeingListenedToRef.current) { - await getAllFramesForCurrentTab(); setTabToRead(tabId.toString()); setTabCookies(Object.keys(data).length > 0 ? data : null); + await getAllFramesForCurrentTab(frameData); } else { setTabFrames(null); } diff --git a/packages/extension/src/view/devtools/stateProviders/cookie/useCookie.ts b/packages/extension/src/view/devtools/stateProviders/cookie/useCookie.ts index c1c215f3a..6e62f50fe 100644 --- a/packages/extension/src/view/devtools/stateProviders/cookie/useCookie.ts +++ b/packages/extension/src/view/devtools/stateProviders/cookie/useCookie.ts @@ -16,7 +16,7 @@ /** * External dependencies. */ -import { useContextSelector } from '@ps-analysis-tool/common'; +import { useContextSelector } from '@google-psat/common'; /** * Internal dependencies. diff --git a/packages/extension/src/view/devtools/stateProviders/settings/context.ts b/packages/extension/src/view/devtools/stateProviders/settings/context.ts index ab8b0cd26..5f336f5e3 100644 --- a/packages/extension/src/view/devtools/stateProviders/settings/context.ts +++ b/packages/extension/src/view/devtools/stateProviders/settings/context.ts @@ -16,7 +16,7 @@ /** * External dependencies. */ -import { noop, createContext } from '@ps-analysis-tool/common'; +import { noop, createContext } from '@google-psat/common'; export interface SettingsStoreContext { state: { diff --git a/packages/extension/src/view/devtools/stateProviders/settings/settingsProvider.tsx b/packages/extension/src/view/devtools/stateProviders/settings/settingsProvider.tsx index 9c852a3a1..9b4fe91e1 100644 --- a/packages/extension/src/view/devtools/stateProviders/settings/settingsProvider.tsx +++ b/packages/extension/src/view/devtools/stateProviders/settings/settingsProvider.tsx @@ -22,6 +22,7 @@ import React, { useState, useCallback, } from 'react'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. */ @@ -135,7 +136,11 @@ const Provider = ({ children }: PropsWithChildren) => { const browserInfo = /Chrome\/([0-9.]+)/.exec(navigator.userAgent); if (browserInfo) { setBrowserInformation( - 'Version ' + browserInfo[1] + ' ' + OSInformation?.split(' ')[1] + I18n.getMessage('version') + + ' ' + + browserInfo[1] + + ' ' + + OSInformation?.split(' ')[1] ); } } diff --git a/packages/extension/src/view/devtools/stateProviders/settings/useSettings.ts b/packages/extension/src/view/devtools/stateProviders/settings/useSettings.ts index 20807b751..e9e3abd28 100644 --- a/packages/extension/src/view/devtools/stateProviders/settings/useSettings.ts +++ b/packages/extension/src/view/devtools/stateProviders/settings/useSettings.ts @@ -16,7 +16,7 @@ /** * External dependencies */ -import { useContextSelector } from '@ps-analysis-tool/common'; +import { useContextSelector } from '@google-psat/common'; /** * Internal dependencies. */ diff --git a/packages/extension/src/view/devtools/tabs.ts b/packages/extension/src/view/devtools/tabs.ts index 1d9c73b37..0b3868c99 100644 --- a/packages/extension/src/view/devtools/tabs.ts +++ b/packages/extension/src/view/devtools/tabs.ts @@ -42,7 +42,8 @@ import { type SidebarItems, InfoIcon, SIDEBAR_ITEMS_KEYS, -} from '@ps-analysis-tool/design-system'; + GroupsIcon, +} from '@google-psat/design-system'; /** * Internal dependencies. @@ -67,10 +68,12 @@ import { Settings, FacilitatedTesting, } from './components'; +import { I18n } from '@google-psat/i18n'; +import ProtectedAudience from './components/privateAdvertising/protectedAudience'; const TABS: SidebarItems = { [SIDEBAR_ITEMS_KEYS.PRIVACY_SANDBOX]: { - title: 'Privacy Sandbox', + title: () => 'Privacy Sandbox', panel: { Element: PrivacySandbox, }, @@ -83,7 +86,7 @@ const TABS: SidebarItems = { dropdownOpen: true, children: { [SIDEBAR_ITEMS_KEYS.COOKIES]: { - title: 'Cookies', + title: () => I18n.getMessage('cookies'), icon: { Element: CookieIcon, }, @@ -94,7 +97,7 @@ const TABS: SidebarItems = { dropdownOpen: true, }, [SIDEBAR_ITEMS_KEYS.SITE_BOUNDARIES]: { - title: 'Site Boundaries', + title: () => I18n.getMessage('siteBoundaries'), panel: { Element: SiteBoundaries, }, @@ -106,7 +109,7 @@ const TABS: SidebarItems = { }, children: { [SIDEBAR_ITEMS_KEYS.CHIPS]: { - title: 'CHIPS', + title: () => I18n.getMessage('chips'), panel: { Element: Chips, }, @@ -119,7 +122,7 @@ const TABS: SidebarItems = { children: {}, }, [SIDEBAR_ITEMS_KEYS.RELATED_WEBSITE_SETS]: { - title: 'Related Website Sets', + title: () => I18n.getMessage('rws'), panel: { Element: RelatedWebsiteSets, }, @@ -134,7 +137,7 @@ const TABS: SidebarItems = { }, }, [SIDEBAR_ITEMS_KEYS.PRIVATE_ADVERTISING]: { - title: 'Private Advertising', + title: () => I18n.getMessage('privateAdvertising'), panel: { Element: PrivateAdvertising, }, @@ -146,7 +149,7 @@ const TABS: SidebarItems = { }, children: { [SIDEBAR_ITEMS_KEYS.TOPICS]: { - title: 'Topics', + title: () => I18n.getMessage('topics'), panel: { Element: Topics, }, @@ -159,7 +162,7 @@ const TABS: SidebarItems = { children: {}, }, [SIDEBAR_ITEMS_KEYS.ATTRIBUTION]: { - title: 'Attribution', + title: () => I18n.getMessage('attribution'), panel: { Element: Attribution, }, @@ -171,10 +174,29 @@ const TABS: SidebarItems = { }, children: {}, }, + [SIDEBAR_ITEMS_KEYS.PROTECTED_AUDIENCE]: { + title: 'Protected Audience', + panel: { + Element: ProtectedAudience, + }, + icon: { + Element: GroupsIcon, + props: { + className: 'fill-gray', + }, + }, + selectedIcon: { + Element: GroupsIcon, + props: { + className: 'fill-white', + }, + }, + children: {}, + }, }, }, [SIDEBAR_ITEMS_KEYS.ANTI_COVERT_TRACKING]: { - title: 'Tracking Protection', + title: () => I18n.getMessage('trackingProtection'), panel: { Element: AntiCovertTracking, }, @@ -186,7 +208,7 @@ const TABS: SidebarItems = { }, children: { [SIDEBAR_ITEMS_KEYS.BOUNCE_TRACKING]: { - title: 'Bounce Tracking', + title: () => I18n.getMessage('bounceTracking'), panel: { Element: BounceTracking, }, @@ -199,7 +221,7 @@ const TABS: SidebarItems = { children: {}, }, [SIDEBAR_ITEMS_KEYS.FINGERPRINTING]: { - title: 'Fingerprinting', + title: () => I18n.getMessage('fingerprinting'), panel: { Element: Fingerprinting, }, @@ -216,7 +238,7 @@ const TABS: SidebarItems = { }, }, [SIDEBAR_ITEMS_KEYS.FACILITATED_TESTING]: { - title: 'Facilitated Testing', + title: () => I18n.getMessage('facilitatedTesting'), panel: { Element: FacilitatedTesting, }, @@ -236,7 +258,7 @@ const TABS: SidebarItems = { children: {}, }, [SIDEBAR_ITEMS_KEYS.SETTINGS]: { - title: 'Settings', + title: () => I18n.getMessage('settings'), panel: { Element: Settings, }, diff --git a/packages/extension/src/view/devtools/tests/app.tsx b/packages/extension/src/view/devtools/tests/app.tsx index 064887abf..4dac22c34 100644 --- a/packages/extension/src/view/devtools/tests/app.tsx +++ b/packages/extension/src/view/devtools/tests/app.tsx @@ -20,8 +20,9 @@ import React, { act } from 'react'; import { render, screen } from '@testing-library/react'; import '@testing-library/jest-dom'; import SinonChrome from 'sinon-chrome'; -import { noop } from '@ps-analysis-tool/common'; -import { useTablePersistentSettingsStore } from '@ps-analysis-tool/design-system'; +import { noop } from '@google-psat/common'; +import { useTablePersistentSettingsStore } from '@google-psat/design-system'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. @@ -165,6 +166,13 @@ describe('App', () => { }), }); } as unknown as typeof fetch; + + I18n.initMessages({ + refreshPanel: { + message: 'Refresh Panel', + }, + }); + globalThis.chrome.i18n = null; }); it('Should show cookie table if frame is selected', async () => { @@ -225,7 +233,7 @@ describe('App', () => { render(); }); - expect(await screen.findByText('Refresh panel')).toBeInTheDocument(); + expect(await screen.findByText('Refresh Panel')).toBeInTheDocument(); }); afterAll(() => { diff --git a/packages/extension/src/view/popup/app.tsx b/packages/extension/src/view/popup/app.tsx index 63ff291eb..ea06d51eb 100644 --- a/packages/extension/src/view/popup/app.tsx +++ b/packages/extension/src/view/popup/app.tsx @@ -25,7 +25,8 @@ import { ToastMessage, ToggleSwitch, prepareCookieStatsComponents, -} from '@ps-analysis-tool/design-system'; +} from '@google-psat/design-system'; +import { I18n } from '@google-psat/i18n'; /** * Internal dependencies. @@ -65,26 +66,30 @@ const App: React.FC = () => { handleSettingsChange: actions.handleSettingsChange, })); - const cdpLabel = isUsingCDP ? 'Disable CDP' : 'Enable CDP'; + const cdpLabel = isUsingCDP + ? I18n.getMessage('disableCDP') + : I18n.getMessage('enableCDP'); if (onChromeUrl) { return ( -
        +
        -

        Not much to analyze here

        +

        + {I18n.getMessage('noMoreAnalysis')} +

        - Its emptier than a cookie jar after a midnight snack! + {I18n.getMessage('emptyCookieJar')}

        {settingsChanged && ( @@ -111,19 +116,22 @@ const App: React.FC = () => { allowedNumberOfTabs !== 'unlimited' ) { return ( -
        +
        -