From c8c5f1a413c2b46e3183a2ffc29ff0e7d0bfb945 Mon Sep 17 00:00:00 2001 From: Lionel Date: Wed, 17 Jun 2020 12:37:38 +0200 Subject: [PATCH] feat(alerts): add kali / legi alerts (#24) Co-authored-by: Douglas Duteil --- .dockerignore | 1 + .gitignore | 1 + .gitlab-ci.yml | 4 + Dockerfile | 8 +- hasura/Dockerfile | 2 +- hasura/metadata/tables.yaml | 29 ++ .../migrations/1591618615701_alerts/down.sql | 6 + hasura/migrations/1591618615701_alerts/up.sql | 45 +++ package.json | 13 +- scripts/add-alerts.js | 30 ++ scripts/lib/ccn-list.js | 55 ++++ scripts/lib/compareTree.js | 150 +++++++++ scripts/update-alerts.js | 279 ++++++++++++++++ src/components/alerts/AlertTitle.js | 18 + src/components/alerts/Status.js | 45 +++ src/components/button/index.js | 33 +- src/components/changes/ChangeGroup.js | 27 ++ src/components/changes/ViewDiff.js | 69 ++++ src/components/changes/index.js | 85 +++++ src/components/collapsible/index.js | 16 + src/components/layout/Nav.js | 53 ++- src/components/tabs/index.js | 50 +++ src/lib/graphqlApiClient.js | 10 +- src/models.js | 10 + src/pages/_app.js | 1 + src/pages/alerts/[repo].js | 144 ++++++++ src/theme.js | 23 +- yarn.lock | 310 ++++++++++++++++-- 28 files changed, 1471 insertions(+), 46 deletions(-) create mode 100644 hasura/migrations/1591618615701_alerts/down.sql create mode 100644 hasura/migrations/1591618615701_alerts/up.sql create mode 100644 scripts/add-alerts.js create mode 100644 scripts/lib/ccn-list.js create mode 100644 scripts/lib/compareTree.js create mode 100644 scripts/update-alerts.js create mode 100644 src/components/alerts/AlertTitle.js create mode 100644 src/components/alerts/Status.js create mode 100644 src/components/changes/ChangeGroup.js create mode 100644 src/components/changes/ViewDiff.js create mode 100644 src/components/changes/index.js create mode 100644 src/components/collapsible/index.js create mode 100644 src/components/tabs/index.js create mode 100644 src/models.js create mode 100644 src/pages/alerts/[repo].js diff --git a/.dockerignore b/.dockerignore index 7f2c6dd27..05cbc7845 100644 --- a/.dockerignore +++ b/.dockerignore @@ -4,3 +4,4 @@ *.md **/node_modules **/.next/cache +data/* diff --git a/.gitignore b/.gitignore index 033103d36..71797681d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.DS_Store node_modules .env.production +data/* diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4e952baa4..01a0d300d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -17,6 +17,10 @@ variables: ENABLE_AZURE_POSTGRES: 1 VALUES_FILE: ./.k8s/app.values.yml +Install: + extends: .autodevops_install + image: node:12.18.0-alpine3.11 + Build: extends: .autodevops_build variables: diff --git a/Dockerfile b/Dockerfile index 2227fd4ae..b05b690b2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,18 @@ -FROM node:14.4-alpine3.11 +FROM node:12.18.0-alpine3.11 WORKDIR /app COPY package.json yarn.lock ./ -RUN yarn --frozen-lockfile +RUN apk add --no-cache build-base python --virtual .build-deps \ + && yarn --production --frozen-lockfile \ + && apk del .build-deps COPY next.config.js ./ COPY .env ./.env COPY .next/ ./.next +COPY scripts/ ./scripts +COPY data/ ./data COPY public/ ./public USER node diff --git a/hasura/Dockerfile b/hasura/Dockerfile index a917f2eb1..c1ad6af03 100644 --- a/hasura/Dockerfile +++ b/hasura/Dockerfile @@ -1,4 +1,4 @@ -FROM hasura/graphql-engine:v1.2.1.cli-migrations-v2 +FROM hasura/graphql-engine:v1.2.2.cli-migrations-v2 ENV HASURA_GRAPHQL_ENABLE_TELEMETRY false COPY ./migrations /hasura-migrations COPY ./metadata /hasura-metadata diff --git a/hasura/metadata/tables.yaml b/hasura/metadata/tables.yaml index 4f6aedc3c..c57eab83e 100644 --- a/hasura/metadata/tables.yaml +++ b/hasura/metadata/tables.yaml @@ -98,6 +98,24 @@ headers: - name: email-secret value_from_env: ACCOUNT_EMAIL_SECRET +- table: + schema: public + name: alert_status + array_relationships: + - name: alerts + using: + foreign_key_constraint_on: + column: status + table: + schema: public + name: alerts +- table: + schema: public + name: alerts + object_relationships: + - name: source + using: + foreign_key_constraint_on: repository - table: schema: public name: roles @@ -122,3 +140,14 @@ columns: - role filter: {} +- table: + schema: public + name: sources + array_relationships: + - name: alerts + using: + foreign_key_constraint_on: + column: repository + table: + schema: public + name: alerts diff --git a/hasura/migrations/1591618615701_alerts/down.sql b/hasura/migrations/1591618615701_alerts/down.sql new file mode 100644 index 000000000..a084e3530 --- /dev/null +++ b/hasura/migrations/1591618615701_alerts/down.sql @@ -0,0 +1,6 @@ + +DROP TABLE "public"."alerts"; + +DROP TABLE "public"."sources"; + +DROP TABLE "public"."alert_status"; diff --git a/hasura/migrations/1591618615701_alerts/up.sql b/hasura/migrations/1591618615701_alerts/up.sql new file mode 100644 index 000000000..d23c307d5 --- /dev/null +++ b/hasura/migrations/1591618615701_alerts/up.sql @@ -0,0 +1,45 @@ + +CREATE TABLE "public"."alert_status"("name" text NOT NULL DEFAULT 'new', PRIMARY KEY ("name") ); +COMMENT ON TABLE "public"."alert_status" IS E'alert statuses'; + +INSERT INTO public.alert_status (name) VALUES ('todo'); +INSERT INTO public.alert_status (name) VALUES ('doing'); +INSERT INTO public.alert_status (name) VALUES ('done'); +INSERT INTO public.alert_status (name) VALUES ('rejected'); + +CREATE TABLE "public"."sources"( + "repository" text NOT NULL, + "label" text NOT NULL, + "tag" text NOT NULL, + "created_at" timestamptz NOT NULL DEFAULT now(), + PRIMARY KEY ("repository") +); + +COMMENT ON TABLE "public"."sources" IS E'sources are git repository that acts as data sources to track changes'; + +INSERT INTO public.sources (repository, label, tag) VALUES ('socialgouv/legi-data', 'code du travail', 'v1.12.0'); +INSERT INTO public.sources (repository, label, tag) VALUES ('socialgouv/kali-data', 'conventions collectives', 'v1.64.0'); + +CREATE TABLE "public"."alerts"( + "id" uuid NOT NULL DEFAULT gen_random_uuid(), + "info" jsonb NOT NULL, + "status" text NOT NULL DEFAULT 'todo', + "repository" text NOT NULL, + "ref" text NOT NULL, + "changes" jsonb NOT NULL, + "created_at" timestamptz NULL DEFAULT now(), + "updated_at" timestamptz NULL DEFAULT now(), + PRIMARY KEY ("id") , + FOREIGN KEY ("status") REFERENCES "public"."alert_status"("name") ON UPDATE restrict ON DELETE restrict, + FOREIGN KEY ("repository") REFERENCES "public"."sources"("repository") ON UPDATE restrict ON DELETE cascade); + +COMMENT ON TABLE "public"."alerts" IS + E'alerts reprensent a change in a text from a source'; + +CREATE TRIGGER "set_public_alerts_updated_at" + BEFORE UPDATE ON public.alerts + FOR EACH ROW + EXECUTE PROCEDURE trigger_set_timestamp(); + +COMMENT ON TRIGGER "set_public_alerts_updated_at" ON public.alerts + IS 'trigger to set value of column "updated_at" to current timestamp on row update'; diff --git a/package.json b/package.json index 8a6061d83..47957e752 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "dependencies": { "@hapi/boom": "^9.1.0", "@hapi/joi": "^17.1.1", + "@reach/accordion": "^0.10.3", "@reach/dialog": "^0.10.3", "@reach/menu-button": "^0.10.3", "@reach/visually-hidden": "^0.10.2", @@ -17,12 +18,14 @@ "@zeit/next-source-maps": "0.0.4-canary.1", "argon2": "^0.26.2", "cookie": "^0.4.1", - "dotenv": "^8.2.0", + "diff": "^4.0.2", "graphql": "^15.0.0", "http-proxy-middleware": "^1.0.4", + "isomorphic-unfetch": "^3.0.0", "jsonwebtoken": "^8.5.1", "next": "^9.4.4", "next-urql": "^0.3.8", + "nodegit": "^0.26.5", "nodemailer": "^6.4.8", "polished": "^3.6.5", "react": "^16.13.1", @@ -30,8 +33,11 @@ "react-hook-form": "^5.7.2", "react-icons": "^3.10.0", "react-is": "^16.13.1", + "semver": "^7.3.2", "sentry-testkit": "^3.2.1", "theme-ui": "^0.3.1", + "unist-util-parents": "^1.0.3", + "unist-util-select": "^3.0.1", "urql": "^1.9.8", "uuid": "^8.1.0", "wonka": "^4.0.14" @@ -50,7 +56,12 @@ "scripts": { "dev": "next dev", "build": "next build", + "prestart": "node scripts/update-alerts.js", "start": "next start", + "alert": " node scripts/update-alerts.js", + "alert:dev": "GRAPHQL_ENDPOINT=http://localhost:8080/v1/graphql HASURA_GRAPHQL_ADMIN_SECRET=admin1 node scripts/update-alerts.js", + "alert:dump": "GRAPHQL_ENDPOINT=http://localhost:8080/v1/graphql HASURA_GRAPHQL_ADMIN_SECRET=admin1 DUMP=true node scripts/update-alerts.js > data/dump.json", + "alert:populate": "GRAPHQL_ENDPOINT=http://localhost:8080/v1/graphql HASURA_GRAPHQL_ADMIN_SECRET=admin1 node scripts/add-alerts.js", "lint": "eslint src/*", "test": "jest" }, diff --git a/scripts/add-alerts.js b/scripts/add-alerts.js new file mode 100644 index 000000000..1cc8e19b8 --- /dev/null +++ b/scripts/add-alerts.js @@ -0,0 +1,30 @@ +const { promises: fs } = require("fs"); +const path = require("path"); +const filename = + process.env.DUMP_FILE || path.join(__dirname, "..", "data", "dump.json"); +const { updateSource, insertAlert } = require("./update-alerts"); + +async function main() { + console.log(filename); + const fileContent = await fs.readFile(filename); + const data = JSON.parse(fileContent); + + for (const result of data) { + if (result.changes.length === 0) { + console.log(`no update for ${result.repository}`); + continue; + } + const inserts = await Promise.all( + result.changes.map((diff) => insertAlert(result.repository, diff)) + ); + inserts.forEach((insert) => { + const { ref, repository, info } = insert.returning[0]; + console.log(`insert alert for ${ref} on ${repository} (${info.file})`); + }); + console.log(`create ${inserts.length} alert for ${result.repository}`); + const update = await updateSource(result.repository, result.newRef); + console.log(`update source ${update.repository} to ${update.tag}`); + } +} + +main().catch(console.error); diff --git a/scripts/lib/ccn-list.js b/scripts/lib/ccn-list.js new file mode 100644 index 000000000..1de82d3a3 --- /dev/null +++ b/scripts/lib/ccn-list.js @@ -0,0 +1,55 @@ +const ccns = [ + { id: "KALICONT000005635624", num: 16 }, + { id: "KALICONT000005635234", num: 29 }, + { id: "KALICONT000005635613", num: 44 }, + { id: "KALICONT000005635630", num: 86 }, + { id: "KALICONT000005635184", num: 176 }, + { id: "KALICONT000005635872", num: 275 }, + { id: "KALICONT000005635856", num: 292 }, + { id: "KALICONT000005635407", num: 413 }, + { id: "KALICONT000005635373", num: 573 }, + { id: "KALICONT000005635842", num: 650 }, + { id: "KALICONT000005635617", num: 675 }, + { id: "KALICONT000005635826", num: 787 }, + { id: "KALICONT000005635886", num: 843 }, + { id: "KALICONT000005635953", num: 1043 }, + { id: "KALICONT000005635191", num: 1090 }, + { id: "KALICONT000005635409", num: 1147 }, + { id: "KALICONT000005635418", num: 1266 }, + { id: "KALICONT000005635405", num: 1351 }, + { id: "KALICONT000005635653", num: 1404 }, + { id: "KALICONT000005635444", num: 1480 }, + { id: "KALICONT000005635594", num: 1483 }, + { id: "KALICONT000005635173", num: 1486 }, + { id: "KALICONT000005635596", num: 1501 }, + { id: "KALICONT000005635421", num: 1505 }, + { id: "KALICONT000005635435", num: 1516 }, + { id: "KALICONT000005635870", num: 1517 }, + { id: "KALICONT000005635177", num: 1518 }, + { id: "KALICONT000005635413", num: 1527 }, + { id: "KALICONT000005635221", num: 1596 }, + { id: "KALICONT000005635220", num: 1597 }, + { id: "KALICONT000005635871", num: 1606 }, + { id: "KALICONT000005635918", num: 1672 }, + { id: "KALICONT000005635467", num: 1702 }, + { id: "KALICONT000005635685", num: 1740 }, + { id: "KALICONT000005635534", num: 1979 }, + { id: "KALICONT000005635528", num: 1996 }, + { id: "KALICONT000005635550", num: 2098 }, + { id: "KALICONT000005635792", num: 2111 }, + { id: "KALICONT000005635780", num: 2120 }, + { id: "KALICONT000005635557", num: 2148 }, + { id: "KALICONT000005635085", num: 2216 }, + { id: "KALICONT000005635813", num: 2264 }, + { id: "KALICONT000005635807", num: 2395 }, + { id: "KALICONT000017941839", num: 2420 }, + { id: "KALICONT000017577652", num: 2511 }, + { id: "KALICONT000018563755", num: 2596 }, + { id: "KALICONT000018773893", num: 2609 }, + { id: "KALICONT000018926209", num: 2614 }, + { id: "KALICONT000025805800", num: 2941 }, + { id: "KALICONT000027172335", num: 3043 }, + { id: "KALICONT000027084096", num: 3127 }, +]; + +module.exports = { ccns }; diff --git a/scripts/lib/compareTree.js b/scripts/lib/compareTree.js new file mode 100644 index 000000000..b6d7897b4 --- /dev/null +++ b/scripts/lib/compareTree.js @@ -0,0 +1,150 @@ +const parents = require("unist-util-parents"); +const { selectAll } = require("unist-util-select"); + +const getParents = (node) => { + var chain = []; + while (node) { + node.data.title && chain.unshift(node.data.title); + node = node.parent; + } + return chain; +}; + +// find the first parent text id to make legifrance links later +const getParentTextId = (node) => { + let id; + node = node.parent; + while (node) { + if ( + node.data && + node.data.id && + node.data.id.match(/^(KALI|LEGI)TEXT\d+$/) + ) { + id = node.data.id; + break; + } + node = node.parent; + } + return id || null; +}; + +// find the root text id to make legifrance links later +const getRootId = (node) => { + let id; + while (node) { + id = node.data.id; + node = node.parent; + } + return id || null; +}; + +const addContext = (node) => ({ + ...node, + parents: getParents(node), + textId: getParentTextId(node) || null, + rootId: getRootId(node) || null, +}); + +// dont include children in final results +const stripChildren = (node) => node; //({ children, ...props }) => props; + +// return diffed articles nodes +const compareArticles = (tree1, tree2, comparator) => { + const parentsTree1 = parents(tree1); + const parentsTree2 = parents(tree2); + + // all articles from tree1 + const articles1 = selectAll("article", parentsTree1).map(addContext); + const articles1cids = articles1 + .map((a) => a && a.data && a.data.cid) + .filter(Boolean); + // all articles from tree2 + const articles2 = selectAll("article", parentsTree2).map(addContext); + const articles2cids = articles2 + .map((a) => a && a.data && a.data.cid) + .filter(Boolean); + + // new : articles in tree2 not in tree1 + const newArticles = articles2.filter( + (art) => art && art.data && !articles1cids.includes(art.data.cid) + ); + const newArticlesCids = newArticles.map((a) => a.data.cid); + + // supressed: articles in tree1 not in tree2 + const missingArticles = articles1.filter( + (art) => art && art.data && !articles2cids.includes(art.data.cid) + ); + + // modified : articles with modified texte + const modifiedArticles = articles2.filter( + (art) => + art && + art.data && + // exclude new articles + !newArticlesCids.includes(art.data.cid) && + articles1.find( + // same article, different texte + (art2) => + art2 && + art2.data && + art2.data.cid === art.data.cid && + comparator(art, art2) + ) + ); + + // all sections from tree1 + const sections1 = selectAll("section", parentsTree1.children).map(addContext); + + const sections1cids = sections1.map((a) => a.data.cid); + + // all sections from tree2 + const sections2 = selectAll("section", parentsTree2.children).map(addContext); + const sections2cids = sections2.map((a) => a.data.cid); + + // new : sections in tree2 not in tree1 + const newSections = sections2.filter( + (section) => !sections1cids.includes(section.data.cid) + ); + const newSectionsCids = newSections.map((a) => a.data.cid); + + // supressed: sections in tree1 not in tree2 + const missingSections = sections1.filter( + (section) => !sections2cids.includes(section.data.cid) + ); + + // modified : sections with modified texte + const modifiedSections = sections2.filter( + (section) => + // exclude new sections + !newSectionsCids.includes(section.data.cid) && + sections1.find( + // same section, different etat + (section2) => + section2.data.cid === section.data.cid && + section2.data.etat !== section.data.etat + ) + ); + + const changes = { + added: [...newSections, ...newArticles].map(stripChildren), + removed: [...missingSections, ...missingArticles].map(stripChildren), + modified: [ + ...modifiedSections.map((modif) => ({ + ...modif, + // add the previous version in the result so we can diff later + previous: sections1.find( + (a) => a.data[idField] === modif.data[idField] + ), + })), + ...modifiedArticles.map((modif) => ({ + ...modif, + // add the previous version in the result so we can diff later + previous: articles1.find((a) => a.data.cid === modif.data.cid), + })), + ].map(stripChildren), + }; + + return changes; +}; + +module.exports = { compareArticles }; diff --git a/scripts/update-alerts.js b/scripts/update-alerts.js new file mode 100644 index 000000000..716a75efd --- /dev/null +++ b/scripts/update-alerts.js @@ -0,0 +1,279 @@ +const { client } = require("../src/lib/graphqlApiClient.js"); +const path = require("path"); +const nodegit = require("nodegit"); +const semver = require("semver"); +const { ccns } = require("./lib/ccn-list.js"); +const { compareArticles } = require("./lib/compareTree.js"); + +const sourcesQuery = ` +query getSources { + sources { + repository + tag + } +} +`; + +const insertAlertsMutation = ` +mutation insert_alerts($data: alerts_insert_input!) { + alert: insert_alerts(objects: [$data]) { + returning { + ref, + repository, + info + } + } +} +`; + +const updateSourceMutation = ` +mutation updateSource($repository: String!, $tag: String!){ + source: update_sources_by_pk( + _set:{ + tag: $tag + }, + pk_columns: { + repository: $repository + } + ){ + repository, tag + } +} +`; + +function getFileFilter(repository) { + switch (repository) { + case "socialgouv/legi-data": + // only code-du-travail + return (path) => /LEGITEXT000006072050\.json$/.test(path); + case "socialgouv/kali-data": + // only a ccn matching our list + return (path) => ccns.some((ccn) => new RegExp(ccn.id).test(path)); + default: + return () => true; + } +} + +function getFileComparator(repository) { + switch (repository) { + case "socialgouv/legi-data": + // only code-du-travail + return (art1, art2) => + art1.data.texte !== art2.data.texte || + art1.data.etat !== art2.data.etat || + art1.data.nota !== art2.data.nota; + case "socialgouv/kali-data": + // only a ccn matching our list + return (art1, art2) => + art1.data.content !== art2.data.content || + art1.data.etat !== art2.data.etat; + default: + return () => true; + } +} + +function getFilename(patche) { + return patche.newFile().path(); +} + +async function getSources() { + const result = await client.query(sourcesQuery).toPromise(); + if (result.error) { + console.error(result.error); + throw new Error("getSources"); + } + return result.data.sources; +} + +async function insertAlert(repository, changes) { + const data = { + repository, + info: { + num: changes.num, + title: changes.title, + id: changes.id, + file: changes.file, + }, + ref: changes.ref, + changes: { + added: changes.added, + removed: changes.removed, + modified: changes.modified, + }, + }; + const result = await client + .mutation(insertAlertsMutation, { data }) + .toPromise(); + if (result.error) { + console.error(result.error); + throw new Error("insertAlert"); + } + return result.data.alert; +} + +async function updateSource(repository, tag) { + const result = await client + .mutation(updateSourceMutation, { + repository, + tag, + }) + .toPromise(); + + if (result.error) { + console.error(result.error); + throw new Error("updateSource"); + } + return result.data.source; +} + +async function openRepo({ repository }) { + const [org, repositoryName] = repository.split("/"); + const localPath = path.join(__dirname, "..", "data", repositoryName); + let repo; + try { + repo = await nodegit.Repository.open(localPath); + await repo.checkoutBranch("master"); + await repo.mergeBranches("master", "origin/master"); + } catch (err) { + repo = await nodegit.Clone( + `git://github.com/${org}/${repositoryName}`, + localPath + ); + } + return repo; +} + +async function getNewerTagsFromRepo(repo, tag) { + const tags = await nodegit.Tag.list(repo); + return await Promise.all( + tags + .flatMap((t) => { + if (!semver.valid(t)) { + return []; + } + if (semver.lt(t, tag)) { + return []; + } + return t; + }) + .sort((a, b) => (semver.lt(a, b) ? -1 : 1)) + .map(async (tag) => { + const reference = await repo.getReference(tag); + const targetRef = await reference.peel(nodegit.Object.TYPE.COMMIT); + const commit = await repo.getCommit(targetRef); + return { + ref: tag, + commit, + }; + }) + ); +} + +async function getDiffFromTags(tags, id) { + let [previousTag] = tags; + const [, ...newTags] = tags; + const changes = []; + const fileFilter = getFileFilter(id); + + for (const tag of newTags) { + const { commit: previousCommit } = previousTag; + const { commit } = tag; + const [prevTree, currTree] = await Promise.all([ + previousCommit.getTree(), + commit.getTree(), + ]); + + const patches = await currTree + .diff(prevTree) + .then((diff) => diff.patches()); + + const files = patches.map(getFilename).filter(fileFilter); + + if (files.length > 0) { + const fileChanges = await Promise.all( + files.map((file) => + getFileDiffFromTrees(file, currTree, prevTree, getFileComparator(id)) + ) + ); + fileChanges + .filter( + (file) => + file.modified.length > 0 || + file.removed.length > 0 || + file.added.length > 0 + ) + .forEach((change) => { + changes.push({ ref: tag.ref, ...change }); + }); + } + previousTag = tag; + } + return changes; +} + +async function getFileDiffFromTrees( + filePath, + currGitTree, + prevGitTree, + compareFn +) { + const [currentFile, prevFile] = await Promise.all([ + currGitTree.getEntry(filePath).then((entry) => entry.getBlob()), + prevGitTree.getEntry(filePath).then((entry) => entry.getBlob()), + ]); + const currTree = JSON.parse(currentFile.toString()); + const prevTree = JSON.parse(prevFile.toString()); + return { + file: filePath, + id: currTree.data.id, + num: currTree.data.num, + title: currTree.data.title, + ...compareArticles(prevTree, currTree, compareFn), + }; +} + +async function main() { + const sources = await getSources(); + const results = []; + for (const source of sources) { + const repo = await openRepo(source); + const tags = await getNewerTagsFromRepo(repo, source.tag); + const diffs = await getDiffFromTags(tags, source.repository); + const [lastTag] = tags.slice(-1); + + results.push({ + repository: source.repository, + changes: diffs, + newRef: lastTag.ref, + }); + } + + if (process.env.DUMP) { + console.log(JSON.stringify(results, 0, 2)); + } else { + for (const result of results) { + if (result.changes.length === 0) { + console.log(`no update for ${result.repository}`); + continue; + } + const inserts = await Promise.all( + result.changes.map((diff) => insertAlert(result.repository, diff)) + ); + inserts.forEach((insert) => { + const { ref, repository, info } = insert.returning[0]; + console.log(`insert alert for ${ref} on ${repository} (${info.file})`); + }); + console.log(`create ${inserts.length} alert for ${result.repository}`); + const update = await updateSource(result.repository, result.newRef); + console.log(`update source ${update.repository} to ${update.tag}`); + } + } +} + +main().catch(console.error); + +module.exports = { + getSources, + insertAlert, + updateSource, +}; diff --git a/src/components/alerts/AlertTitle.js b/src/components/alerts/AlertTitle.js new file mode 100644 index 000000000..c682750f0 --- /dev/null +++ b/src/components/alerts/AlertTitle.js @@ -0,0 +1,18 @@ +/** @jsx jsx */ + +import { jsx, Flex, Box } from "theme-ui"; +import PropTypes from "prop-types"; +import { AlertStatus } from "./Status"; + +export function AlertTitle({ alertId, ...props }) { + return ( + + + + + ); +} + +AlertTitle.propTypes = { + alertId: PropTypes.string.isRequired, +}; diff --git a/src/components/alerts/Status.js b/src/components/alerts/Status.js new file mode 100644 index 000000000..b80905dc5 --- /dev/null +++ b/src/components/alerts/Status.js @@ -0,0 +1,45 @@ +/** @jsx jsx */ + +import { IoIosCheckmark, IoIosClose } from "react-icons/io"; +import { MenuButton, MenuItem } from "../button"; +import { jsx } from "theme-ui"; +import PropTypes from "prop-types"; +import { useMutation } from "urql"; + +export const alertMutation = ` +mutation updateAlertStatus($id:uuid!, $status:String!) { + update_alerts_by_pk( + pk_columns: { + id: $id + } + _set: { status: $status } + ){ + __typename + } +} +`; + +export function AlertStatus({ alertId }) { + const [, executeUpdate] = useMutation(alertMutation); + function updateStatus(status) { + console.log("update statys", alertId, status); + executeUpdate({ id: alertId, status }); + } + return ( + + updateStatus("doing")}> + + En cours + + updateStatus("done")}> + Traité + + updateStatus("rejected")}> + Rejeté + + + ); +} +AlertStatus.propTypes = { + alertId: PropTypes.string.isRequired, +}; diff --git a/src/components/button/index.js b/src/components/button/index.js index e9e54e8c8..823f75f51 100644 --- a/src/components/button/index.js +++ b/src/components/button/index.js @@ -13,7 +13,13 @@ import { MenuList, MenuItem as ReachMenuItem, } from "@reach/menu-button"; -import { IoMdMore } from "react-icons/io"; + +import { + AccordionButton as ReachAccordionButton, + useAccordionItemContext, +} from "@reach/accordion"; + +import { IoMdMore, IoIosArrowForward, IoIosArrowDown } from "react-icons/io"; const buttonPropTypes = { variant: PropTypes.oneOf(["secondary", "primary", "link"]), @@ -188,3 +194,28 @@ export function MenuItem(props) { /> ); } + +export function AccordionButton({ children, ...props }) { + return ( + + + {children} + + ); +} + +export function ExpandedIcon() { + const { isExpanded } = useAccordionItemContext(); + return isExpanded ? : ; +} diff --git a/src/components/changes/ChangeGroup.js b/src/components/changes/ChangeGroup.js new file mode 100644 index 000000000..8e6610d69 --- /dev/null +++ b/src/components/changes/ChangeGroup.js @@ -0,0 +1,27 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { AccordionButton } from "src/components/button"; +import { AccordionItem, AccordionPanel } from "@reach/accordion"; + +export const ChangesGroup = ({ changes, label, renderChange }) => { + return changes.length > 0 ? ( + + {label} + +
    {changes.map(renderChange)}
+
+
+ ) : null; +}; + +ChangesGroup.propTypes = { + label: PropTypes.string.isRequired, + renderChange: PropTypes.func.isRequired, + changes: PropTypes.arrayOf( + PropTypes.shape({ + added: PropTypes.object, + removed: PropTypes.object, + modified: PropTypes.object, + }) + ), +}; diff --git a/src/components/changes/ViewDiff.js b/src/components/changes/ViewDiff.js new file mode 100644 index 000000000..145354730 --- /dev/null +++ b/src/components/changes/ViewDiff.js @@ -0,0 +1,69 @@ +// adapted from https://github.com/davidmason/react-stylable-diff/blob/master/lib/react-diff.js +/** @jsx jsx */ + +import { useState, useMemo } from "react"; +import { jsx } from "theme-ui"; +var jsdiff = require("diff"); + +const fnMap = { + chars: jsdiff.diffChars, + words: jsdiff.diffWords, + sentences: jsdiff.diffSentences, + json: jsdiff.diffJson, +}; + +export const ViewDiff = ({ sx, type, inputA, inputB }) => { + const [mode, setMode] = useState(type); + const diff = fnMap[mode](inputA, inputB); + + const groupName = useMemo(() => Math.random()); + + const result = diff.map((part, index) => { + if (part.added) { + return ( + + {part.value} + + ); + } + if (part.removed) { + return ( + + {part.value} + + ); + } + return {part.value}; + }); + return ( +
+
+ Diff mode : + setMode("words")} + style={{ marginLeft: 10 }} + checked={mode === "words"} + />{" "} + Mots + setMode("sentences")} + style={{ marginLeft: 10 }} + checked={mode === "sentences"} + />{" "} + Phrases +
+ {result} +
+ ); +}; + +ViewDiff.defaultProps = { + inputA: "", + inputB: "", + type: "chars", + className: "Difference", +}; diff --git a/src/components/changes/index.js b/src/components/changes/index.js new file mode 100644 index 000000000..d732ce283 --- /dev/null +++ b/src/components/changes/index.js @@ -0,0 +1,85 @@ +/** @jsx jsx */ +import { jsx, Badge, Card } from "theme-ui"; +import { ViewDiff } from "./ViewDiff"; +import { Collapsible } from "../collapsible"; +import PropTypes from "prop-types"; + +export function DiffChange({ change, repository }) { + const { data, previous } = change; + const textFieldname = /legi-data/.test(repository) ? "texte" : "content"; + const content = data[textFieldname] || ""; + const previousContent = previous?.data[textFieldname] || ""; + const showDiff = previous && content !== previousContent; + const showNotaDiff = previous && previous.data.nota !== data.nota; + return ( +
+ Article {data.num}{" "} + {previous?.data.etat && previous?.data.etat !== data.etat && ( + <> + + {previous.data.etat} + {" "} + ›{" "} + + )} + + {data.etat} + + {showDiff && ( + + + + + + )} + {showNotaDiff && ( + + + + + + )} +
+ ); +} + +DiffChange.propTypes = { + repository: PropTypes.string.isRequired, + change: PropTypes.object.isRequired, +}; + +function getBadgeColor(etat) { + switch (etat) { + case "VIGUEUR": + return "positive"; + case "MOIFIE": + return "caution"; + case "ABROGE": + case "ABROGE_DIFF": + return "critical"; + default: + return "info"; + } +} diff --git a/src/components/collapsible/index.js b/src/components/collapsible/index.js new file mode 100644 index 000000000..8ddb5e8dc --- /dev/null +++ b/src/components/collapsible/index.js @@ -0,0 +1,16 @@ +import { Button } from "../button"; + +import React, { useState } from "react"; +import { IoMdGitNetwork } from "react-icons/io"; + +export function Collapsible({ label, children, ...props }) { + const [isVisible, setVisible] = useState(false); + return ( +
+ + {isVisible && children} +
+ ); +} diff --git a/src/components/layout/Nav.js b/src/components/layout/Nav.js index 9d908335e..9c6c3c88c 100644 --- a/src/components/layout/Nav.js +++ b/src/components/layout/Nav.js @@ -1,17 +1,40 @@ -import { jsx, Box, NavLink, Text } from "theme-ui"; +/** @jsx jsx */ + +import { jsx, Box, NavLink, Text, Badge } from "theme-ui"; import { useAuth } from "src/hooks/useAuth"; import Link from "next/link"; import { Li, List } from "../list"; +import { useQuery } from "urql"; +import { useMemo } from "react"; + +const getSourcesQuery = ` +query getAlerts{ + sources { + repository, + label, + alerts: alerts_aggregate(where: {status: {_eq: "todo"}}) { + aggregate { + count + } + } + } +} +`; -/** @jsx jsx */ export function Nav() { const { user } = useAuth(); const isAdmin = user?.roles.some(({ role }) => role === "admin"); - + // https://formidable.com/open-source/urql/docs/basics/document-caching/#adding-typenames + const context = useMemo( + () => ({ additionalTypenames: ["alerts", "sources"] }), + [] + ); + const [result] = useQuery({ query: getSourcesQuery, context }); + const { fetching, data } = result; return ( - Navigation + Utilisateurs {isAdmin && (
  • @@ -21,6 +44,28 @@ export function Nav() {
  • )}
    + Alertes + {!fetching && ( + + {data.sources.map((source) => ( +
  • + + {source.label} + + {" "} + {source.alerts.aggregate.count > 0 && ( + + {source.alerts.aggregate.count} + + )} +
  • + ))} +
    + )}
    ); diff --git a/src/components/tabs/index.js b/src/components/tabs/index.js new file mode 100644 index 000000000..99b1c177a --- /dev/null +++ b/src/components/tabs/index.js @@ -0,0 +1,50 @@ +/** @jsx jsx */ +import { jsx } from "theme-ui"; +import PropTypes from "prop-types"; + +export function Tabs(props) { + return ( +
      + ); +} + +export function TabItem({ selected, controls, ...props }) { + return ( +
    • + ); +} +TabItem.propTypes = { + selected: PropTypes.bool, + controls: PropTypes.string.isRequired, +}; +TabItem.defaultProps = { + selected: false, +}; diff --git a/src/lib/graphqlApiClient.js b/src/lib/graphqlApiClient.js index d1f40a76c..c25f4bee3 100644 --- a/src/lib/graphqlApiClient.js +++ b/src/lib/graphqlApiClient.js @@ -1,9 +1,14 @@ -import { createClient } from "urql"; +/** + * using require syntax here because graphqlClient is alos used + * within the nodejs scripts + */ +require("isomorphic-unfetch"); +const { createClient } = require("urql"); const HASURA_GRAPHQL_ADMIN_SECRET = process.env.HASURA_GRAPHQL_ADMIN_SECRET; const HASURA_GRAPHQL_ENDPOINT = process.env.GRAPHQL_ENDPOINT; -export const client = new createClient({ +const client = createClient({ url: HASURA_GRAPHQL_ENDPOINT, requestPolicy: "network-only", fetchOptions: { @@ -13,3 +18,4 @@ export const client = new createClient({ }, }, }); +module.exports = { client }; diff --git a/src/models.js b/src/models.js new file mode 100644 index 000000000..786580024 --- /dev/null +++ b/src/models.js @@ -0,0 +1,10 @@ +export const statusLabels = { + todo: "À traiter", + doing: "En cours de traitement", + done: "Traités", + rejected: "Rejetés", +}; + +export function getStatusLabel(status) { + return statusLabels[status] || status; +} diff --git a/src/pages/_app.js b/src/pages/_app.js index 304d568ac..84414355c 100644 --- a/src/pages/_app.js +++ b/src/pages/_app.js @@ -6,6 +6,7 @@ import { theme } from "src/theme"; import { ThemeProvider } from "theme-ui"; import "@reach/menu-button/styles.css"; import "@reach/dialog/styles.css"; +import "@reach/accordion/styles.css"; Sentry.init({ enabled: process.env.NODE_ENV === "production", diff --git a/src/pages/alerts/[repo].js b/src/pages/alerts/[repo].js new file mode 100644 index 000000000..229ff7328 --- /dev/null +++ b/src/pages/alerts/[repo].js @@ -0,0 +1,144 @@ +/** @jsx jsx */ + +import Link from "next/link"; +import { withCustomUrqlClient } from "src/hoc/CustomUrqlClient"; +import { Layout } from "src/components/layout/auth.layout"; +import { withAuthProvider } from "src/lib/auth"; +import { jsx, Message, NavLink, Container, Divider } from "theme-ui"; +import { useRouter } from "next/router"; +import { useQuery } from "urql"; +import { useState, useEffect, useMemo } from "react"; +import { Accordion } from "@reach/accordion"; +import { Tabs, TabItem } from "src/components/tabs"; +import { ChangesGroup } from "src/components/changes/ChangeGroup"; +import { DiffChange } from "src/components/changes"; +import { AlertTitle } from "src/components/alerts/AlertTitle"; +import { getStatusLabel } from "src/models"; + +const getAlertQuery = ` +query getAlerts($status: String!, $repository: String!) { + statuses: alert_status { + name + alerts: alerts_aggregate(where: { + repository: {_eq: $repository } + }) { + aggregate { + count + } + __typename + } + } + alerts(where: { + _and: [ + {status: {_eq: $status}}, + {repository: {_eq: $repository}}, + ] + }) { + id + ref + info + changes + status + created_at + __typename + } +} +`; + +export function AlertPage() { + const router = useRouter(); + const [, initialHash = "todo"] = router.asPath.split("#"); + const [hash, setHash] = useState(initialHash); + // https://formidable.com/open-source/urql/docs/basics/document-caching/#adding-typenames + const context = useMemo(() => ({ additionalTypenames: ["alerts"] }), []); + + const repository = router.query.repo.replace(/–/g, "/"); + const [result] = useQuery({ + query: getAlertQuery, + variables: { repository, status: hash }, + context, + }); + + useEffect(() => { + function onHashChange(url) { + const [, hash = "todo"] = url.split("#"); + setHash(hash); + } + + router.events.on("hashChangeComplete", onHashChange); + return function () { + router.events.off("hashChangeComplete", onHashChange); + }; + }); + + const { fetching, error, data } = result; + if (fetching) { + return
      Chargement...
      ; + } + if (error) { + return ( + +
      {JSON.stringify(error, 0, 2)}
      +
      + ); + } + const { statuses, alerts } = data; + + function renderChange(change, key) { + return ; + } + + return ( + + + {statuses.map((status) => ( + + + + {getStatusLabel(status.name)} ({status.alerts.aggregate.count}) + + + + ))} + + {alerts.map((alert) => ( + + + IDCC{alert.info.num} -{" "} + {new Date(alert.created_at).toLocaleDateString()} ({alert.ref}) + + + {alert.changes.added.length > 0 && ( + + renderChange(changes, `${alert.ref}-added-${i}`) + } + /> + )} + {alert.changes.added.length > 0 && + alert.changes.modified.length > 0 && } + + renderChange(changes, `${alert.ref}-modified-${i}`) + } + /> + {alert.changes.removed.length > 0 && } + + renderChange(changes, `${alert.ref}-removed-${i}`) + } + /> + + + ))} + + ); +} + +export default withCustomUrqlClient(withAuthProvider(AlertPage)); diff --git a/src/theme.js b/src/theme.js index 53f624edc..e3c8d87b8 100644 --- a/src/theme.js +++ b/src/theme.js @@ -1,12 +1,18 @@ import { rgba, darken, transparentize } from "polished"; export const theme = { + fonts: { + body: "muli", + heading: "muli", + monospace: "monospace", + }, sizes: { container: 1440, normal: "xsmall", small: "xxsmall", }, fontSizes: { + 0: "0.8rem", xsmall: "0.8rem", small: "0.9rem", medium: "1rem", @@ -15,11 +21,13 @@ export const theme = { xxlarge: "3.2rem", icons: "1.5rem", }, + // fontSizes: [12, 14, 16, 20, 24, 32, 48, 64], fontWeights: { body: 300, regular: 400, heading: 600, semibold: 600, + bold: 600, }, lineHeights: { body: 1.625, @@ -69,6 +77,7 @@ export const theme = { radii: { small: "4px", large: "8px", + xlarge: "16px", }, breakpoints: ["40rem", "56rem", "64rem"], styles: { @@ -131,15 +140,25 @@ export const theme = { primary: { bg: "primary", color: "white", - fontSize: "medium", px: "xxsmall", }, secondary: { bg: "secondary", color: "white", - fontSize: "medium", px: "xxsmall", }, + outline: { + color: "primary", + bg: "transparent", + boxShadow: "inset 0 0 0 1px", + }, + circle: { + bg: "accent", + borderRadius: "xlarge", + px: "5px", + py: "3px", + lineHeight: 1, + }, }, forms: { label: { diff --git a/yarn.lock b/yarn.lock index f8ca8c5db..c8a3f9e9a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1500,6 +1500,16 @@ dependencies: safe-buffer "^5.1.2" +"@reach/accordion@^0.10.3": + version "0.10.3" + resolved "https://registry.yarnpkg.com/@reach/accordion/-/accordion-0.10.3.tgz#6be590b6f8468a42f5727d1ff5ea03f7b36eb8be" + integrity sha512-GzRCJUPeDTGUdo+TMdwOFTTKc0GubkOEdBTQcOjKKMcLHxpOfZTx+hvkNREnXuqAyylyOfhxBKnuMWGS45+Weg== + dependencies: + "@reach/auto-id" "^0.10.3" + "@reach/descendants" "^0.10.3" + "@reach/utils" "^0.10.3" + tslib "^1.11.2" + "@reach/auto-id@^0.10.3": version "0.10.3" resolved "https://registry.yarnpkg.com/@reach/auto-id/-/auto-id-0.10.3.tgz#d2b6fe3ccb81b0fb44dc8bd3aca567c94ac11f5e" @@ -2506,6 +2516,11 @@ arrify@^2.0.1: resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== +asap@~2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= + asn1.js@^4.0.0: version "4.10.1" resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" @@ -2785,6 +2800,14 @@ bindings@^1.5.0: dependencies: file-uri-to-path "1.0.0" +bl@^1.0.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c" + integrity sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA== + dependencies: + readable-stream "^2.3.5" + safe-buffer "^5.1.1" + bluebird@^3.5.5: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" @@ -2939,11 +2962,29 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" +buffer-alloc-unsafe@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" + integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg== + +buffer-alloc@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec" + integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow== + dependencies: + buffer-alloc-unsafe "^1.1.0" + buffer-fill "^1.0.0" + buffer-equal-constant-time@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= +buffer-fill@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c" + integrity sha1-+PeLdniYiO858gXNY39o5wISKyw= + buffer-from@^1.0.0, buffer-from@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" @@ -3202,7 +3243,7 @@ chokidar@^3.3.0, chokidar@^3.4.0: optionalDependencies: fsevents "~2.1.2" -chownr@^1.1.1, chownr@^1.1.2: +chownr@^1.0.1, chownr@^1.1.1, chownr@^1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== @@ -3684,6 +3725,11 @@ css-select@^2.0.0: domutils "^1.7.0" nth-check "^1.0.2" +css-selector-parser@^1.0.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/css-selector-parser/-/css-selector-parser-1.4.1.tgz#03f9cb8a81c3e5ab2c51684557d5aaf6d2569759" + integrity sha512-HYPSb7y/Z7BNDCOrakL4raGO2zltZkbeXyAd6Tg9obzix6QhzxCotdBl6VT0Dv4vZfJGVz3WL/xaEI9Ly3ul0g== + css-tree@1.0.0-alpha.37: version "1.0.0-alpha.37" resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22" @@ -4011,6 +4057,11 @@ diff-sequences@^26.0.0: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.0.0.tgz#0760059a5c287637b842bd7085311db7060e88a6" integrity sha512-JC/eHYEC3aSS0vZGjuoc4vHA0yAQTzhQQldXMeMF+JlxLGJlCO38Gma82NV9gk1jGFz8mDzUMeaKXvjRRdJ2dg== +diff@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + diffie-hellman@^5.0.0: version "5.0.3" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" @@ -4110,11 +4161,6 @@ dot-prop@^5.2.0: dependencies: is-obj "^2.0.0" -dotenv@^8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" - integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== - duplexify@^3.4.2, duplexify@^3.6.0: version "3.7.1" resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" @@ -4249,7 +4295,7 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -es5-ext@^0.10.35, es5-ext@^0.10.50: +es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50: version "0.10.53" resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== @@ -4258,7 +4304,7 @@ es5-ext@^0.10.35, es5-ext@^0.10.50: es6-symbol "~3.1.3" next-tick "~1.0.0" -es6-iterator@2.0.3, es6-iterator@~2.0.3: +es6-iterator@2.0.3, es6-iterator@^2.0.3, es6-iterator@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= @@ -4275,6 +4321,16 @@ es6-symbol@^3.1.1, es6-symbol@~3.1.3: d "^1.0.1" ext "^1.1.2" +es6-weak-map@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53" + integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== + dependencies: + d "1" + es5-ext "^0.10.46" + es6-iterator "^2.0.3" + es6-symbol "^3.1.1" + escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -4914,6 +4970,20 @@ from2@^2.1.0: inherits "^2.0.1" readable-stream "^2.0.0" +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + +fs-extra@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" + integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-minipass@^1.2.5: version "1.2.7" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" @@ -5062,7 +5132,7 @@ glob-to-regexp@^0.4.1: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: +glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -5093,7 +5163,7 @@ globals@^12.1.0: dependencies: type-fest "^0.8.1" -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.2.2, graceful-fs@^4.2.4: +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.2.2, graceful-fs@^4.2.4: version "4.2.4" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== @@ -5818,6 +5888,14 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= +isomorphic-unfetch@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/isomorphic-unfetch/-/isomorphic-unfetch-3.0.0.tgz#de6d80abde487b17de2c400a7ef9e5ecc2efb362" + integrity sha512-V0tmJSYfkKokZ5mgl0cmfQMTb7MLHsBMngTkbLY0eXvKqiVRRoZP04Ly+KhKrJfKtzC9E6Pp15Jo+bwh7Vi2XQ== + dependencies: + node-fetch "^2.2.0" + unfetch "^4.0.0" + isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -6327,6 +6405,13 @@ json5@^2.1.0, json5@^2.1.2: dependencies: minimist "^1.2.5" +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + jsonparse@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" @@ -6989,7 +7074,7 @@ mute-stream@0.0.8: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -nan@^2.12.1: +nan@^2.12.1, nan@^2.14.0: version "2.14.1" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw== @@ -7024,9 +7109,9 @@ natural-compare@^1.4.0: integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= needle@^2.2.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.1.tgz#14af48732463d7475696f937626b1b993247a56a" - integrity sha512-x/gi6ijr4B7fwl6WYL9FwlCvRQKGlUNvnceho8wxkwXqN8jvVmmmATTmZPRRG7b/yC1eode26C2HO9jl78Du9g== + version "2.5.0" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.5.0.tgz#e6fc4b3cc6c25caed7554bd613a5cf0bac8c31c0" + integrity sha512-o/qITSDR0JCyCKEQ1/1bnUXMmznxabbwi/Y4WwJElf+evwJNFNwIDMCCt5IigFVxgeGBJESLohGtIS9gEzo1fA== dependencies: debug "^3.2.6" iconv-lite "^0.4.4" @@ -7118,15 +7203,32 @@ nice-try@^1.0.4: integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== node-addon-api@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.0.tgz#f9afb8d777a91525244b01775ea0ddbe1125483b" - integrity sha512-ASCL5U13as7HhOExbT6OlWJJUV/lLzL2voOSP1UVehpRD8FbSrSDjfScK/KwAvVTI5AS6r4VwbOMlIqtvRidnA== + version "2.0.1" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.1.tgz#4fd0931bf6d7e48b219ff3e6abc73cbb0252b7a3" + integrity sha512-2WVfwRfIr1AVn3dRq4yRc2Hn35ND+mPJH6inC6bjpYCZVrpXPB4j3T6i//OGVfqVsR1t/X/axRulDsheq4F0LQ== -node-fetch@2.6.0, node-fetch@^2.6.0: +node-fetch@2.6.0, node-fetch@^2.2.0, node-fetch@^2.6.0: version "2.6.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== +node-gyp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-4.0.0.tgz#972654af4e5dd0cd2a19081b4b46fe0442ba6f45" + integrity sha512-2XiryJ8sICNo6ej8d0idXDEMKfVfFK7kekGCtJAuelGsYHQxhj13KTf95swTCN2dZ/4lTfZ84Fu31jqJEEgjWA== + dependencies: + glob "^7.0.3" + graceful-fs "^4.1.2" + mkdirp "^0.5.0" + nopt "2 || 3" + npmlog "0 || 1 || 2 || 3 || 4" + osenv "0" + request "^2.87.0" + rimraf "2" + semver "~5.3.0" + tar "^4.4.8" + which "1" + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -7178,6 +7280,22 @@ node-notifier@^7.0.0: uuid "^7.0.3" which "^2.0.2" +node-pre-gyp@^0.13.0: + version "0.13.0" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.13.0.tgz#df9ab7b68dd6498137717838e4f92a33fc9daa42" + integrity sha512-Md1D3xnEne8b/HGVQkZZwV27WUi1ZRuZBij24TNaZwUPU3ZAFtvT6xxJGaUVillfmMKnn5oD1HoGsp2Ftik7SQ== + dependencies: + detect-libc "^1.0.2" + mkdirp "^0.5.1" + needle "^2.2.1" + nopt "^4.0.1" + npm-packlist "^1.1.6" + npmlog "^4.0.2" + rc "^1.2.7" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^4" + node-pre-gyp@^0.14.0: version "0.14.0" resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz#9a0596533b877289bcad4e143982ca3d904ddc83" @@ -7199,11 +7317,41 @@ node-releases@^1.1.53: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.55.tgz#8af23b7c561d8e2e6e36a46637bab84633b07cee" integrity sha512-H3R3YR/8TjT5WPin/wOoHOUPHgvj8leuU/Keta/rwelEQN9pA/S2Dx8/se4pZ2LBxSd0nAGzsNzhqwa77v7F1w== +nodegit-promise@~4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/nodegit-promise/-/nodegit-promise-4.0.0.tgz#5722b184f2df7327161064a791d2e842c9167b34" + integrity sha1-VyKxhPLfcycWEGSnkdLoQskWezQ= + dependencies: + asap "~2.0.3" + +nodegit@^0.26.5: + version "0.26.5" + resolved "https://registry.yarnpkg.com/nodegit/-/nodegit-0.26.5.tgz#1534d8aaa52de7acfbbe18de28df2075de86f7d2" + integrity sha512-l9l2zhcJ0V7FYzPdXIsuJcXN8UnLuhQgM+377HJfCYE/eupL/OWtMVvUOq42F9dRsgC3bAYH9j2Xbwr0lpYVZQ== + dependencies: + fs-extra "^7.0.0" + json5 "^2.1.0" + lodash "^4.17.14" + nan "^2.14.0" + node-gyp "^4.0.0" + node-pre-gyp "^0.13.0" + promisify-node "~0.3.0" + ramda "^0.25.0" + request-promise-native "^1.0.5" + tar-fs "^1.16.3" + nodemailer@^6.4.8: version "6.4.8" resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.4.8.tgz#aca52886e4e56f71f6b8a65f5ca6b767ca751fc7" integrity sha512-UbJD0+g5e2H20bWv7Rpj3B+N3TMMJ0MLoLwaGVJ0k3Vo8upq0UltwHJ5BJfrpST1vFa91JQ8cf7cICK5DSIo1Q== +"nopt@2 || 3": + version "3.0.6" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k= + dependencies: + abbrev "1" + nopt@^4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" @@ -7254,6 +7402,11 @@ normalize-url@^3.0.0: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== +not@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/not/-/not-0.1.0.tgz#c9691c1746c55dcfbe54cbd8bd4ff041bc2b519d" + integrity sha1-yWkcF0bFXc++VMvYvU/wQbwrUZ0= + npm-bundled@^1.0.1: version "1.1.1" resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b" @@ -7289,7 +7442,7 @@ npm-run-path@^4.0.0: dependencies: path-key "^3.0.0" -npmlog@^4.0.2: +"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== @@ -7299,7 +7452,7 @@ npmlog@^4.0.2: gauge "~2.7.3" set-blocking "~2.0.0" -nth-check@^1.0.2: +nth-check@^1.0.0, nth-check@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== @@ -7485,7 +7638,7 @@ os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= -osenv@^0.1.4: +osenv@0, osenv@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== @@ -8198,6 +8351,13 @@ promise-inflight@^1.0.1: resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= +promisify-node@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/promisify-node/-/promisify-node-0.3.0.tgz#b4b55acf90faa7d2b8b90ca396899086c03060cf" + integrity sha1-tLVaz5D6p9K4uQyjlomQhsAwYM8= + dependencies: + nodegit-promise "~4.0.0" + prompts@^2.0.1: version "2.3.2" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.3.2.tgz#480572d89ecf39566d2bd3fe2c9fccb7c4c0b068" @@ -8259,6 +8419,14 @@ public-encrypt@^4.0.0: randombytes "^2.0.1" safe-buffer "^5.1.2" +pump@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/pump/-/pump-1.0.3.tgz#5dfe8311c33bbf6fc18261f9f34702c47c08a954" + integrity sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + pump@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" @@ -8342,6 +8510,11 @@ quick-lru@^4.0.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== +ramda@^0.25.0: + version "0.25.0" + resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.25.0.tgz#8fdf68231cffa90bc2f9460390a0cb74a29b29a9" + integrity sha512-GXpfrYVPwx3K7RQ6aYT8KPS8XViSXUVJT1ONhoKPE9VAleW42YE+U+8VEyGWt41EnEQW7gwecYJriTI0pKoecQ== + randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -8530,7 +8703,7 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -8696,7 +8869,7 @@ request-promise-core@1.1.3: dependencies: lodash "^4.17.15" -request-promise-native@^1.0.8: +request-promise-native@^1.0.5, request-promise-native@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.8.tgz#a455b960b826e44e2bf8999af64dff2bfe58cb36" integrity sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ== @@ -8705,7 +8878,7 @@ request-promise-native@^1.0.8: stealthy-require "^1.1.1" tough-cookie "^2.3.3" -request@^2.88.2: +request@^2.87.0, request@^2.88.2: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -8839,6 +9012,13 @@ rgba-regex@^1.0.0: resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= +rimraf@2, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.3, rimraf@^2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + rimraf@2.6.3: version "2.6.3" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" @@ -8846,13 +9026,6 @@ rimraf@2.6.3: dependencies: glob "^7.1.3" -rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.3, rimraf@^2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== - dependencies: - glob "^7.1.3" - rimraf@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -9007,6 +9180,11 @@ 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@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" + integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8= + send@0.17.1: version "0.17.1" resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" @@ -9715,7 +9893,30 @@ tapable@^1.0.0, tapable@^1.1.3: resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== -tar@^4.4.2: +tar-fs@^1.16.3: + version "1.16.3" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-1.16.3.tgz#966a628841da2c4010406a82167cbd5e0c72d509" + integrity sha512-NvCeXpYx7OsmOh8zIOP/ebG55zZmxLE0etfWRbWok+q2Qo8x/vOR/IJT1taADXPe+jsiu9axDb3X4B+iIgNlKw== + dependencies: + chownr "^1.0.1" + mkdirp "^0.5.1" + pump "^1.0.0" + tar-stream "^1.1.2" + +tar-stream@^1.1.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555" + integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A== + dependencies: + bl "^1.0.0" + buffer-alloc "^1.2.0" + end-of-stream "^1.0.0" + fs-constants "^1.0.0" + readable-stream "^2.3.0" + to-buffer "^1.1.1" + xtend "^4.0.0" + +tar@^4, tar@^4.4.2, tar@^4.4.8: version "4.4.13" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== @@ -9845,6 +10046,11 @@ to-arraybuffer@^1.0.0: resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M= +to-buffer@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80" + integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg== + to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" @@ -10046,6 +10252,11 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= +unfetch@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.1.0.tgz#6ec2dd0de887e58a4dee83a050ded80ffc4137db" + integrity sha512-crP/n3eAPUJxZXM9T80/yv0YhkTEx2K1D3h7D1AJM6fzsWZrxdyRuLN0JH/dkZh1LNH8LxCnBzoPFCPbb2iGpg== + unicode-canonical-property-names-ecmascript@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" @@ -10103,6 +10314,34 @@ unique-slug@^2.0.0: dependencies: imurmurhash "^0.1.4" +unist-util-is@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.0.2.tgz#c7d1341188aa9ce5b3cff538958de9895f14a5de" + integrity sha512-Ofx8uf6haexJwI1gxWMGg6I/dLnF2yE+KibhD3/diOqY2TinLcqHXCV6OI5gFVn3xQqDH+u0M625pfKwIwgBKQ== + +unist-util-parents@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/unist-util-parents/-/unist-util-parents-1.0.3.tgz#9730446164303c40c01bcbe03221d11bc9189a4d" + integrity sha512-GA82HBLgPxR+qkOzbti/jxzmkMBDu8iS/mw58iCwLpCl9JpLyGxkVr7nsFpAX956qIsa5yKNKM5SeGS8yCY8Lw== + dependencies: + es6-weak-map "^2.0.0" + +unist-util-select@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/unist-util-select/-/unist-util-select-3.0.1.tgz#787fc452db9ba77f0ade0e7dc53c3d9d4acc79c7" + integrity sha512-VQpTuqZVJlRbosQdnLdTPIIqwZeU70YZ5aMBOqtFNGeeCdYn6ORZt/9RiaVlbl06ocuf58SVMoFa7a13CSGPMA== + dependencies: + css-selector-parser "^1.0.0" + not "^0.1.0" + nth-check "^1.0.0" + unist-util-is "^4.0.0" + zwitch "^1.0.0" + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" @@ -10430,7 +10669,7 @@ which-pm-runs@^1.0.0: resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs= -which@^1.2.9: +which@1, which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -10584,3 +10823,8 @@ yargs@^15.3.1: which-module "^2.0.0" y18n "^4.0.0" yargs-parser "^18.1.1" + +zwitch@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920" + integrity sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==