+
+This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a "Source" link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements.
+
+You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see .
+Standard License Header
+Copyright (C) [year] [name of author]
+
+This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License along with this program. If not, see .
diff --git a/constants.js b/constants.js
index 97a68fc6..b1a6126c 100644
--- a/constants.js
+++ b/constants.js
@@ -43,8 +43,8 @@ module.exports = {
AZURE_STORAGE_KEY: secretsEnv.AZURE_STORAGE_KEY || '',
CLIENT_SECRET: secretsEnv.CLIENT_SECRET || '',
API_CLIENT_SECRET: secretsEnv.API_CLIENT_SECRET || '',
- DOMAINS_ALLOWED_TO_LOGIN: secretsEnv.DOMAINS_ALLOWED_TO_LOGIN || '',
+ GROUP_GENERATE: secretsEnv.GROUP_GENERATE || '',
+ GROUP_READONLY: secretsEnv.GROUP_READONLY || '',
HSL_TESTING_HSLID_USERNAME: secretsEnv.HSL_TESTING_HSLID_USERNAME || '',
HSL_TESTING_HSLID_PASSWORD: secretsEnv.HSL_TESTING_HSLID_PASSWORD || '',
- ROUTEMAP_TEST_GROUP: secretsEnv.ROUTEMAP_TEST_GROUP || '',
};
diff --git a/package.json b/package.json
index b779718e..15135dc9 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "hsl-routemap-server",
- "version": "0.1.0",
+ "version": "1.1.0",
"description": "HSL Routemap server",
"main": "index.js",
"scripts": {
@@ -30,7 +30,7 @@
"url": "git+https://github.com/HSLdevcom/hsl-routemap-server.git"
},
"author": "",
- "license": "MIT",
+ "license": "AGPL-3.0-only",
"bugs": {
"url": "https://github.com/HSLdevcom/hsl-routemap-server/issues"
},
@@ -96,13 +96,14 @@
"pg": "^8.7.3",
"prop-types": "^15.6.0",
"puppeteer": "^15.4.1",
- "react": "16.8.6",
+ "react": "18.2.0",
"react-apollo": "^2.0.1",
- "react-dom": "16.8.6",
+ "react-dom": "18.2.0",
"recompose": "^0.30.0",
"segseg": "^0.2.2",
"serve": "^13.0.2",
"uuid": "^3.1.0",
+ "validator": "^13.15.0",
"viewport-mercator-project": "^4.1.1"
}
}
diff --git a/scripts/auth/authEndpoints.js b/scripts/auth/authEndpoints.js
index 44e4e009..5b9cf4a3 100644
--- a/scripts/auth/authEndpoints.js
+++ b/scripts/auth/authEndpoints.js
@@ -1,24 +1,20 @@
const { get, last, clone } = require('lodash');
const AuthService = require('./authService');
+const validator = require('validator');
-const { DOMAINS_ALLOWED_TO_LOGIN, ROUTEMAP_TEST_GROUP } = require('../../constants');
+const { GROUP_GENERATE, GROUP_READONLY } = require('../../constants');
-const allowedDomains = DOMAINS_ALLOWED_TO_LOGIN.split(',');
+const hasAllowedGroup = async (userInfo) => {
+ const groups = get(userInfo, 'groups', {});
-const hasAllowedDomain = async (userInfo) => {
- const groupNames = get(userInfo, 'groups');
- const domain = last(userInfo.email.toLowerCase().split('@')) || '';
-
- if (groupNames.includes(ROUTEMAP_TEST_GROUP)) {
- return true;
- }
-
- if (!allowedDomains.includes(domain)) {
- console.log(`User does not have allowed domain. Logging out.`);
+ if (!groups || !Array.isArray(groups)) {
+ console.log('User does not have valid groups assigned');
return false;
}
-
- return true;
+ if (groups.includes(GROUP_GENERATE) || groups.includes(GROUP_READONLY)) {
+ return true;
+ }
+ return false;
};
const authorize = async (req, res, session) => {
@@ -56,7 +52,7 @@ const authorize = async (req, res, session) => {
if (session && tokenResponse.access_token) {
modifiedSession.accessToken = tokenResponse.access_token;
const userInfo = await AuthService.requestUserInfo(modifiedSession.accessToken);
- const isAllowed = await hasAllowedDomain(userInfo);
+ const isAllowed = await hasAllowedGroup(userInfo);
if (!isAllowed) {
return {
status: 401,
@@ -93,7 +89,7 @@ const authorize = async (req, res, session) => {
const checkExistingSession = async (req, res, session) => {
if (session && session.accessToken) {
- const isAllowed = await hasAllowedDomain(session);
+ const isAllowed = await hasAllowedGroup(session);
if (!isAllowed) {
await AuthService.logoutFromIdentityProvider(session.accessToken);
return {
@@ -104,6 +100,7 @@ const checkExistingSession = async (req, res, session) => {
const response = {
isOk: true,
email: session.email,
+ groups: session.groups,
};
return {
status: 200,
diff --git a/scripts/server.js b/scripts/server.js
index 06fdc0c0..fa3753c5 100644
--- a/scripts/server.js
+++ b/scripts/server.js
@@ -29,7 +29,7 @@ const {
} = require('./joreStore');
const { downloadPostersFromCloud } = require('./cloudService');
-const { REDIS_CONNECTION_STRING } = require('../constants');
+const { REDIS_CONNECTION_STRING, GROUP_GENERATE } = require('../constants');
const PORT = 4000;
@@ -66,6 +66,16 @@ const errorHandler = async (ctx, next) => {
}
};
+const allowedToGenerate = (user) => {
+ if (!user || !user.email) return false;
+
+ if (user.groups && user.groups.includes(GROUP_GENERATE)) {
+ return true;
+ }
+
+ return false;
+};
+
const authMiddleware = async (ctx, next) => {
const endpointsNotRequiringAuthentication = ['/login', '/logout', '/session'];
if (endpointsNotRequiringAuthentication.includes(ctx.path)) {
@@ -82,6 +92,14 @@ const authMiddleware = async (ctx, next) => {
// Not authenticated, throw 401
ctx.throw(401);
} else {
+ // If the request is CRUD, check if the user has privileges to perform the action
+ if (ctx.method !== 'GET' && ctx.method !== 'HEAD') {
+ const user = authResponse.body;
+ if (!allowedToGenerate(user)) {
+ ctx.throw(403, 'User does not have privileges to perform this action.');
+ }
+ }
+
await next();
}
}
@@ -106,12 +124,40 @@ async function main() {
});
router.post('/builds', async (ctx) => {
+ const authResponse = await authEndpoints.checkExistingSession(
+ ctx.request,
+ ctx.response,
+ ctx.session,
+ );
+
+ if (!authResponse.body.isOk) {
+ ctx.throw(401, 'Not allowed.');
+ }
+
+ if (!authResponse.body.groups.includes(GROUP_GENERATE)) {
+ ctx.throw(403, 'User does not have permission to modify builds.');
+ }
+
const { title } = ctx.request.body;
const build = await addBuild({ title });
ctx.body = build;
});
router.put('/builds/:id', async (ctx) => {
+ const authResponse = await authEndpoints.checkExistingSession(
+ ctx.request,
+ ctx.response,
+ ctx.session,
+ );
+
+ if (!authResponse.body.isOk) {
+ ctx.throw(401, 'Not allowed.');
+ }
+
+ if (!authResponse.body.groups.includes(GROUP_GENERATE)) {
+ ctx.throw(403, 'User does not have permission to modify builds.');
+ }
+
const { id } = ctx.params;
const { status } = ctx.request.body;
const build = await updateBuild({
@@ -143,13 +189,18 @@ async function main() {
if (!authResponse.body.isOk) {
ctx.throw(401, 'Not allowed.');
}
- const posters = [];
- for (let i = 0; i < props.length; i++) {
- // eslint-disable-next-line no-await-in-loop
- const poster = await generatePoster(buildId, props[i]);
- posters.push(poster);
+
+ if (!authResponse.body.groups.includes(GROUP_GENERATE)) {
+ ctx.throw(403, 'User does not have permission to generate posters.');
+ } else {
+ const posters = [];
+ for (let i = 0; i < props.length; i++) {
+ // eslint-disable-next-line no-await-in-loop
+ const poster = await generatePoster(buildId, props[i]);
+ posters.push(poster);
+ }
+ ctx.body = posters;
}
- ctx.body = posters;
});
router.post('/cancelPoster', async (ctx) => {
diff --git a/scripts/worker.js b/scripts/worker.js
index 46175a6f..1c7774a9 100644
--- a/scripts/worker.js
+++ b/scripts/worker.js
@@ -89,11 +89,14 @@ async function renderComponent(options) {
timeout: 5 * 60000,
};
- const contents = await page.pdf(printOptions);
-
- await fs.outputFile(pdfPath(id), contents);
- await page.close();
- await uploadPosterToCloud(pdfPath(id));
+ try {
+ const contents = await page.pdf(printOptions);
+ await fs.outputFile(pdfPath(id), contents);
+ await page.close();
+ await uploadPosterToCloud(pdfPath(id));
+ } catch (e) {
+ throw new Error('PDF Generation failed', e);
+ }
}
async function renderComponentRetry(options) {
diff --git a/src/components/labelPlacement/costFunctions.js b/src/components/labelPlacement/costFunctions.js
index c1857467..4009ba84 100644
--- a/src/components/labelPlacement/costFunctions.js
+++ b/src/components/labelPlacement/costFunctions.js
@@ -5,7 +5,7 @@ const OVERLAP_COST_FIXED = 6;
const OVERFLOW_COST = 10000;
const INTERSECTION_COST = 700;
const INTERSECTION_WITH_FIXED_COST = 25;
-const DISTANCE_COST = 120;
+const DISTANCE_COST = 70;
const ANGLE_COST = 1;
const ALPHA_COST = 25;
@@ -80,7 +80,7 @@ function getOverlapArea(a, b) {
function getPositionOverlapCost(positions, indexes, position) {
let overlap = 0;
- indexes.forEach(j => {
+ indexes.forEach((j) => {
if (positions[j].allowCollision || (!positions[j].shouldBeVisible && positions[j].allowHidden))
return;
else if (j === position.index) return;
@@ -99,7 +99,7 @@ function getPositionOverlapCost(positions, indexes, position) {
*/
function getOverlapCost(positions, indexes, closeByPositions) {
let overlap = 0;
- closeByPositions.forEach(position => {
+ closeByPositions.forEach((position) => {
if ((position.shouldBeVisible || !position.allowHidden) && !position.allowCollision) {
overlap += getPositionOverlapCost(positions, indexes, position);
}
@@ -125,9 +125,9 @@ function hasIntersectingLines(a, b) {
*/
function getIntersectionCost(positions, indexes, closeByPositions) {
let sum = 0;
- closeByPositions.forEach(position => {
+ closeByPositions.forEach((position) => {
if (position.isFixed) return;
- indexes.forEach(j => {
+ indexes.forEach((j) => {
if (positions[j].isFixed) return;
if (j >= position.index && indexes.includes(position.index)) return;
if (hasIntersectingLines(position, positions[j])) sum += 1;
@@ -140,7 +140,7 @@ function getPositionFixedIntersectionCost(positions, indexes, index) {
let sum = 0;
const position = positions[index];
if (position.allowCollision) return sum;
- indexes.forEach(j => {
+ indexes.forEach((j) => {
if (positions[j].allowCollision) return;
if (j >= index && indexes.includes(index)) return;
// If both are dynamic or fixed, return
@@ -161,7 +161,7 @@ function getPositionFixedIntersectionCost(positions, indexes, index) {
const p3 = segseg(a0, a1, bl, br);
const p4 = segseg(a0, a1, tr, br);
- const intersections = [p1, p2, p3, p4].filter(p => Array.isArray(p));
+ const intersections = [p1, p2, p3, p4].filter((p) => Array.isArray(p));
if (intersections.length === 2) {
const dx = intersections[0][0] - intersections[1][0];
@@ -197,7 +197,7 @@ function getDistanceCost(positions, indexes) {
return (
DISTANCE_COST *
indexes
- .filter(index => !positions[index].isFixed)
+ .filter((index) => !positions[index].isFixed)
.reduce(
(prev, index) =>
prev +
@@ -220,7 +220,7 @@ function getAngleCost(positions, indexes) {
return (
ANGLE_COST *
indexes
- .filter(index => !positions[index].isFixed)
+ .filter((index) => !positions[index].isFixed)
.reduce((prev, index) => {
const phi = Math.abs(positions[index].angle - positions[index].initialAngle) % 180;
return prev + (phi > 90 ? 180 - phi : phi) * positions[index].anglePriority;
diff --git a/src/components/labelPlacement/itemFixed.js b/src/components/labelPlacement/itemFixed.js
index c6d561b0..dabe87ba 100644
--- a/src/components/labelPlacement/itemFixed.js
+++ b/src/components/labelPlacement/itemFixed.js
@@ -6,6 +6,7 @@ class ItemFixed extends Component {
super(props);
this.state = { top: props.top, left: props.left };
this.visible = true;
+ this.root = React.createRef();
}
setPosition(top, left) {
@@ -46,10 +47,11 @@ class ItemFixed extends Component {
return (
{
+ ref={(ref) => {
this.root = ref;
}}
- style={style}>
+ style={style}
+ >
{this.props.children}
);
diff --git a/src/components/labelPlacement/itemPositioned.js b/src/components/labelPlacement/itemPositioned.js
index 68eb98e7..236ea9dc 100644
--- a/src/components/labelPlacement/itemPositioned.js
+++ b/src/components/labelPlacement/itemPositioned.js
@@ -10,6 +10,7 @@ class ItemPositioned extends Component {
left: props.x,
visible: props.visible || !props.allowHidden,
};
+ this.root = React.createRef();
}
setPosition(top, left, visible) {
@@ -59,10 +60,11 @@ class ItemPositioned extends Component {
if (this.state.visible) {
return (
{
+ ref={(ref) => {
this.root = ref;
}}
- style={style}>
+ style={style}
+ >
{this.props.children}
);
diff --git a/src/components/labelPlacement/optimizePositions.js b/src/components/labelPlacement/optimizePositions.js
index 0236985e..25c57571 100644
--- a/src/components/labelPlacement/optimizePositions.js
+++ b/src/components/labelPlacement/optimizePositions.js
@@ -13,19 +13,20 @@ import {
getAlphaOverflowCost,
shouldBeVisible,
} from './costFunctions';
+import { head } from 'lodash';
const timeout = 7 * 24 * 60 * 60 * 1000;
-const iterationsPerFactor = 8;
+const iterationsPerFactor = 10;
const angles = [-6, -3, -1, 0, 1, 3, 6];
const distances = [-4, -2, -1, 0, 1, 2, 4];
const factors = [30, 15, 7, 3];
-const diffsArray = factors.map(factor =>
+const diffsArray = factors.map((factor) =>
angles.reduce(
(prev, angle) => [
...prev,
- ...distances.map(distance => ({ angle: angle * factor, distance: distance * factor })),
+ ...distances.map((distance) => ({ angle: angle * factor, distance: distance * factor })),
],
[],
),
@@ -43,9 +44,9 @@ function getCloseByPositions(positions, index, maxDistance) {
...pos,
index: i,
}))
- .filter(pos => !pos.allowCollision)
- .filter(pos => pos.shouldBeVisible || !pos.allowHidden)
- .filter(pos => {
+ .filter((pos) => !pos.allowCollision)
+ .filter((pos) => pos.shouldBeVisible || !pos.allowHidden)
+ .filter((pos) => {
if (!pos.allowHidden) return true;
if (getDistance(pos, positions[index]) < maxDistance * 3) {
return true;
@@ -88,7 +89,7 @@ function getPlacements(placement, index, diffs, bbox, alphaByteArray, configurat
const { positions, indexes } = placement;
return diffs
- .map(diff => {
+ .map((diff) => {
const updatedPosition = updatePosition(positions[index], diff);
if (updatedPosition) {
@@ -108,8 +109,8 @@ function getPlacements(placement, index, diffs, bbox, alphaByteArray, configurat
}
return positions.map((position, i) => (i === index ? updatedPosition : position));
})
- .filter(updatedPositions => !!updatedPositions)
- .map(updatedPositions => ({ positions: updatedPositions, indexes: [...indexes, index] }));
+ .filter((updatedPositions) => !!updatedPositions)
+ .map((updatedPositions) => ({ positions: updatedPositions, indexes: [...indexes, index] }));
}
function comparePlacements(placement, other, bbox, alphaByteArray, closeByPositions) {
@@ -147,11 +148,9 @@ function getNextPlacement(initialPlacement, index, diffs, bbox, alphaByteArray,
];
}, []);
- const nextPlacement = [
- initialPlacement,
- ...placements,
- ...placementsOverlapping,
- ].reduce((prev, cur) => comparePlacements(prev, cur, bbox, alphaByteArray, closeByPositions));
+ const nextPlacement = [initialPlacement, ...placements, ...placementsOverlapping].reduce(
+ (prev, cur) => comparePlacements(prev, cur, bbox, alphaByteArray, closeByPositions),
+ );
return nextPlacement;
}
@@ -186,18 +185,29 @@ function findMostSuitablePosition(initialPlacement, bbox, isOccupied, configurat
function optimizePositions(initialPositions, bbox, alphaByteArray, mapOptions, configuration) {
const placement = {
- positions: initialPositions.map(position => updatePosition(position)),
+ positions: initialPositions.map((position) => updatePosition(position)),
indexes: [],
};
+ const defaultDPI = 300;
+ const marginMm = 6;
+ const bboxPrintMargin = Math.round((marginMm * defaultDPI) / 25.4);
+
+ const bboxWithMargins = {
+ left: bbox.left + bboxPrintMargin,
+ top: bbox.top + bboxPrintMargin,
+ width: bbox.width - bboxPrintMargin * 2,
+ height: bbox.height - bboxPrintMargin * 2,
+ };
+
const isOccupied = getIfOccupied(alphaByteArray, mapOptions);
- const positions = findMostSuitablePosition(placement, bbox, isOccupied, configuration);
+ const positions = findMostSuitablePosition(placement, bboxWithMargins, isOccupied, configuration);
const newPlacements = [];
- positions.forEach(position => {
+ positions.forEach((position) => {
newPlacements.push({
...position,
- visible: shouldBeVisible(position, isOccupied, bbox, configuration, true),
+ visible: shouldBeVisible(position, isOccupied, bboxWithMargins, configuration, true),
});
});
return newPlacements;
diff --git a/src/components/routeMap/routeMap.js b/src/components/routeMap/routeMap.js
index 9826f819..79e65fc7 100644
--- a/src/components/routeMap/routeMap.js
+++ b/src/components/routeMap/routeMap.js
@@ -188,11 +188,16 @@ const RouteMap = (props) => {
))}
{projectedSymbols &&
projectedSymbols.length > 0 &&
- projectedSymbols.map((symbol, index) => (
-
-
-
- ))}
+ projectedSymbols.map((symbol, index) => {
+ const matchValues = symbol.size.match(/\d+/);
+ const symbolSizeNumber = matchValues ? parseInt(matchValues[0], 10) : null;
+ const offset = symbolSizeNumber ? symbolSizeNumber / 2 : 0;
+ return (
+
+
+
+ );
+ })}
{props.projectedTerminuses.map((terminus, index) => (
WrappedComponent =>
- class PromiseWrapper extends Component {
- constructor(props) {
- super(props);
- this.state = { loading: !!props[propName] };
- }
-
- componentDidMount() {
- if (this.props[propName]) {
- this.handlePromise(this.props[propName]);
- }
- }
+const hocFactory = (propName) => (WrappedComponent) => (props) => {
+ const [state, setState] = useState({
+ loading: !!props[propName],
+ value: null,
+ error: null,
+ });
- componentWillReceiveProps(nextProps) {
- if (nextProps[propName] && nextProps[propName] !== this.props[propName]) {
- this.setState({ loading: true });
- this.handlePromise(nextProps[propName]);
- }
- }
-
- componentWillUnmount() {
- this.promise = null;
- }
+ useEffect(() => {
+ let isMounted = true;
+ const promise = props[propName];
- handlePromise(promise) {
- this.promise = promise;
+ if (promise) {
+ setState((prevState) => ({ ...prevState, loading: true }));
renderQueue.add(promise);
+
promise
- .then(value => {
- const callback = () => renderQueue.remove(promise);
- if (this.promise !== promise) {
- callback();
- } else {
- this.setState({ value, loading: false }, callback);
+ .then((value) => {
+ if (isMounted) {
+ setState({ value, loading: false, error: null });
+ renderQueue.remove(promise);
}
})
- .catch(error => {
- const callback = () => renderQueue.remove(promise, { error });
- if (this.promise !== promise) {
- callback();
- } else {
- this.setState({ error, loading: false }, callback);
+ .catch((error) => {
+ if (isMounted) {
+ setState({ error, loading: false });
+ renderQueue.remove(promise, { error });
}
});
}
- render() {
- if (this.state.loading || this.state.error) {
- return null;
- }
- const props = { ...this.props, [propName]: this.state.value };
- return ;
- }
- };
+ return () => {
+ isMounted = false;
+ renderQueue.remove(promise);
+ };
+ }, [props[propName]]);
+
+ const { loading, error, value } = state;
+
+ if (loading || error) {
+ return null;
+ }
+
+ const newProps = { ...props, [propName]: value };
+ return ;
+};
export default hocFactory;
diff --git a/yarn.lock b/yarn.lock
index c8cfdf89..8c4ab840 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4980,8 +4980,8 @@ hpack.js@^2.1.6:
wbuf "^1.1.0"
hsl-map-style@hsldevcom/hsl-map-style#master:
- version "1.1.2"
- resolved "https://codeload.github.com/hsldevcom/hsl-map-style/tar.gz/207a8de2664c9a50b5a5f886d30f5b1628f995c7"
+ version "1.2.0"
+ resolved "https://codeload.github.com/hsldevcom/hsl-map-style/tar.gz/68b1642ce364ebcea115bc3f20465d3c2ee83c0d"
dependencies:
lodash "^4.17.4"
@@ -6254,9 +6254,9 @@ lru-cache@^6.0.0:
yallist "^4.0.0"
luxon@^3.0.1:
- version "3.0.3"
- resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.0.3.tgz#573e65531efd3d92265feb640f02ba7a192e2388"
- integrity sha512-+EfHWnF+UT7GgTnq5zXg3ldnTKL2zdv7QJgsU5bjjpbH17E3qi/puMhQyJVYuCq+FRkogvB5WB6iVvUr+E4a7w==
+ version "3.7.1"
+ resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.7.1.tgz#9bd09aa84a56afb00c57ea78a8ec5bd16eb24ec0"
+ integrity sha512-RkRWjA926cTvz5rAb1BqyWkKbbjzCGchDUIKMCUvNi17j6f6j8uHGDV82Aqcqtzd+icoYpELmG3ksgGiFNNcNg==
make-dir@^1.0.0:
version "1.3.0"
@@ -7475,7 +7475,7 @@ prompt@0.2.14:
utile "0.2.x"
winston "0.8.x"
-prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1:
+prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.7.2, prop-types@^15.8.1:
version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@@ -7687,15 +7687,13 @@ react-apollo@^2.0.1:
ts-invariant "^0.4.2"
tslib "^1.9.3"
-react-dom@16.8.6:
- version "16.8.6"
- resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.6.tgz#71d6303f631e8b0097f56165ef608f051ff6e10f"
- integrity sha512-1nL7PIq9LTL3fthPqwkvr2zY7phIPjYrT0jp4HjyEQrEROnw4dG41VVwi/wfoCneoleqrNX7iAD+pXebJZwrwA==
+react-dom@18.2.0:
+ version "18.2.0"
+ resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
+ integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
dependencies:
loose-envify "^1.1.0"
- object-assign "^4.1.1"
- prop-types "^15.6.2"
- scheduler "^0.13.6"
+ scheduler "^0.23.0"
react-hot-loader@^4.13.1:
version "4.13.1"
@@ -7721,15 +7719,12 @@ react-lifecycles-compat@^3.0.2, react-lifecycles-compat@^3.0.4:
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
-react@16.8.6:
- version "16.8.6"
- resolved "https://registry.yarnpkg.com/react/-/react-16.8.6.tgz#ad6c3a9614fd3a4e9ef51117f54d888da01f2bbe"
- integrity sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw==
+react@18.2.0:
+ version "18.2.0"
+ resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
+ integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
dependencies:
loose-envify "^1.1.0"
- object-assign "^4.1.1"
- prop-types "^15.6.2"
- scheduler "^0.13.6"
read@1.0.x:
version "1.0.7"
@@ -8149,13 +8144,12 @@ sax@>=0.6.0:
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
-scheduler@^0.13.6:
- version "0.13.6"
- resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.6.tgz#466a4ec332467b31a91b9bf74e5347072e4cd889"
- integrity sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==
+scheduler@^0.23.0:
+ version "0.23.0"
+ resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
+ integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==
dependencies:
loose-envify "^1.1.0"
- object-assign "^4.1.1"
schema-utils@^1.0.0:
version "1.0.0"
@@ -9408,6 +9402,11 @@ uuid@^9.0.0:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5"
integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==
+validator@^13.15.0:
+ version "13.15.0"
+ resolved "https://registry.yarnpkg.com/validator/-/validator-13.15.0.tgz#2dc7ce057e7513a55585109eec29b2c8e8c1aefd"
+ integrity sha512-36B2ryl4+oL5QxZ3AzD0t5SsMNGvTtQHpjgFO5tbNxfXbMFkY822ktCDe1MnlqV3301QQI9SLHDNJokDI+Z9pA==
+
vary@^1.1.2, vary@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"