diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 88be4d3b9..f09abc4b6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -50,6 +50,7 @@ jobs: # be deployed to ECS. docker build \ --build-arg NPM_TOKEN=${{ secrets.NPM_TOKEN }} \ + --build-arg SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} \ -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" diff --git a/.gitignore b/.gitignore index be46e7753..001a5043c 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,6 @@ cypress/videos cypress/screenshots cypress/reports cypress/downloads + +# Sentry Config File +.sentryclirc diff --git a/Dockerfile b/Dockerfile index 39125e7c3..f1ee294d6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,6 +3,7 @@ FROM node:16.19-alpine3.17 as base COPY package.json ./ COPY yarn.lock ./ +ARG SENTRY_AUTH_TOKEN ARG NPM_TOKEN RUN echo //registry.npmjs.org/:_authToken=${NPM_TOKEN} > .npmrc RUN ["yarn", "install", "--frozen-lockfile", "--ignore-scripts"] @@ -11,6 +12,7 @@ RUN rm -f .npmrc COPY . . RUN ["yarn", "build"] +RUN ["yarn", "sentry:sourcemaps"] FROM alpine:3.14 as web diff --git a/deployment/after-install.sh b/deployment/after-install.sh index 6944b9225..81d2ce021 100644 --- a/deployment/after-install.sh +++ b/deployment/after-install.sh @@ -44,6 +44,7 @@ services: PORT: 80 NODE_ENV: ${ENVIRONMENT} REACT_APP_ENVIRONMENT: ${ENVIRONMENT} + REACT_APP_VERSION: ${DOCKER_IMAGE} logging: driver: awslogs options: diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 5aaa34fd1..161b50c00 100755 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -6,7 +6,7 @@ rm -rf $CONFIG_FILE_PATH touch $CONFIG_FILE_PATH # Add assignment -echo "window._env_ = {" > $CONFIG_FILE_PATH +echo "window.react_env = {" > $CONFIG_FILE_PATH VARIABLES=$(printenv | grep REACT_APP) diff --git a/package.json b/package.json index 4ca597e91..5f4e9a2e9 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "@appquality/craft-blocks": "^0.1.27", "@datadog/browser-logs": "^3.4.1", "@reduxjs/toolkit": "^1.8.2", + "@sentry/react": "^7.83.0", "formik": "^2.2.6", "i18n-iso-countries": "^6.7.0", "i18next": "^20.2.2", @@ -57,7 +58,8 @@ "translate": "i18next-scanner", "generate-schema": ". ./.env.development; npx openapi-typescript $REACT_APP_API_URL/reference/ --output src/utils/schema.ts ", "generate-api": ". ./.env.development; API_URL=$REACT_APP_API_URL/reference/ npx @rtk-query/codegen-openapi src/services/tryberApi/config.ts", - "generate-fixtures": ". ./.env.development; API_URL=$REACT_APP_API_URL/reference/; npx @appquality/fixture-generator -u $API_URL && npx prettier --write 'cypress/fixtures/**/*.json'" + "generate-fixtures": ". ./.env.development; API_URL=$REACT_APP_API_URL/reference/; npx @appquality/fixture-generator -u $API_URL && npx prettier --write 'cypress/fixtures/**/*.json'", + "sentry:sourcemaps": "sentry-cli sourcemaps inject --org appquality-srl --project tryber-react ./build && sentry-cli sourcemaps upload --org appquality-srl --project tryber-react ./build" }, "eslintConfig": { "extends": [ @@ -94,6 +96,7 @@ "@craco/craco": "^6.4.2", "@redux-devtools/core": "^3.11.0", "@rtk-query/codegen-openapi": "^1.0.0-alpha.1", + "@sentry/cli": "^2.23.0", "@types/node": "^14.14.41", "@types/react": "17.0.9", "@types/react-dom": "17.0.9", diff --git a/public/index.html b/public/index.html index aa90eedfd..8c1b80b52 100644 --- a/public/index.html +++ b/public/index.html @@ -78,7 +78,7 @@ src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDy8e6_8uv8JnNZFRXng6HfbhnhvobWyac&libraries=places" > - + diff --git a/public/static/env-config.js b/public/static/env-config.js index b59c0a4aa..3f704a78a 100644 --- a/public/static/env-config.js +++ b/public/static/env-config.js @@ -1,3 +1,4 @@ -window._env_ = { +window.react_env = { REACT_APP_ENVIRONMENT: "local", + REACT_APP_VERSION: "local", }; diff --git a/src/Page.tsx b/src/Page.tsx index 68f00edfe..af5a0ad4f 100644 --- a/src/Page.tsx +++ b/src/Page.tsx @@ -1,11 +1,10 @@ import "./i18n"; import { datadogLogs } from "@datadog/browser-logs"; -import { Location } from "history"; +import { Location, createBrowserHistory } from "history"; import queryString from "query-string"; import { useEffect } from "react"; -import TagManager from "react-gtm-module"; import { useDispatch } from "react-redux"; -import { Redirect, Route, Switch, useLocation } from "react-router-dom"; +import { Redirect, Route, Router, Switch, useLocation } from "react-router-dom"; import GenericModal from "./features/GenericModal"; import SiteWideMessages from "./features/SiteWideMessages"; @@ -27,6 +26,12 @@ import { refreshUser } from "./redux/user/actions/refreshUser"; import BugForm from "./pages/BugForm"; import ThankYouPage from "./pages/ThankYou"; import VdpPage from "./pages/VDP"; +import * as Sentry from "@sentry/react"; +import SentryWrapper from "./features/SentryWrapper"; + +// Create Custom Sentry Route component +const SentryRoute = Sentry.withSentryRouting(Route); +const history = createBrowserHistory(); if (process.env.REACT_APP_DATADOG_CLIENT_TOKEN) { datadogLogs.init({ @@ -36,13 +41,6 @@ if (process.env.REACT_APP_DATADOG_CLIENT_TOKEN) { sampleRate: 100, }); } -if (process.env.REACT_APP_GTM_ID) { - const tagManagerArgs = { - gtmId: process.env.REACT_APP_GTM_ID, - }; - - TagManager.initialize(tagManagerArgs); -} const base = "/:locale(en|it|es)?"; function Page() { @@ -59,117 +57,132 @@ function Page() { }, []); return ( -
+ - - - - - + + + + + + - + - - ( - - )} - /> - ( - - )} - /> + + ( + + )} + /> + ( + + )} + /> - - - - - - + + + + + + - - - - ( - - )} - /> - ( - - )} - /> - ( - - )} - /> - ( - - )} - /> + + + + ( + + )} + /> + ( + + )} + /> + ( + + )} + /> + ( + + )} + /> - - - - - - {/* TODO Temporary route */} - - - } /> - -
+ + + + + + {/* TODO Temporary route */} + + + } + /> + + + ); } diff --git a/src/features/LoggedOnly.tsx b/src/features/LoggedOnly.tsx index a5e85427c..9915960d5 100644 --- a/src/features/LoggedOnly.tsx +++ b/src/features/LoggedOnly.tsx @@ -3,6 +3,7 @@ import { useGetUsersMeQuery } from "src/services/tryberApi"; import Loading from "./Loading"; import { LoginPage } from "./LoginPage"; import SiteHeader from "./SiteHeader"; +import * as Sentry from "@sentry/react"; const LoggedOnly = ({ children, @@ -11,7 +12,13 @@ const LoggedOnly = ({ children: React.ReactNode; showHeader: boolean; }) => { - const { error, isLoading } = useGetUsersMeQuery({}); + const { + data: user, + error, + isLoading, + } = useGetUsersMeQuery({ + fields: "id,email,username,wp_user_id,role", + }); if (isLoading) { return ; @@ -21,6 +28,14 @@ const LoggedOnly = ({ event: "ApiLoaded", }, }); + Sentry.setUser({ + id: user?.id ?? 0, + email: user?.email ?? "unknown", + username: user?.username ?? "unknown", + wp_user_id: user?.wp_user_id ?? 0, + role: user?.role ?? "unknown", + }); + if (error) { if ("status" in error && error.status === 403) { return ; diff --git a/src/features/NotLoggedOnly.tsx b/src/features/NotLoggedOnly.tsx index b23198a32..612bafae9 100644 --- a/src/features/NotLoggedOnly.tsx +++ b/src/features/NotLoggedOnly.tsx @@ -1,6 +1,7 @@ import TagManager from "react-gtm-module"; import { useHistory } from "react-router-dom"; +import * as Sentry from "@sentry/react"; import { useGetUsersMeQuery } from "src/services/tryberApi"; import { useAppSelector } from "src/store"; import Loading from "./Loading"; @@ -13,7 +14,13 @@ const NotLoggedOnly = ({ children: React.ReactNode; redirect?: { url: string; message?: string }; }) => { - const { data: user, error, isLoading } = useGetUsersMeQuery({}); + const { + data: user, + error, + isLoading, + } = useGetUsersMeQuery({ + fields: "id,email,username,wp_user_id,role", + }); const history = useHistory(); const isPublicUser = useAppSelector( (state) => state.publicUserPages.isPublic @@ -23,6 +30,13 @@ const NotLoggedOnly = ({ return ; } if (user && redirect) { + Sentry.setUser({ + id: user?.id ?? 0, + email: user?.email ?? "unknown", + username: user?.username ?? "unknown", + wp_user_id: user?.wp_user_id ?? 0, + role: user?.role ?? "unknown", + }); history.push(redirect.url); } TagManager.dataLayer({ diff --git a/src/features/PageTemplate.tsx b/src/features/PageTemplate.tsx index 41098dc42..186296cab 100644 --- a/src/features/PageTemplate.tsx +++ b/src/features/PageTemplate.tsx @@ -1,6 +1,5 @@ import { Container, PageTitle } from "@appquality/appquality-design-system"; import React, { FC, useEffect } from "react"; - import GoogleTagManager from "./GoogleTagManager"; import LoggedOnly from "./LoggedOnly"; import NotLoggedOnly from "./NotLoggedOnly"; diff --git a/src/features/SentryWrapper.tsx b/src/features/SentryWrapper.tsx new file mode 100644 index 000000000..1eff90479 --- /dev/null +++ b/src/features/SentryWrapper.tsx @@ -0,0 +1,44 @@ +import * as Sentry from "@sentry/react"; +import i18n from "src/i18n"; +import isStagingEnvironment from "./isStagingEnvironment"; +import { RouterHistory } from "@sentry/react/types/reactrouter"; + +const SentryWrapper = ({ + children, + history, +}: { + children: React.ReactNode; + history: RouterHistory; +}) => { + if (process.env.NODE_ENV !== "test") { + Sentry.init({ + dsn: "https://84fe7a6107da4c0058197b52ce74743d@o1087982.ingest.sentry.io/4506337899511808", + integrations: [ + new Sentry.BrowserTracing({ + routingInstrumentation: Sentry.reactRouterV5Instrumentation(history), + }), + ], + environment: react_env.REACT_APP_ENVIRONMENT, + // trace all staging and locale traces and 70% of production traces + tracesSampleRate: isStagingEnvironment() ? 1.0 : 0.7, + // Set `tracePropagationTargets` to control for which URLs distributed tracing should be enabled + tracePropagationTargets: [ + "localhost", + /^https:\/\/dev\.tryber\.me\/api/, + /^https:\/\/tryber\.me\/api/, + ], + release: react_env.REACT_APP_VERSION, + // Capture Replay for 10% of all sessions, + // plus for 100% of sessions with an error + // do not capture for staging and locale + replaysSessionSampleRate: isStagingEnvironment() ? 0.0 : 0.1, + replaysOnErrorSampleRate: isStagingEnvironment() ? 0.0 : 1.0, + }); + } + + Sentry.setTag("page_locale", i18n.language); + + return <>{children}; +}; + +export default SentryWrapper; diff --git a/src/features/isStagingEnvironment.tsx b/src/features/isStagingEnvironment.tsx index e335beca7..db6658a2c 100644 --- a/src/features/isStagingEnvironment.tsx +++ b/src/features/isStagingEnvironment.tsx @@ -1,5 +1,5 @@ const isStagingEnvironment = () => { - return _env_.REACT_APP_ENVIRONMENT !== "production"; + return react_env.REACT_APP_ENVIRONMENT !== "production"; }; export default isStagingEnvironment; diff --git a/src/types.d.ts b/src/types.d.ts index 184cd01a0..9b2cfafe1 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -32,7 +32,8 @@ declare global { type SelectOptionType = SelectType.Option; type OrderType = "DESC" | "ASC"; - var _env_: { + var react_env: { REACT_APP_ENVIRONMENT: string; + REACT_APP_VERSION: string; }; } diff --git a/yarn.lock b/yarn.lock index 774cc72f6..422b619ce 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2026,6 +2026,120 @@ swagger2openapi "^7.0.4" typescript "^5.0.0" +"@sentry-internal/tracing@7.83.0": + version "7.83.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.83.0.tgz#8f69d339569b020c495f8350a8ea527c369586e8" + integrity sha512-fY1ZyOiQaaUTuoq5rO+G4/5Ov3n8BnfNK7ck97yAGxy3w+E1CwhVZkXHEvTngNfdYV3ArxvlrtPRb9STFRqXvQ== + dependencies: + "@sentry/core" "7.83.0" + "@sentry/types" "7.83.0" + "@sentry/utils" "7.83.0" + +"@sentry/browser@7.83.0": + version "7.83.0" + resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.83.0.tgz#01e8ba0d3f4d4652e67c8b0d955d2f3903a19ab0" + integrity sha512-8v7QEaC/fVAHn8pi59ZlJznr7ZdOQIgtz8DAOJeJsC2vHTAxQ9nVkoMkJWjTp/qaDHUjSe5ob6eqaChuhi6t2g== + dependencies: + "@sentry-internal/tracing" "7.83.0" + "@sentry/core" "7.83.0" + "@sentry/replay" "7.83.0" + "@sentry/types" "7.83.0" + "@sentry/utils" "7.83.0" + +"@sentry/cli-darwin@2.23.0": + version "2.23.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-darwin/-/cli-darwin-2.23.0.tgz#1a4f149071a77f2e767a9cd7997b0b9257eae59f" + integrity sha512-tWuTxvb6P5pA0E+O1/7jKQ6AP45DOOW+BAd7mwBMHZ+5xG3nsvvrRS9hOIzBNPTeB2RyIEXgpQ2Mb6NdD21DBQ== + +"@sentry/cli-linux-arm64@2.23.0": + version "2.23.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.23.0.tgz#f3f154dc9ee68b4c3e6386ececb7c4b7dbe6cdbe" + integrity sha512-KsOckP+b0xAzrRuoP4eiqJ6ASD6SqIplL8BCHOAODQfvWn9rgNwsJWOgKlWwfrJnkJYkpWVYvYeyx0oeUx3N0g== + +"@sentry/cli-linux-arm@2.23.0": + version "2.23.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm/-/cli-linux-arm-2.23.0.tgz#93afbf73c21b69c0e7b605d7508ee8002f90bba0" + integrity sha512-1R8ngBDKtPw++Km6VnVTx76ndrBL9BuBBNpF9TUCGftK3ArdaifqoIx8cZ8aKu8sWXLAKO7lHzxL4BNPZvlDiw== + +"@sentry/cli-linux-i686@2.23.0": + version "2.23.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-i686/-/cli-linux-i686-2.23.0.tgz#4a2db236f94474aea127abcb73402c5aa5a48f89" + integrity sha512-KRqB98KstBkKh33ZqUq+q8O0U4c01aTWCNPpVrqAX7zikSk0AAJTG8eAtqwDSx949IkKUl8xa6PFLfz+Nb2EMQ== + +"@sentry/cli-linux-x64@2.23.0": + version "2.23.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-x64/-/cli-linux-x64-2.23.0.tgz#fd0a1ac2fe4841247dcf89e43f3df9a416719259" + integrity sha512-USHZ0zzg9qujGYAyRjLeUfLDZOMgNjCr82m0BSBMmlFs4oKwHmO6bSvdi9UzNNcpmkOavNAdUM4jnZWk11i46Q== + +"@sentry/cli-win32-i686@2.23.0": + version "2.23.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-win32-i686/-/cli-win32-i686-2.23.0.tgz#0201030d2128a2fba79e3fd7e644052151bb21ed" + integrity sha512-lS/B3pONDl18IEu/I//3vcMnosThobyXpqfAm4WYUtFTiw/wwDHgwGgaIjZWm5wMRkPFzYoRFpZfPlUrJd/4cQ== + +"@sentry/cli-win32-x64@2.23.0": + version "2.23.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-win32-x64/-/cli-win32-x64-2.23.0.tgz#648b99be78af130dbb0a53506d5c55bbe0d46472" + integrity sha512-7LP6wA3w93ViYKQR8tMN2i/SfpQzaXqM2SAHI3yfJ3bdREHOV3+/N0mNiWVRvgL0TKNQJS42v2IILLhiDxufHQ== + +"@sentry/cli@^2.23.0": + version "2.23.0" + resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-2.23.0.tgz#d8fa6a514aedfe316a8afc046435b96746e7099f" + integrity sha512-xFTv7YOaKWMCSPgN8A1jZpxJQhwdES89pqMTWjJOgjmkwFvziuaTM7O7kazps/cACDhJp2lK2j6AT6imhr4t9w== + dependencies: + https-proxy-agent "^5.0.0" + node-fetch "^2.6.7" + progress "^2.0.3" + proxy-from-env "^1.1.0" + which "^2.0.2" + optionalDependencies: + "@sentry/cli-darwin" "2.23.0" + "@sentry/cli-linux-arm" "2.23.0" + "@sentry/cli-linux-arm64" "2.23.0" + "@sentry/cli-linux-i686" "2.23.0" + "@sentry/cli-linux-x64" "2.23.0" + "@sentry/cli-win32-i686" "2.23.0" + "@sentry/cli-win32-x64" "2.23.0" + +"@sentry/core@7.83.0": + version "7.83.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.83.0.tgz#29bdd5aba40a6f25c01c68387b6a9aa13e27f20a" + integrity sha512-fglvpw8aWM6nWXzCjAVXIMTiTEAQ9G9b85IpDd/7L8fuwaFTPQAUSJXupF2PfbpQ3FUYbJt80dxshbERVJG8vQ== + dependencies: + "@sentry/types" "7.83.0" + "@sentry/utils" "7.83.0" + +"@sentry/react@^7.83.0": + version "7.83.0" + resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.83.0.tgz#e3d5e4480d613e8c5b2e927f6f72a7b91896f721" + integrity sha512-8GjKRXkZH+FkmO0LaGEVOrTC9g6Csn7VnTVIqtnfX2hVxbdHnqyjhHDgnCbmW7JRb0X6//QK4CuLCWu8uApLBw== + dependencies: + "@sentry/browser" "7.83.0" + "@sentry/types" "7.83.0" + "@sentry/utils" "7.83.0" + hoist-non-react-statics "^3.3.2" + +"@sentry/replay@7.83.0": + version "7.83.0" + resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.83.0.tgz#14fd39a638911f60b780d232d2056f0d19ed478e" + integrity sha512-B/rzmjmQ3ZWE68m4Z9rHIN3Fa/wkfVVTK+iSQtqErFflyMETMNwtWRNd6P9FhXnphEINZEbcn/UZF5w5xu/DfA== + dependencies: + "@sentry-internal/tracing" "7.83.0" + "@sentry/core" "7.83.0" + "@sentry/types" "7.83.0" + "@sentry/utils" "7.83.0" + +"@sentry/types@7.83.0": + version "7.83.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.83.0.tgz#117e45900603190c547e52bba35e1b3d539d47fb" + integrity sha512-Bd+zJcy8p1VgCfQqUprmUaw0QPWUV+GmCt6zJRHrHTb2pwLahXv6sHJvQ8F8Va6S7Keuy088U+kHzUFGQLMZMQ== + +"@sentry/utils@7.83.0": + version "7.83.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.83.0.tgz#ec0fc0a468ec35ab9623603f1ba218dd58233eda" + integrity sha512-7SrZtgAn3pHFBqSSvV/VL0CWTBQ7VenJjok4+WGWd6/FhP3fKrEEd9rjVTUb2Pzq9WLJJYzdvxAG8RlggG+H4g== + dependencies: + "@sentry/types" "7.83.0" + "@sideway/address@^4.1.3": version "4.1.4" resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.4.tgz#03dccebc6ea47fdc226f7d3d1ad512955d4783f0" @@ -9353,7 +9467,7 @@ node-fetch-h2@^2.3.0: dependencies: http2-client "^1.2.5" -node-fetch@^2.6.1, node-fetch@^2.6.12: +node-fetch@^2.6.1, node-fetch@^2.6.12, node-fetch@^2.6.7: version "2.7.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== @@ -10827,7 +10941,7 @@ process@^0.11.10: resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== -progress@^2.0.0: +progress@^2.0.0, progress@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== @@ -10894,6 +11008,11 @@ proxy-from-env@1.0.0: resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee" integrity sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A== +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"