diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
new file mode 100644
index 000000000..fa176b433
--- /dev/null
+++ b/.github/workflows/ci.yaml
@@ -0,0 +1,35 @@
+name: CI
+on:
+ workflow_dispatch:
+ push:
+
+jobs:
+ CI:
+ runs-on: ubuntu-latest
+ permissions:
+ id-token: write
+ contents: read
+ steps:
+ - uses: actions/checkout@v2
+ - uses: aws-actions/configure-aws-credentials@v1
+ with:
+ role-to-assume: ${{ secrets.GU_RIFF_RAFF_ROLE_ARN }}
+ aws-region: eu-west-1
+ - uses: actions/setup-java@v3
+ with:
+ java-version: "8"
+ distribution: "corretto"
+ - uses: actions/setup-node@v3
+ with:
+ node-version-file: '.nvmrc'
+ - name: Build Pluto lambda
+ run: |
+ ./scripts/pluto-ci.sh
+ - name: Build Media Atom Maker
+ run: |
+ ./scripts/app-ci.sh
+ - name: Compile Scala and upload artifacts to RiffRaff
+ run: |
+ ./scripts/scala-ci.sh
+
+
diff --git a/app/controllers/Api.scala b/app/controllers/Api.scala
index e0d7829e7..81219a901 100644
--- a/app/controllers/Api.scala
+++ b/app/controllers/Api.scala
@@ -45,15 +45,19 @@ class Api(
}
}
- def getMediaAtoms(search: Option[String], limit: Option[Int]) = APIAuthAction {
- val atoms = stores.atomListStore.getAtoms(search, limit)
+ def getMediaAtoms(search: Option[String], limit: Option[Int], shouldUseCreatedDateForSort: Boolean) = APIAuthAction {
+ val atoms = stores.atomListStore.getAtoms(search, limit, shouldUseCreatedDateForSort)
Ok(Json.toJson(atoms))
}
- def getMediaAtom(id: String) = APIAuthAction {
+ def getMediaAtom(id: String) = APIAuthAction {req =>
try {
+ val maybeCorsValue = req.headers.get("Origin").filter(_.endsWith("gutools.co.uk"))
val atom = getPreviewAtom(id)
- Ok(Json.toJson(MediaAtom.fromThrift(atom)))
+ Ok(Json.toJson(MediaAtom.fromThrift(atom))).withHeaders(
+ "Access-Control-Allow-Origin" -> maybeCorsValue.getOrElse(""),
+ "Access-Control-Allow-Credentials" -> maybeCorsValue.isDefined.toString
+ )
} catch {
commandExceptionAsResult
}
diff --git a/app/controllers/VideoUIApp.scala b/app/controllers/VideoUIApp.scala
index 221896cbc..6edf45a1a 100644
--- a/app/controllers/VideoUIApp.scala
+++ b/app/controllers/VideoUIApp.scala
@@ -1,6 +1,7 @@
package controllers
+import com.gu.editorial.permissions.client.{Permission, PermissionDenied, PermissionsUser}
import com.gu.media.MediaAtomMakerPermissionsProvider
import com.gu.media.logging.Logging
import com.gu.media.youtube.YouTubeAccess
@@ -66,6 +67,7 @@ class VideoUIApp(val authActions: HMACAuthActions, conf: Configuration, awsConfi
title = "Media Atom Maker",
jsLocation,
presenceJsLocation = clientConfig.presence.map(_.jsLocation),
+ pinboardJsLocation = if(permissions.pinboard) awsConfig.pinboardLoaderUrl else None,
Json.toJson(clientConfig).toString(),
isHotReloading,
CSRF.getToken.value
diff --git a/app/data/AtomListStore.scala b/app/data/AtomListStore.scala
index 2a2f49256..b717bcfde 100644
--- a/app/data/AtomListStore.scala
+++ b/app/data/AtomListStore.scala
@@ -11,18 +11,18 @@ import model.{MediaAtomList, MediaAtomSummary}
import play.api.libs.json.{JsArray, JsValue}
trait AtomListStore {
- def getAtoms(search: Option[String], limit: Option[Int]): MediaAtomList
+ def getAtoms(search: Option[String], limit: Option[Int], shouldUseCreatedDateForSort: Boolean): MediaAtomList
}
class CapiBackedAtomListStore(capi: CapiAccess) extends AtomListStore {
- override def getAtoms(search: Option[String], limit: Option[Int]): MediaAtomList = {
+ override def getAtoms(search: Option[String], limit: Option[Int], shouldUseCreatedDateForSort: Boolean): MediaAtomList = {
// CAPI max page size is 200
val cappedLimit: Option[Int] = limit.map(Math.min(200, _))
val base: Map[String, String] = Map(
"types" -> "media",
"order-by" -> "newest"
- )
+ ) ++ (if(shouldUseCreatedDateForSort) Map("order-date" -> "first-publication") else Map.empty)
val baseWithSearch = search match {
case Some(q) => base ++ Map(
@@ -76,7 +76,7 @@ class CapiBackedAtomListStore(capi: CapiAccess) extends AtomListStore {
}
class DynamoBackedAtomListStore(store: PreviewDynamoDataStore) extends AtomListStore {
- override def getAtoms(search: Option[String], limit: Option[Int]): MediaAtomList = {
+ override def getAtoms(search: Option[String], limit: Option[Int], shouldUseCreatedDateForSort: Boolean): MediaAtomList = {
// We must filter the entire list of atoms rather than use Dynamo limit to ensure stable iteration order.
// Without it, the front page will shuffle around when clicking the Load More button.
store.listAtoms match {
@@ -84,12 +84,16 @@ class DynamoBackedAtomListStore(store: PreviewDynamoDataStore) extends AtomListS
AtomDataStoreError(err.msg)
case Right(atoms) =>
- def created(atom: MediaAtom) = atom.contentChangeDetails.created.map(_.date.getMillis)
+ def sortField(atom: MediaAtom) =
+ if(shouldUseCreatedDateForSort)
+ atom.contentChangeDetails.created
+ else
+ atom.contentChangeDetails.lastModified
val mediaAtoms = atoms
.map(MediaAtom.fromThrift)
.toList
- .sortBy(created)
+ .sortBy(sortField(_).map(_.date.getMillis))
.reverse // newest atoms first
val filteredAtoms = search match {
diff --git a/app/model/commands/PublishAtomCommand.scala b/app/model/commands/PublishAtomCommand.scala
index b3e090007..3bc36db3c 100644
--- a/app/model/commands/PublishAtomCommand.scala
+++ b/app/model/commands/PublishAtomCommand.scala
@@ -134,16 +134,15 @@ case class PublishAtomCommand(
contentChangeDetails = atom.contentChangeDetails.copy(
published = changeRecord,
lastModified = changeRecord,
- revision = atom.contentChangeDetails.revision + 1,
scheduledLaunch = None,
embargo = None
)
)
AuditMessage(id, "Publish", getUsername(user)).logMessage()
- UpdateAtomCommand(id, updatedAtom, stores, user, awsConfig).process()
+ val updatedAtomToPublish = UpdateAtomCommand(id, updatedAtom, stores, user, awsConfig).process()
- val publishedAtom = publishAtomToLive(updatedAtom)
+ val publishedAtom = publishAtomToLive(updatedAtomToPublish)
updateInactiveAssets(publishedAtom)
publishedAtom
}
diff --git a/app/util/AWS.scala b/app/util/AWS.scala
index 747a49360..1d9f36b41 100644
--- a/app/util/AWS.scala
+++ b/app/util/AWS.scala
@@ -29,6 +29,7 @@ class AWSConfig(override val config: Config, override val credentials: AwsCreden
.withCredentials(credentials.instance)
.build()
+ lazy val pinboardLoaderUrl = getString("panda.domain").map(domain => s"https://pinboard.$domain/pinboard.loader.js")
lazy val composerUrl = getMandatoryString("flexible.url")
lazy val workflowUrl = getMandatoryString("workflow.url")
lazy val viewerUrl = getMandatoryString("viewer.url")
diff --git a/app/util/ThumbnailGenerator.scala b/app/util/ThumbnailGenerator.scala
index dcb40dec5..7b3b4f56b 100644
--- a/app/util/ThumbnailGenerator.scala
+++ b/app/util/ThumbnailGenerator.scala
@@ -1,7 +1,7 @@
package util
import java.awt.RenderingHints
-import java.awt.image.BufferedImage
+import java.awt.image.{BufferedImage, ColorConvertOp}
import java.io._
import java.net.URL
import com.google.api.client.http.InputStreamContent
@@ -24,8 +24,11 @@ case class ThumbnailGenerator(logoFile: File) extends Logging {
.maxBy(_.size.get)
}
- private def imageAssetToBufferedImage(imageAsset: ImageAsset): BufferedImage =
- ImageIO.read(new URL(imageAsset.file))
+ private def imageAssetToBufferedImage(imageAsset: ImageAsset): BufferedImage = {
+ val image = ImageIO.read(new URL(imageAsset.file))
+ val rgbImage = new BufferedImage(image.getWidth, image.getHeight, BufferedImage.TYPE_3BYTE_BGR)
+ new ColorConvertOp(null).filter(image, rgbImage)
+ }
private def overlayImages(bgImage: BufferedImage, bgImageMimeType: String, atomId: String): ByteArrayInputStream = {
val logoWidth: Double = List(bgImage.getWidth() / 3.0, logo.getWidth()).min
diff --git a/app/views/VideoUIApp/app.scala.html b/app/views/VideoUIApp/app.scala.html
index 0289a6a05..7e7ecfca8 100644
--- a/app/views/VideoUIApp/app.scala.html
+++ b/app/views/VideoUIApp/app.scala.html
@@ -2,6 +2,7 @@
title: String,
jsFileLocation: String,
presenceJsLocation: Option[String],
+ pinboardJsLocation: Option[String],
clientConfigJson: String,
isHotReloading: Boolean,
csrf: String
@@ -22,5 +23,8 @@
Loading...
+ @pinboardJsLocation.map { pinboardJs =>
+
+ }
}
diff --git a/build.sbt b/build.sbt
index 2614483a9..5ceb81b5e 100644
--- a/build.sbt
+++ b/build.sbt
@@ -253,8 +253,6 @@ lazy val root = (project in file("root"))
(scheduler / Universal / packageBin).value -> s"${(scheduler / name).value}/${(scheduler / Universal / packageBin).value.getName}",
(app / baseDirectory).value / "pluto-message-ingestion/target/pluto-message-ingestion.zip" -> "pluto-message-ingestion/pluto-message-ingestion.zip",
(app / baseDirectory).value / "conf/riff-raff.yaml" -> "riff-raff.yaml",
- (app / baseDirectory).value / "fluentbit/td-agent-bit.conf" -> "media-atom-maker/fluentbit/td-agent-bit.conf",
- (app / baseDirectory).value / "fluentbit/parsers.conf" -> "media-atom-maker/fluentbit/parsers.conf",
(uploader / Compile / resourceManaged).value / "media-atom-pipeline.yaml" -> "media-atom-pipeline-cloudformation/media-atom-pipeline.yaml"
)
)
diff --git a/cloudformation/media-atom-maker-dev.yml b/cloudformation/media-atom-maker-dev.yml
index d76b7b273..ee3a728a7 100644
--- a/cloudformation/media-atom-maker-dev.yml
+++ b/cloudformation/media-atom-maker-dev.yml
@@ -96,7 +96,7 @@ Resources:
Type: "AWS::IAM::AccessKey"
Properties:
UserName: {"Ref": "MediaAtomUser"}
- Serial: 3
+ Serial: 4
MediaAtomsDynamoTable:
Type: "AWS::DynamoDB::Table"
Properties:
diff --git a/common/src/main/scala/com/gu/media/Permissions.scala b/common/src/main/scala/com/gu/media/Permissions.scala
index 6b8b398e1..fea103d94 100644
--- a/common/src/main/scala/com/gu/media/Permissions.scala
+++ b/common/src/main/scala/com/gu/media/Permissions.scala
@@ -7,7 +7,12 @@ import play.api.libs.json.Format
import com.gu.pandomainauth.model.{User => PandaUser}
import scala.concurrent.Future
-case class Permissions(deleteAtom: Boolean, addSelfHostedAsset: Boolean, setVideosOnAllChannelsPublic: Boolean)
+case class Permissions(
+ deleteAtom: Boolean,
+ addSelfHostedAsset: Boolean,
+ setVideosOnAllChannelsPublic: Boolean,
+ pinboard: Boolean
+)
object Permissions {
implicit val format: Format[Permissions] = Jsonx.formatCaseClass[Permissions]
@@ -15,17 +20,15 @@ object Permissions {
val deleteAtom = Permission("delete_atom", app, defaultVal = PermissionDenied)
val addSelfHostedAsset = Permission("add_self_hosted_asset", app, defaultVal = PermissionDenied)
val setVideosOnAllChannelsPublic = Permission("set_videos_on_all_channels_public", app, defaultVal = PermissionDenied)
+ val pinboard = Permission("pinboard", "pinboard", defaultVal = PermissionDenied)
}
class MediaAtomMakerPermissionsProvider(stage: String, credsProvider: AWSCredentialsProvider) extends PermissionsProvider {
import Permissions._
- val all = Seq(deleteAtom, addSelfHostedAsset, setVideosOnAllChannelsPublic)
- val none = Permissions(deleteAtom = false, addSelfHostedAsset = false, setVideosOnAllChannelsPublic = false )
-
implicit def config = PermissionsConfig(
app = "media-atom-maker",
- all = Seq(deleteAtom, addSelfHostedAsset, setVideosOnAllChannelsPublic),
+ all = Seq(deleteAtom, addSelfHostedAsset, setVideosOnAllChannelsPublic, pinboard),
s3BucketPrefix = if(stage == "PROD") "PROD" else "CODE",
awsCredentials = credsProvider
)
@@ -34,19 +37,13 @@ class MediaAtomMakerPermissionsProvider(stage: String, credsProvider: AWSCredent
deleteAtom <- hasPermission(deleteAtom, user)
selfHostedMediaAtom <- hasPermission(addSelfHostedAsset, user)
publicStatusPermissions <- hasPermission(setVideosOnAllChannelsPublic, user)
- } yield Permissions(deleteAtom, selfHostedMediaAtom, publicStatusPermissions)
-
-
- def getUploadPermissions(user: PandaUser): Future[Permissions] = for {
- selfHostedMediaAtom <- hasPermission(addSelfHostedAsset, user)
- } yield {
- Permissions(deleteAtom = false, selfHostedMediaAtom, setVideosOnAllChannelsPublic = false)
- }
+ pinboard <- hasPermission(pinboard, user)
+ } yield Permissions(deleteAtom, selfHostedMediaAtom, publicStatusPermissions, pinboard)
def getStatusPermissions(user: PandaUser): Future[Permissions] = for {
publicStatus <- hasPermission(setVideosOnAllChannelsPublic, user)
} yield {
- Permissions(deleteAtom = false, addSelfHostedAsset = false, publicStatus)
+ Permissions(deleteAtom = false, addSelfHostedAsset = false, publicStatus, pinboard = false)
}
private def hasPermission(permission: Permission, user: PandaUser): Future[Boolean] = {
diff --git a/conf/riff-raff.yaml b/conf/riff-raff.yaml
index 4f0e38901..e48470e17 100644
--- a/conf/riff-raff.yaml
+++ b/conf/riff-raff.yaml
@@ -12,7 +12,7 @@ deployments:
app: media-atom-maker
parameters:
amiTags:
- Recipe: editorial-tools-focal-java8-ARM
+ Recipe: editorial-tools-focal-java8-ARM-WITH-cdk-base
AmigoStage: PROD
media-atom-maker:
type: autoscaling
diff --git a/conf/routes b/conf/routes
index 14f90a70e..1b81373f0 100644
--- a/conf/routes
+++ b/conf/routes
@@ -1,5 +1,5 @@
# optional limit
-GET /api/atoms controllers.Api.getMediaAtoms(search: Option[String], limit: Option[Int])
+GET /api/atoms controllers.Api.getMediaAtoms(search: Option[String], limit: Option[Int], shouldUseCreatedDateForSort: Boolean?=false)
POST /api/atoms controllers.Api.createMediaAtom
GET /api/atoms/:id controllers.Api.getMediaAtom(id)
diff --git a/docs/01-dev-setup.md b/docs/01-dev-setup.md
index b4cd6cba8..684950cdd 100644
--- a/docs/01-dev-setup.md
+++ b/docs/01-dev-setup.md
@@ -5,7 +5,7 @@ Ensure you have the following installed:
- awscli
- Java 8
- nginx
-- node v10+
+- node v14.18.1
- npm
- yarn
- nvm
@@ -13,12 +13,12 @@ Ensure you have the following installed:
You'll also need Janus credentials to the `media-service` account.
-## Local setup
+## Local setup
We use a shared DEV stack, with a shared config. Fetch it by running:
```bash
-./scripts/fetch-dev-config.sh
+sudo ./scripts/fetch-dev-config.sh
```
There is a chance that the IAM key used for local development (media-atom-maker-DEV) has been disabled if it has not been rotated in a while. If this is the case, and you need the key, you will need to rotate the IAM key. To do this, increment the [Serial property](https://github.com/guardian/media-atom-maker/blob/ba9f87b4b3d3f3446affabc4410ea598ae130e36/cloudformation/media-atom-maker-dev.yml#L99) in the CloudFormation template, and update the stack with the new template. This will generate the new IAM key (found in the CloudFormation `Outputs` tab, under `AwsId` and `AwsSecret`), which you should update in the dev config file in S3 (under the settings `upload.accessKey` and `upload.secretKey`).
diff --git a/fluentbit/parsers.conf b/fluentbit/parsers.conf
deleted file mode 100644
index 700e23ec1..000000000
--- a/fluentbit/parsers.conf
+++ /dev/null
@@ -1,4 +0,0 @@
-# https://docs.fluentbit.io/manual/pipeline/filters/parser
-[PARSER]
- Name systemd_json
- Format json
diff --git a/fluentbit/td-agent-bit.conf b/fluentbit/td-agent-bit.conf
deleted file mode 100644
index da8287d33..000000000
--- a/fluentbit/td-agent-bit.conf
+++ /dev/null
@@ -1,66 +0,0 @@
-[SERVICE]
- Parsers_File parsers.conf
-
-# https://docs.fluentbit.io/manual/pipeline/inputs/systemd
-[INPUT]
- Name systemd
- Systemd_Filter _SYSTEMD_UNIT=APP_NAME.service
- Strip_Underscores true
-
-# https://docs.fluentbit.io/manual/pipeline/filters/record-modifier
-# Drop all systemd fields - we only want the message
-[FILTER]
- Name record_modifier
- Match *
- Allowlist_key MESSAGE
-
-# https://docs.fluentbit.io/manual/pipeline/filters/modify
-# Lowercase MESSAGE field for consistency
-[FILTER]
- Name modify
- Match *
- Rename MESSAGE message
-
-# https://docs.fluentbit.io/manual/pipeline/filters/parser
-# Attempt to parse the message field, in case the app is logging structured data
-[FILTER]
- Name parser
- Match *
- Key_Name message
- Parser systemd_json
-
-# https://docs.fluentbit.io/manual/pipeline/filters/multiline-stacktrace
-# Attempt to group up log lines which are split over multiple lines
-[FILTER]
- name multiline
- match *
- multiline.parser java
- multiline.key_content message
-
-# https://docs.fluentbit.io/manual/pipeline/filters/aws-metadata
-# Add useful AWS metadata
-[FILTER]
- Name aws
- Match *
- az true
- ec2_instance_id true
- ami_id true
-
-# https://docs.fluentbit.io/manual/pipeline/filters/modify
-# Add app identity fields
-[FILTER]
- Name modify
- Match *
- # To be replaced with the actual value by the CFN
- Add app APP_NAME
- Add stage STACK_STAGE
- Add stack STACK_NAME
- Rename ec2_instance_id instanceId
-
-# https://docs.fluentbit.io/manual/pipeline/outputs/kinesis
-[OUTPUT]
- Name kinesis_streams
- Match *
- region eu-west-1
- # To be replaced with the actual value by the CFN
- stream STACK_STREAM
diff --git a/public/video-ui/src/actions/SearchActions/updateShouldUseCreatedDateForSort.js b/public/video-ui/src/actions/SearchActions/updateShouldUseCreatedDateForSort.js
new file mode 100644
index 000000000..34a6eb3d1
--- /dev/null
+++ b/public/video-ui/src/actions/SearchActions/updateShouldUseCreatedDateForSort.js
@@ -0,0 +1,7 @@
+export function updateShouldUseCreatedDateForSort(shouldUseCreatedDateForSort) {
+ return {
+ type: 'UPDATE_SHOULD_USE_CREATED_DATE_FOR_SORT',
+ shouldUseCreatedDateForSort,
+ receivedAt: Date.now()
+ };
+}
diff --git a/public/video-ui/src/actions/VideoActions/getVideos.js b/public/video-ui/src/actions/VideoActions/getVideos.js
index 0e4b44a57..47e322ce3 100644
--- a/public/video-ui/src/actions/VideoActions/getVideos.js
+++ b/public/video-ui/src/actions/VideoActions/getVideos.js
@@ -1,10 +1,11 @@
import VideosApi from '../../services/VideosApi';
-function requestVideos(search, limit) {
+function requestVideos(search, limit, shouldUseCreatedDateForSort) {
return {
type: 'VIDEOS_GET_REQUEST',
- search: search,
- limit: limit,
+ search,
+ limit,
+ shouldUseCreatedDateForSort,
receivedAt: Date.now()
};
}
@@ -27,10 +28,10 @@ function errorReceivingVideos(error) {
};
}
-export function getVideos(search, limit) {
+export function getVideos(search, limit, shouldUseCreatedDateForSort) {
return dispatch => {
- dispatch(requestVideos(search, limit));
- return VideosApi.fetchVideos(search, limit)
+ dispatch(requestVideos(search, limit, shouldUseCreatedDateForSort));
+ return VideosApi.fetchVideos(search, limit, shouldUseCreatedDateForSort)
.then(res => {
dispatch(receiveVideos(res.total, res.atoms));
})
diff --git a/public/video-ui/src/components/FormFields/RichTextField.tsx b/public/video-ui/src/components/FormFields/RichTextField.tsx
index e799f52f2..385e7f199 100644
--- a/public/video-ui/src/components/FormFields/RichTextField.tsx
+++ b/public/video-ui/src/components/FormFields/RichTextField.tsx
@@ -12,8 +12,7 @@ type EditorProps = {
fieldValue: string;
derivedFrom: string;
onUpdateField: (string: string) => any;
- maxLength: number;
- maxCharLength: number;
+ maxWordLength: number;
editable: boolean;
fieldLocation: string;
fieldName: string;
@@ -46,7 +45,7 @@ export default class RichTextField extends React.Component {
- if (!isTooLong(value, this.props.maxLength, this.props.maxCharLength)) {
+ if (!isTooLong(value, this.props.maxWordLength)) {
this.setState({
isTooLong: false
});
diff --git a/public/video-ui/src/components/FormFields/richtext/utils/richTextHelpers.ts b/public/video-ui/src/components/FormFields/richtext/utils/richTextHelpers.ts
index fcffe44ba..e010efcb6 100644
--- a/public/video-ui/src/components/FormFields/richtext/utils/richTextHelpers.ts
+++ b/public/video-ui/src/components/FormFields/richtext/utils/richTextHelpers.ts
@@ -21,13 +21,12 @@ export const getWords = (text: string): string[] => {
.filter(_ => _.length !== 0);
};
-export const isTooLong = (value: string, maxLength: number, maxCharLength: number): boolean => {
+export const isTooLong = (value: string, maxWordLength: number): boolean => {
const wordLength = getWords(value).reduce((length, word) => {
length += word.length;
return length;
}, 0);
return (
- wordLength > maxLength ||
- value.length > maxCharLength
+ wordLength > maxWordLength
);
};
diff --git a/public/video-ui/src/components/Header.js b/public/video-ui/src/components/Header.js
index 4de20c586..4c9c000d4 100644
--- a/public/video-ui/src/components/Header.js
+++ b/public/video-ui/src/components/Header.js
@@ -1,14 +1,15 @@
import React from 'react';
-import { Link } from 'react-router';
+import {Link} from 'react-router';
import VideoSearch from './VideoSearch/VideoSearch';
import VideoPublishBar from './VideoPublishBar/VideoPublishBar';
import VideoPublishState from './VideoPublishState/VideoPublishState';
import AdvancedActions from './Videos/AdvancedActions';
import ComposerPageCreate from './Videos/ComposerPageCreate';
import Icon from './Icon';
-import { Presence } from './Presence';
-import { canonicalVideoPageExists } from '../util/canonicalVideoPageExists';
+import {Presence} from './Presence';
+import {canonicalVideoPageExists} from '../util/canonicalVideoPageExists';
import VideoUtils from '../util/video';
+import {QUERY_PARAM_shouldUseCreatedDateForSort} from "../constants/queryParams";
export default class Header extends React.Component {
state = { presence: null };
@@ -60,6 +61,37 @@ export default class Header extends React.Component {
);
}
+ renderSortBy() {
+ return (
+
+ Sort by:
+
+
+ );
+ }
+
renderHeaderBack() {
return (
@@ -170,6 +202,8 @@ export default class Header extends React.Component {
+ {this.renderSortBy()}
+
{this.renderCreateVideo()}
{this.renderHelpLink()}
@@ -188,7 +222,6 @@ export default class Header extends React.Component {
className="flex-grow"
video={this.props.video}
publishedVideo={this.props.publishedVideo}
- editableFields={this.props.editableFields}
saveState={this.props.saveState}
videoEditOpen={this.props.videoEditOpen}
updateVideoPage={this.props.updateVideoPage}
diff --git a/public/video-ui/src/components/ManagedForm/ManagedField.js b/public/video-ui/src/components/ManagedForm/ManagedField.js
index 3a30cfcb5..6be941a53 100644
--- a/public/video-ui/src/components/ManagedForm/ManagedField.js
+++ b/public/video-ui/src/components/ManagedForm/ManagedField.js
@@ -60,7 +60,8 @@ export class ManagedField extends React.Component {
this.props.isRequired,
this.props.isDesired,
this.props.customValidation,
- composerValidation
+ composerValidation,
+ this.props.maxLength,
);
if (this.props.updateFormErrors) {
@@ -159,7 +160,7 @@ export class ManagedField extends React.Component {
hasWarning: this.hasWarning,
displayPlaceholder: this.displayPlaceholder,
derivedFrom: this.props.derivedFrom,
- maxCharLength: this.props.maxCharLength,
+ maxWordLength: this.props.maxWordLength,
tagType: this.props.tagType,
inputPlaceholder: this.props.inputPlaceholder,
tooltip: this.props.tooltip,
diff --git a/public/video-ui/src/components/Presence.js b/public/video-ui/src/components/Presence.js
index 5d8d0aabe..c8bc55eec 100644
--- a/public/video-ui/src/components/Presence.js
+++ b/public/video-ui/src/components/Presence.js
@@ -27,7 +27,7 @@ export class Presence extends React.Component {
);
}
- if (current) {
+ if (current && window.presenceClient) {
this.startPresence(current, this.props.config);
}
}
diff --git a/public/video-ui/src/components/ReactApp.js b/public/video-ui/src/components/ReactApp.js
index 599eb05be..aaad033bc 100644
--- a/public/video-ui/src/components/ReactApp.js
+++ b/public/video-ui/src/components/ReactApp.js
@@ -32,28 +32,15 @@ class ReactApp extends React.Component {
}
}
- updateSearchTerm = searchTerm => {
- this.props.appActions.updateSearchTerm(searchTerm);
- };
-
- getEditableFields = () => {
- const allFields = this.props.checkedFormFields;
-
- const editableFormFields = Object.keys(
- allFields
- ).reduce((fields, formName) => {
- return fields.concat(Object.keys(this.props.checkedFormFields[formName]));
- }, []);
- return editableFormFields;
- };
-
render() {
const showPublishedState = this.props.params.id;
return (
diff --git a/public/video-ui/src/components/VideoImages/VideoImages.js b/public/video-ui/src/components/VideoImages/VideoImages.js
index 1ee493b55..fa899ff3d 100644
--- a/public/video-ui/src/components/VideoImages/VideoImages.js
+++ b/public/video-ui/src/components/VideoImages/VideoImages.js
@@ -23,12 +23,22 @@ export default class VideoImages extends React.Component {
getGridUrl(cropType) {
const posterImage = this.props.video.posterImage;
+ const queryParam = cropType == "verticalVideo" ?
+ `cropType=${cropType}&customRatio=${cropType},9,16` :
+ `cropType=${cropType}`;
+
if (posterImage.assets.length > 0) {
const imageGridId = getGridMediaId(posterImage);
- return `${this.props.gridDomain}/images/${imageGridId}?cropType=${cropType}`;
+
+ return `${this.props.gridDomain}/images/${imageGridId}?${queryParam}`;
}
- return `${this.props.gridDomain}?cropType=${cropType}`;
+ return `${this.props.gridDomain}?${queryParam}`;
+ }
+
+ hasVerticalVideoTag() {
+ const tags = this.props.video.keywords || [];
+ return tags.includes('tone/vertical-video');
}
render() {
@@ -45,7 +55,7 @@ export default class VideoImages extends React.Component {
{
+ const embedUrl = getStore().getState().config.youtubeEmbedUrl;
+ return `${embedUrl}${id}?showinfo=0&rel=0`;
+}
+export function YouTubeEmbed({ id, className }) {
return (
diff --git a/public/video-ui/src/constants/queryParams.js b/public/video-ui/src/constants/queryParams.js
new file mode 100644
index 000000000..75c23c6d2
--- /dev/null
+++ b/public/video-ui/src/constants/queryParams.js
@@ -0,0 +1 @@
+export const QUERY_PARAM_shouldUseCreatedDateForSort = "shouldUseCreatedDateForSort";
diff --git a/public/video-ui/src/pages/Search/index.tsx b/public/video-ui/src/pages/Search/index.tsx
index f3d939312..04df8fbcc 100644
--- a/public/video-ui/src/pages/Search/index.tsx
+++ b/public/video-ui/src/pages/Search/index.tsx
@@ -13,13 +13,14 @@ type Video = {
}
type VideosProps = {
- videos: Video[],
- total: number,
+ videos: Video[],
+ total: number,
videoActions: {
- getVideos: (searchTerm: string, limit: number) => null;
+ getVideos: (searchTerm: string, limit: number, shouldUseCreatedDateForSort: boolean) => null;
},
searchTerm: string,
- limit: number
+ limit: number,
+ shouldUseCreatedDateForSort: boolean
}
type PresenceConfig = {
@@ -70,7 +71,7 @@ const MoreLink = ({ onClick }: {onClick: () => void} ) => {
)
}
-const Videos = ({videos, total, videoActions, searchTerm, limit}: VideosProps) => {
+const Videos = ({videos, total, videoActions, searchTerm, limit, shouldUseCreatedDateForSort}: VideosProps) => {
const [mediaIds, setMediaIds] = useState
([]);
const [videoPresences, setVideoPresences] = useState([]);
const [client, setClient] = useState(null);
@@ -80,7 +81,8 @@ const Videos = ({videos, total, videoActions, searchTerm, limit}: VideosProps) =
const showMore = () => {
videoActions.getVideos(
searchTerm,
- limit + frontPageSize
+ limit + frontPageSize,
+ shouldUseCreatedDateForSort
);
};
@@ -97,7 +99,7 @@ const Videos = ({videos, total, videoActions, searchTerm, limit}: VideosProps) =
email
}) as PresenceClient
presenceClient.startConnection();
- presenceClient.on('visitor-list-subscribe', visitorData => {
+ presenceClient.on('visitor-list-subscribe', visitorData => {
if (visitorData.data.subscribedTo){
const initialState = visitorData.data.subscribedTo
.map(subscribedTo => {return {mediaId: subscribedTo.subscriptionId, presences: subscribedTo.currentState} as VideoPresences})
@@ -107,7 +109,7 @@ const Videos = ({videos, total, videoActions, searchTerm, limit}: VideosProps) =
})
presenceClient.on('visitor-list-updated', data => {
if (data.subscriptionId && data.currentState){
- // We dump the data to a queue (which is picked up by a useEffect rather than directly modifying videoPresences
+ // We dump the data to a queue (which is picked up by a useEffect rather than directly modifying videoPresences
// so we don't need to depend on videoPresences, which led to some cyclicality
setPresencesQueue(data);
}
@@ -120,14 +122,14 @@ const Videos = ({videos, total, videoActions, searchTerm, limit}: VideosProps) =
}
useEffect(() => {
- videoActions.getVideos(searchTerm, limit);
+ videoActions.getVideos(searchTerm, limit, shouldUseCreatedDateForSort);
const config = getStore().getState().config;
const presenceConfig = config.presence;
if (presenceConfig){
startPresence(presenceConfig);
}
}, [])
-
+
useEffect(() => {
const newMediaIds = videos.map(video => `media-${video.id}`)
setMediaIds(newMediaIds)
@@ -140,7 +142,7 @@ const Videos = ({videos, total, videoActions, searchTerm, limit}: VideosProps) =
client.on('connection.open', () => {
client.subscribe(mediaIds);
});
- }
+ }
}, [mediaIds, client]);
useEffect(() => {
@@ -153,10 +155,14 @@ const Videos = ({videos, total, videoActions, searchTerm, limit}: VideosProps) =
useEffect(() => {
if (searchTerm !== prevSearch) {
setPrevSearch(searchTerm)
- videoActions.getVideos(searchTerm, limit);
+ videoActions.getVideos(searchTerm, limit, shouldUseCreatedDateForSort);
}
}, [searchTerm, prevSearch])
+ useEffect(() => {
+ videoActions.getVideos(searchTerm, limit, shouldUseCreatedDateForSort);
+ }, [shouldUseCreatedDateForSort])
+
return (
@@ -170,7 +176,7 @@ const Videos = ({videos, total, videoActions, searchTerm, limit}: VideosProps) =
{videos.length === total ? null : }
)
-
+
}
//REDUX CONNECTIONS
@@ -178,12 +184,13 @@ import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as getVideos from '../../actions/VideoActions/getVideos';
-function mapStateToProps(state: {videos: {entries: number, total: number, limit: number}, searchTerm: string}) {
+function mapStateToProps(state: {videos: {entries: number, total: number, limit: number}, searchTerm: string, shouldUseCreatedDateForSort: boolean}) {
return {
videos: state.videos.entries,
total: state.videos.total,
limit: state.videos.limit,
- searchTerm: state.searchTerm
+ searchTerm: state.searchTerm,
+ shouldUseCreatedDateForSort: state.shouldUseCreatedDateForSort
};
}
diff --git a/public/video-ui/src/pages/Video/index.js b/public/video-ui/src/pages/Video/index.js
index 9374a5804..39ecca728 100644
--- a/public/video-ui/src/pages/Video/index.js
+++ b/public/video-ui/src/pages/Video/index.js
@@ -105,7 +105,7 @@ class VideoDisplay extends React.Component {
How are these images used?
-
+
Guardian Video Thumbnail Image |
@@ -142,6 +142,12 @@ class VideoDisplay extends React.Component {
Video Preview
{youtubeAsset ? ` (${youtubeAsset.id})` : ``}
+
+
{
+function getUsages({ id, stage }: {id: string, stage: Stage}): Promise {
return pandaReqwest({
url: `${ContentApi.getUrl(stage)}/atom/media/${id}/usage?page-size=100`
}).then(res => {
@@ -102,11 +123,14 @@ function splitUsages({ usages }: {usages: CapiContent[]}) {
}
export default {
- fetchVideos: (search: string, limit: number) => {
+ fetchVideos: (search: string, limit: number, shouldUseCreatedDateForSort: boolean) => {
let url = `/api/atoms?limit=${limit}`;
if (search) {
url += `&search=${search}`;
}
+ if(shouldUseCreatedDateForSort) {
+ url += '&shouldUseCreatedDateForSort=true';
+ }
return pandaReqwest({
url: url
diff --git a/public/video-ui/src/test/richTextInput.spec.ts b/public/video-ui/src/test/richTextInput.spec.ts
index 0c11cf82b..933a46f31 100644
--- a/public/video-ui/src/test/richTextInput.spec.ts
+++ b/public/video-ui/src/test/richTextInput.spec.ts
@@ -24,29 +24,17 @@ describe("isTooLong", () => {
it('Should return false for phrases less than the character and word limit', () => {
const tooLongString = "So it goes";
const maxWords = 100;
- const maxChars = 10;
- const isTooLongResult = isTooLong(tooLongString, maxWords, maxChars);
+ const isTooLongResult = isTooLong(tooLongString, maxWords);
expect(isTooLongResult).toBe(false);
});
- it('Should return true for phrases over the character limit', () => {
- const stringWithTooManyCharacters = "abcdefghijklmnop";
- const maxWords = 100;
- const maxChars = 10;
-
- const isTooLongResult = isTooLong(stringWithTooManyCharacters, maxWords, maxChars);
-
- expect(isTooLongResult).toBe(true);
- });
-
it('Should return true for phrases over the word limit', () => {
const stringWithTooManyWords = " All statements are true in some sense, false in some sense, meaningless in some sense, true and false in some sense, true and meaningless in some sense, false and meaningless in some sense, and true and false and meaningless in some sense.";
const maxWords = 5;
- const maxChars = 10000;
- const isTooLongResult = isTooLong(stringWithTooManyWords, maxWords, maxChars);
+ const isTooLongResult = isTooLong(stringWithTooManyWords, maxWords);
expect(isTooLongResult).toBe(true);
});
diff --git a/public/video-ui/src/util/hasUnpublishedChanges.js b/public/video-ui/src/util/hasUnpublishedChanges.js
deleted file mode 100644
index 6e759bc7d..000000000
--- a/public/video-ui/src/util/hasUnpublishedChanges.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import _ from 'lodash';
-import { appUpdatedFields } from '../constants/appUpdatedFields';
-import { imageFields } from '../constants/imageFields';
-
-export function hasUnpublishedChanges(
- previewVideo,
- publishedVideo,
- editableFields
-) {
- if (!previewVideo) {
- return false;
- }
-
- if (!publishedVideo) {
- return true;
- }
-
- const allFields = [...editableFields, ...appUpdatedFields, ...imageFields];
-
- return !allFields.every(key => {
- return _.isEqual(previewVideo[key], publishedVideo[key]);
- });
-}
diff --git a/public/video-ui/src/util/hasUnpublishedChanges.ts b/public/video-ui/src/util/hasUnpublishedChanges.ts
new file mode 100644
index 000000000..57a2b9343
--- /dev/null
+++ b/public/video-ui/src/util/hasUnpublishedChanges.ts
@@ -0,0 +1,19 @@
+import _ from 'lodash';
+import {appUpdatedFields} from '../constants/appUpdatedFields';
+import {imageFields} from '../constants/imageFields';
+import {Video} from "../services/VideosApi";
+
+export function hasUnpublishedChanges(
+ previewVideo: Video,
+ publishedVideo: Video
+) {
+ if (!previewVideo) {
+ return false;
+ }
+
+ if (!publishedVideo) {
+ return true;
+ }
+
+ return previewVideo.contentChangeDetails?.revision > publishedVideo.contentChangeDetails?.revision;
+}
diff --git a/public/video-ui/src/util/validateField.js b/public/video-ui/src/util/validateField.js
index a486db750..3f905ffbc 100644
--- a/public/video-ui/src/util/validateField.js
+++ b/public/video-ui/src/util/validateField.js
@@ -6,7 +6,8 @@ const validateField = (
isRequired = false,
isDesired = false,
customValidation = null,
- composerValidation = false
+ composerValidation = false,
+ maxLength
) => {
if (customValidation) {
return customValidation(fieldValue);
@@ -41,6 +42,22 @@ const validateField = (
);
}
+ function fieldValueTooLong() {
+ if (fieldValue && fieldValue.length === maxLength) {
+ return true;
+ }
+
+ return false;
+ }
+
+ if (maxLength && fieldValueTooLong()) {
+ return new FieldNotification(
+ 'too long',
+ 'You have reached the maximum character length',
+ FieldNotification.error
+ );
+ }
+
return null;
};
diff --git a/public/video-ui/styles/layout/_topbar.scss b/public/video-ui/styles/layout/_topbar.scss
index 806efb63a..10a19f6d0 100644
--- a/public/video-ui/styles/layout/_topbar.scss
+++ b/public/video-ui/styles/layout/_topbar.scss
@@ -5,6 +5,9 @@ $topbarHeight: 50px;
top: 0;
z-index: 1;
+ justify-content: right;
+ flex-wrap: wrap;
+
background: $color700Grey;
border-bottom: 1px solid $greyBorderColor;
@@ -50,6 +53,12 @@ $topbarHeight: 50px;
.topbar__global {
margin-right: 10px;
}
+.topbar__global select {
+ font-family: inherit;
+ background: none;
+ color: inherit;
+ padding: 3px;
+}
.topbar__functional {
margin-left: 20px;
diff --git a/public/video-ui/styles/layout/_video.scss b/public/video-ui/styles/layout/_video.scss
index ef7780062..a74296832 100644
--- a/public/video-ui/styles/layout/_video.scss
+++ b/public/video-ui/styles/layout/_video.scss
@@ -64,6 +64,8 @@
justify-content: space-between;
padding: 10px;
margin-right: 4px;
+ gap: 10px;
+ align-items: center;
}
.video__detailbox {
@@ -72,6 +74,7 @@
border-top: 1px solid $color600Grey;
&__header {
+ flex-grow: 1;
color: $color400Grey;
font-weight: bold;
display: block;
diff --git a/scripts/scala-ci.sh b/scripts/scala-ci.sh
index 6c19276af..b8f884950 100755
--- a/scripts/scala-ci.sh
+++ b/scripts/scala-ci.sh
@@ -1,5 +1,7 @@
#!/usr/bin/env bash
set -e
-
+ # Ensure we don't overwrite existing (Teamcity) builds.
+ LAST_TEAMCITY_BUILD=4800
+ export GITHUB_RUN_NUMBER=$(( $GITHUB_RUN_NUMBER + $LAST_TEAMCITY_BUILD ))
sbt clean compile test riffRaffUpload
diff --git a/yarn.lock b/yarn.lock
index 8ff242d3b..1f5132d30 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -10,14 +10,22 @@
"@jridgewell/gen-mapping" "^0.1.0"
"@jridgewell/trace-mapping" "^0.3.9"
-"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4":
+"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.16.7":
+ version "7.22.13"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e"
+ integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==
+ dependencies:
+ "@babel/highlight" "^7.22.13"
+ chalk "^2.4.2"
+
+"@babel/code-frame@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a"
integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==
dependencies:
"@babel/highlight" "^7.10.4"
-"@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.18.6":
+"@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a"
integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==
@@ -448,15 +456,10 @@
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63"
integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==
-"@babel/helper-validator-identifier@^7.10.4":
- version "7.10.4"
- resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2"
- integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==
-
-"@babel/helper-validator-identifier@^7.18.6":
- version "7.18.6"
- resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076"
- integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==
+"@babel/helper-validator-identifier@^7.10.4", "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.22.5":
+ version "7.22.15"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.15.tgz#601fa28e4cc06786c18912dca138cec73b882044"
+ integrity sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ==
"@babel/helper-validator-identifier@^7.19.1":
version "7.19.1"
@@ -496,22 +499,13 @@
"@babel/traverse" "^7.18.6"
"@babel/types" "^7.18.6"
-"@babel/highlight@^7.10.4":
- version "7.10.4"
- resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143"
- integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==
- dependencies:
- "@babel/helper-validator-identifier" "^7.10.4"
- chalk "^2.0.0"
- js-tokens "^4.0.0"
-
-"@babel/highlight@^7.18.6":
- version "7.18.6"
- resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf"
- integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==
+"@babel/highlight@^7.10.4", "@babel/highlight@^7.18.6", "@babel/highlight@^7.22.13":
+ version "7.22.13"
+ resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.13.tgz#9cda839e5d3be9ca9e8c26b6dd69e7548f0cbf16"
+ integrity sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==
dependencies:
- "@babel/helper-validator-identifier" "^7.18.6"
- chalk "^2.0.0"
+ "@babel/helper-validator-identifier" "^7.22.5"
+ chalk "^2.4.2"
js-tokens "^4.0.0"
"@babel/parser@^7.1.0", "@babel/parser@^7.10.4", "@babel/parser@^7.11.0", "@babel/parser@^7.11.1", "@babel/parser@^7.7.0":
@@ -1765,7 +1759,7 @@
jest-matcher-utils "^28.0.0"
pretty-format "^28.0.0"
-"@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
+"@types/json-schema@*", "@types/json-schema@^7.0.9":
version "7.0.11"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
@@ -1775,6 +1769,11 @@
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd"
integrity sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==
+"@types/json-schema@^7.0.8":
+ version "7.0.12"
+ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb"
+ integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==
+
"@types/lodash@^4.14.191":
version "4.14.191"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.191.tgz#09511e7f7cba275acd8b419ddac8da9a6a79e2fa"
@@ -2245,7 +2244,7 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1:
dependencies:
color-convert "^1.9.0"
-ansi-styles@^4.0.0, ansi-styles@^4.1.0:
+ansi-styles@^4.0.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359"
integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==
@@ -2253,6 +2252,13 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
"@types/color-name" "^1.1.1"
color-convert "^2.0.1"
+ansi-styles@^4.1.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
+ integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
+ dependencies:
+ color-convert "^2.0.1"
+
ansi-styles@^5.0.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
@@ -2767,9 +2773,9 @@ balanced-match@^0.4.1:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838"
balanced-match@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
- integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
+ integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
base64-js@^1.0.2:
version "1.2.0"
@@ -3072,7 +3078,7 @@ chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3:
strip-ansi "^3.0.0"
supports-color "^2.0.0"
-chalk@^2.0.0, chalk@^2.4.2:
+chalk@^2.4.2:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@@ -3237,7 +3243,7 @@ color-convert@^2.0.1:
color-name@1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
- integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
+ integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
color-name@~1.1.4:
version "1.1.4"
@@ -3298,7 +3304,7 @@ compression@^1.7.4:
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
- integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
+ integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
connect-history-api-fallback@^1.6.0:
version "1.6.0"
@@ -3542,9 +3548,9 @@ deep-is@^0.1.3:
integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
deepmerge@^4.2.2:
- version "4.2.2"
- resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
- integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a"
+ integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==
default-gateway@^4.2.0:
version "4.2.0"
@@ -3769,7 +3775,15 @@ end-of-stream@^1.1.0:
dependencies:
once "^1.4.0"
-enhanced-resolve@^5.0.0, enhanced-resolve@^5.10.0:
+enhanced-resolve@^5.0.0:
+ version "5.15.0"
+ resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35"
+ integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==
+ dependencies:
+ graceful-fs "^4.2.4"
+ tapable "^2.2.0"
+
+enhanced-resolve@^5.10.0:
version "5.12.0"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz#300e1c90228f5b570c4d35babf263f6da7155634"
integrity sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==
@@ -3917,6 +3931,7 @@ escape-html@~1.0.3:
escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+ integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
escape-string-regexp@^2.0.0:
version "2.0.0"
@@ -4555,10 +4570,10 @@ fs-extra@^10.0.0:
jsonfile "^6.0.1"
universalify "^2.0.0"
-fs-monkey@^1.0.3:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.3.tgz#ae3ac92d53bb328efe0e9a1d9541f6ad8d48e2d3"
- integrity sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==
+fs-monkey@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.4.tgz#ee8c1b53d3fe8bb7e5d2c5c5dfc0168afdd2f747"
+ integrity sha512-INM/fWAxMICjttnD0DX1rBvinKskj5G1w+oy/pnm9u/tSlnBrzFonJMcalKJ30P8RRsPzKcCG7Q8l0jx5Fh9YQ==
fs.realpath@^1.0.0:
version "1.0.0"
@@ -4573,11 +4588,16 @@ fsevents@^1.2.7:
bindings "^1.5.0"
nan "^2.12.1"
-fsevents@^2.3.2, fsevents@~2.3.2:
+fsevents@^2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
+fsevents@~2.3.2:
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
+ integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
+
fstream@^1.0.0, fstream@^1.0.12:
version "1.0.12"
resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045"
@@ -4837,12 +4857,17 @@ gopd@^1.0.1:
dependencies:
get-intrinsic "^1.1.3"
-graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6:
+graceful-fs@^4.1.11, graceful-fs@^4.1.2:
version "4.2.4"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
-graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9:
+graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4:
+ version "4.2.11"
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
+ integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
+
+graceful-fs@^4.2.9:
version "4.2.10"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
@@ -4889,7 +4914,7 @@ has-bigints@^1.0.1, has-bigints@^1.0.2:
has-flag@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
- integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
+ integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==
has-flag@^4.0.0:
version "4.0.0"
@@ -5266,6 +5291,7 @@ is-array-buffer@^3.0.1:
is-arrayish@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
+ integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==
is-bigint@^1.0.1:
version "1.0.4"
@@ -5391,6 +5417,7 @@ is-extglob@^1.0.0:
is-extglob@^2.1.0, is-extglob@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+ integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
is-finite@^1.0.0:
version "1.0.2"
@@ -5438,14 +5465,14 @@ is-glob@^3.1.0:
dependencies:
is-extglob "^2.1.0"
-is-glob@^4.0.0, is-glob@^4.0.1:
+is-glob@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
dependencies:
is-extglob "^2.1.1"
-is-glob@^4.0.3, is-glob@~4.0.1:
+is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
version "4.0.3"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
@@ -6263,9 +6290,9 @@ levn@^0.4.1:
type-check "~0.4.0"
lines-and-columns@^1.1.6:
- version "1.1.6"
- resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
- integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
+ integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
load-json-file@^1.0.0:
version "1.1.0"
@@ -6439,11 +6466,11 @@ media-typer@0.3.0:
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
memfs@^3.4.1:
- version "3.4.13"
- resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.4.13.tgz#248a8bd239b3c240175cd5ec548de5227fc4f345"
- integrity sha512-omTM41g3Skpvx5dSYeZIbXKcXoAVc/AoMNwn9TKx++L/gaen/+4TTttmu8ZSch5vfVJ8uJvGbroTsIlslRg6lg==
+ version "3.6.0"
+ resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.6.0.tgz#d7a2110f86f79dd950a8b6df6d57bc984aa185f6"
+ integrity sha512-EGowvkkgbMcIChjMTMkESFDbZeSh8xZ7kNSF0hAiAN4Jh6jgHCRS0Ga/+C8y6Au+oqpezRHCfPsmJ2+DwAgiwQ==
dependencies:
- fs-monkey "^1.0.3"
+ fs-monkey "^1.0.4"
memory-fs@^0.4.1:
version "0.4.1"
@@ -6608,14 +6635,7 @@ minimatch@^3.0.2, minimatch@~3.0.2:
dependencies:
brace-expansion "^1.0.0"
-minimatch@^3.0.4:
- version "3.0.4"
- resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
- integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
- dependencies:
- brace-expansion "^1.1.7"
-
-minimatch@^3.1.2:
+minimatch@^3.0.4, minimatch@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
@@ -7252,12 +7272,7 @@ picocolors@^1.0.0:
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
-picomatch@^2.0.4:
- version "2.2.2"
- resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
- integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==
-
-picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1:
+picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
@@ -7581,7 +7596,12 @@ punycode@1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
-punycode@^2.1.0, punycode@^2.1.1:
+punycode@^2.1.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f"
+ integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==
+
+punycode@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
@@ -8271,7 +8291,7 @@ schema-utils@^2.6.5, schema-utils@^2.7.0:
ajv "^6.12.2"
ajv-keywords "^3.4.1"
-schema-utils@^3.1.0, schema-utils@^3.1.1:
+schema-utils@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281"
integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==
@@ -8280,6 +8300,15 @@ schema-utils@^3.1.0, schema-utils@^3.1.1:
ajv "^6.12.5"
ajv-keywords "^3.5.2"
+schema-utils@^3.1.1:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe"
+ integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==
+ dependencies:
+ "@types/json-schema" "^7.0.8"
+ ajv "^6.12.5"
+ ajv-keywords "^3.5.2"
+
schema-utils@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.0.0.tgz#60331e9e3ae78ec5d16353c467c34b3a0a1d3df7"
@@ -8319,7 +8348,7 @@ semver@7.0.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
-semver@7.x, semver@^7.3.4, semver@^7.3.5:
+semver@7.x:
version "7.3.7"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f"
integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==
@@ -8341,6 +8370,13 @@ semver@^7.2.1, semver@^7.3.2:
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938"
integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==
+semver@^7.3.4, semver@^7.3.5:
+ version "7.5.4"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
+ integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
+ dependencies:
+ lru-cache "^6.0.0"
+
semver@^7.3.7:
version "7.3.8"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798"
@@ -8893,13 +8929,20 @@ supports-color@^6.1.0:
dependencies:
has-flag "^3.0.0"
-supports-color@^7.0.0, supports-color@^7.1.0:
+supports-color@^7.0.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1"
integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==
dependencies:
has-flag "^4.0.0"
+supports-color@^7.1.0:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+ integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+ dependencies:
+ has-flag "^4.0.0"
+
supports-color@^8.0.0:
version "8.1.1"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
@@ -9283,9 +9326,9 @@ update-browserslist-db@^1.0.4:
picocolors "^1.0.0"
uri-js@^4.2.2:
- version "4.2.2"
- resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
- integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==
+ version "4.4.1"
+ resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
+ integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
dependencies:
punycode "^2.1.0"