mirror of
https://github.com/Unleash/unleash.git
synced 2025-04-19 01:17:18 +02:00
fix: Upgrade to @hapi/joi (#543)
This commit is contained in:
parent
4089a40fa3
commit
b532c86695
@ -1,5 +1,9 @@
|
||||
# Changelog
|
||||
|
||||
## 3.2.23
|
||||
|
||||
- fix: upgrade to @hapi/joi to version 16.1.8
|
||||
|
||||
## 3.2.22
|
||||
|
||||
- fix: add appName as label in usage metrics
|
||||
|
@ -1,13 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
class ValidationError extends Error {
|
||||
constructor(message) {
|
||||
super();
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
|
||||
this.name = this.constructor.name;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ValidationError;
|
@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const joi = require('joi');
|
||||
const joi = require('@hapi/joi');
|
||||
const { nameType } = require('./util');
|
||||
|
||||
const nameSchema = joi.object().keys({ name: nameType });
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
const test = require('ava');
|
||||
const { featureShema } = require('./feature-schema');
|
||||
const joi = require('joi');
|
||||
|
||||
test('should require URL firendly name', t => {
|
||||
const toggle = {
|
||||
@ -11,7 +10,7 @@ test('should require URL firendly name', t => {
|
||||
strategies: [{ name: 'default' }],
|
||||
};
|
||||
|
||||
const { error } = joi.validate(toggle, featureShema);
|
||||
const { error } = featureShema.validate(toggle);
|
||||
t.deepEqual(error.details[0].message, '"name" must be URL friendly');
|
||||
});
|
||||
|
||||
@ -22,7 +21,7 @@ test('should be valid toggle name', t => {
|
||||
strategies: [{ name: 'default' }],
|
||||
};
|
||||
|
||||
const { value } = joi.validate(toggle, featureShema);
|
||||
const { value } = featureShema.validate(toggle);
|
||||
t.deepEqual(value, toggle);
|
||||
});
|
||||
|
||||
@ -40,7 +39,7 @@ test('should strip extra variant fields', t => {
|
||||
],
|
||||
};
|
||||
|
||||
const { value } = joi.validate(toggle, featureShema);
|
||||
const { value } = featureShema.validate(toggle);
|
||||
t.notDeepEqual(value, toggle);
|
||||
t.falsy(value.variants[0].unkown);
|
||||
});
|
||||
@ -64,7 +63,7 @@ test('should be possible to define variant overrides', t => {
|
||||
],
|
||||
};
|
||||
|
||||
const { value, error } = joi.validate(toggle, featureShema);
|
||||
const { value, error } = featureShema.validate(toggle);
|
||||
t.deepEqual(value, toggle);
|
||||
t.falsy(error);
|
||||
});
|
||||
@ -88,9 +87,12 @@ test('variant overrides must have corect shape', async t => {
|
||||
};
|
||||
|
||||
try {
|
||||
await joi.validate(toggle, featureShema);
|
||||
await featureShema.validateAsync(toggle);
|
||||
} catch (error) {
|
||||
t.is(error.details[0].message, '"overrides" must be an array');
|
||||
t.is(
|
||||
error.details[0].message,
|
||||
'"variants[0].overrides" must be an array'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@ -112,7 +114,7 @@ test('should keep constraints', t => {
|
||||
],
|
||||
};
|
||||
|
||||
const { value, error } = joi.validate(toggle, featureShema);
|
||||
const { value, error } = featureShema.validate(toggle);
|
||||
t.deepEqual(value, toggle);
|
||||
t.falsy(error);
|
||||
});
|
||||
|
@ -1,7 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const Controller = require('../controller');
|
||||
const joi = require('joi');
|
||||
|
||||
const {
|
||||
FEATURE_CREATED,
|
||||
@ -56,7 +55,7 @@ class FeatureController extends Controller {
|
||||
const name = req.body.name;
|
||||
|
||||
try {
|
||||
await joi.validate({ name }, nameSchema);
|
||||
await nameSchema.validateAsync({ name });
|
||||
await this.validateUniqueName(name);
|
||||
res.status(201).end();
|
||||
} catch (error) {
|
||||
@ -87,11 +86,11 @@ class FeatureController extends Controller {
|
||||
|
||||
try {
|
||||
await this.validateUniqueName(toggleName);
|
||||
const featureToggle = await joi.validate(req.body, featureShema);
|
||||
const value = await featureShema.validateAsync(req.body);
|
||||
await this.eventStore.store({
|
||||
type: FEATURE_CREATED,
|
||||
createdBy: userName,
|
||||
data: featureToggle,
|
||||
data: value,
|
||||
});
|
||||
res.status(201).end();
|
||||
} catch (error) {
|
||||
@ -108,7 +107,7 @@ class FeatureController extends Controller {
|
||||
|
||||
try {
|
||||
await this.featureToggleStore.getFeature(featureName);
|
||||
await joi.validate(updatedFeature, featureShema);
|
||||
await featureShema.validateAsync(updatedFeature);
|
||||
await this.eventStore.store({
|
||||
type: FEATURE_UPDATED,
|
||||
createdBy: userName,
|
||||
|
@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const joi = require('joi');
|
||||
const joi = require('@hapi/joi');
|
||||
const { nameType } = require('./util');
|
||||
|
||||
const applicationSchema = joi
|
||||
|
@ -1,6 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
const joi = require('joi');
|
||||
const Controller = require('../controller');
|
||||
const ClientMetrics = require('../../client-metrics');
|
||||
const schema = require('./metrics-schema');
|
||||
@ -80,7 +79,7 @@ class MetricsController extends Controller {
|
||||
const input = Object.assign({}, req.body, {
|
||||
appName: req.params.appName,
|
||||
});
|
||||
const { value: applicationData, error } = joi.validate(input, schema);
|
||||
const { value: applicationData, error } = schema.validate(input);
|
||||
|
||||
if (error) {
|
||||
this.logger.warn('Invalid application data posted', error);
|
||||
|
@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const joi = require('joi');
|
||||
const joi = require('@hapi/joi');
|
||||
const { nameType } = require('./util');
|
||||
|
||||
const strategySchema = joi.object().keys({
|
||||
|
@ -1,7 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const Controller = require('../controller');
|
||||
const joi = require('joi');
|
||||
|
||||
const eventType = require('../../event-type');
|
||||
const NameExistsError = require('../../error/name-exists-error');
|
||||
@ -65,12 +64,12 @@ class StrategyController extends Controller {
|
||||
|
||||
async createStrategy(req, res) {
|
||||
try {
|
||||
const newStrategy = await joi.validate(req.body, strategySchema);
|
||||
await this._validateStrategyName(newStrategy);
|
||||
const value = await strategySchema.validateAsync(req.body);
|
||||
await this._validateStrategyName(value);
|
||||
await this.eventStore.store({
|
||||
type: eventType.STRATEGY_CREATED,
|
||||
createdBy: extractUser(req),
|
||||
data: newStrategy,
|
||||
data: value,
|
||||
});
|
||||
res.status(201).end();
|
||||
} catch (error) {
|
||||
@ -82,14 +81,14 @@ class StrategyController extends Controller {
|
||||
const input = req.body;
|
||||
|
||||
try {
|
||||
const updatedStrategy = await joi.validate(input, strategySchema);
|
||||
const value = await strategySchema.validateAsync(input);
|
||||
const strategy = await this.strategyStore.getStrategy(input.name);
|
||||
await this._validateEditable(strategy);
|
||||
|
||||
await this.eventStore.store({
|
||||
type: eventType.STRATEGY_UPDATED,
|
||||
createdBy: extractUser(req),
|
||||
data: updatedStrategy,
|
||||
data: value,
|
||||
});
|
||||
res.status(200).end();
|
||||
} catch (error) {
|
||||
|
@ -59,7 +59,7 @@ test('require a name when creating a new stratey', t => {
|
||||
.send({})
|
||||
.expect(400)
|
||||
.expect(res => {
|
||||
t.true(res.body.name === 'ValidationError');
|
||||
t.true(res.body.details[0].message === '"name" is required');
|
||||
});
|
||||
});
|
||||
|
||||
@ -73,7 +73,10 @@ test('require parameters array when creating a new stratey', t => {
|
||||
.send({ name: 'TestStrat' })
|
||||
.expect(400)
|
||||
.expect(res => {
|
||||
t.true(res.body.name === 'ValidationError');
|
||||
t.deepEqual(
|
||||
res.body.details[0].message,
|
||||
'"parameters" is required'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1,34 +1,23 @@
|
||||
'use strict';
|
||||
|
||||
const joi = require('joi');
|
||||
const joi = require('@hapi/joi');
|
||||
|
||||
const customJoi = joi.extend(j => ({
|
||||
type: 'isUrlFriendly',
|
||||
base: j.string(),
|
||||
name: 'string',
|
||||
language: {
|
||||
isUrlFriendly: 'must be URL friendly',
|
||||
messages: {
|
||||
'isUrlFriendly.base': '"{{#label}}" must be URL friendly',
|
||||
},
|
||||
validate(value, helpers) {
|
||||
// Base validation regardless of the rules applied
|
||||
if (encodeURIComponent(value) !== value) {
|
||||
// Generate an error, state and options need to be passed
|
||||
return { value, errors: helpers.error('isUrlFriendly.base') };
|
||||
}
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
name: 'isUrlFriendly',
|
||||
validate(params, value, state, options) {
|
||||
if (encodeURIComponent(value) !== value) {
|
||||
// Generate an error, state and options need to be passed
|
||||
return this.createError(
|
||||
'string.isUrlFriendly',
|
||||
{ v: value },
|
||||
state,
|
||||
options
|
||||
);
|
||||
}
|
||||
return value; // Everything is OK
|
||||
},
|
||||
},
|
||||
],
|
||||
}));
|
||||
|
||||
const nameType = customJoi
|
||||
.string()
|
||||
.isUrlFriendly()
|
||||
.min(2)
|
||||
.max(100)
|
||||
@ -41,6 +30,7 @@ const handleErrors = (res, logger, error) => {
|
||||
return res.status(404).end();
|
||||
case 'NameExistsError':
|
||||
case 'ValidationError':
|
||||
error.isJoi = true;
|
||||
return res
|
||||
.status(400)
|
||||
.json(error)
|
||||
|
@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const joi = require('joi');
|
||||
const joi = require('@hapi/joi');
|
||||
|
||||
const countSchema = joi
|
||||
.object()
|
||||
|
@ -1,7 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
const joi = require('joi');
|
||||
|
||||
const Controller = require('../controller');
|
||||
const { clientMetricsSchema } = require('./metrics-schema');
|
||||
|
||||
@ -19,7 +17,7 @@ class ClientMetricsController extends Controller {
|
||||
const data = req.body;
|
||||
const clientIp = req.ip;
|
||||
|
||||
const { error, value } = joi.validate(data, clientMetricsSchema);
|
||||
const { error, value } = clientMetricsSchema.validate(data);
|
||||
|
||||
if (error) {
|
||||
this.logger.warn('Invalid metrics posted', error);
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
const test = require('ava');
|
||||
const supertest = require('supertest');
|
||||
const joi = require('joi');
|
||||
const store = require('./../../../test/fixtures/store');
|
||||
const getLogger = require('../../../test/fixtures/no-logger');
|
||||
const getApp = require('../../app');
|
||||
@ -132,7 +131,7 @@ test('shema allow empty strings', t => {
|
||||
stop: '2019-05-06T09:30:50.515Z',
|
||||
},
|
||||
};
|
||||
const { error, value } = joi.validate(data, clientMetricsSchema);
|
||||
const { error, value } = clientMetricsSchema.validate(data);
|
||||
t.falsy(error);
|
||||
t.is(value.bucket.toggles.Demo2.yes, 0);
|
||||
t.is(value.bucket.toggles.Demo2.no, 0);
|
||||
@ -148,7 +147,7 @@ test('shema allow yes=<string nbr>', t => {
|
||||
stop: '2019-05-06T09:30:50.515Z',
|
||||
},
|
||||
};
|
||||
const { error, value } = joi.validate(data, clientMetricsSchema);
|
||||
const { error, value } = clientMetricsSchema.validate(data);
|
||||
t.falsy(error);
|
||||
t.is(value.bucket.toggles.Demo2.yes, 12);
|
||||
t.is(value.bucket.toggles.Demo2.no, 256);
|
||||
|
@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const joi = require('joi');
|
||||
const joi = require('@hapi/joi');
|
||||
|
||||
const clientRegisterSchema = joi
|
||||
.object()
|
||||
|
@ -1,7 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
const joi = require('joi');
|
||||
|
||||
const Controller = require('../controller');
|
||||
const { clientRegisterSchema: schema } = require('./register-schema');
|
||||
|
||||
@ -17,19 +15,19 @@ class RegisterController extends Controller {
|
||||
|
||||
async handleRegister(req, res) {
|
||||
const data = req.body;
|
||||
const { value: clientRegistration, error } = joi.validate(data, schema);
|
||||
const { value, error } = schema.validate(data);
|
||||
|
||||
if (error) {
|
||||
this.logger.warn('Invalid client data posted', error);
|
||||
return res.status(400).json(error);
|
||||
}
|
||||
|
||||
clientRegistration.clientIp = req.ip;
|
||||
value.clientIp = req.ip;
|
||||
|
||||
try {
|
||||
await this.clientApplicationsStore.upsert(clientRegistration);
|
||||
await this.clientInstanceStore.insert(clientRegistration);
|
||||
const { appName, instanceId } = clientRegistration;
|
||||
await this.clientApplicationsStore.upsert(value);
|
||||
await this.clientInstanceStore.insert(value);
|
||||
const { appName, instanceId } = value;
|
||||
this.logger.info(
|
||||
`New client registration: appName=${appName}, instanceId=${instanceId}`
|
||||
);
|
||||
|
@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const joi = require('joi');
|
||||
const joi = require('@hapi/joi');
|
||||
const fs = require('fs');
|
||||
const mime = require('mime');
|
||||
const { featureShema } = require('./routes/admin-api/feature-schema');
|
||||
@ -52,7 +52,7 @@ class StateService {
|
||||
async import({ data, userName, dropBeforeImport }) {
|
||||
const { eventStore } = this.config.stores;
|
||||
|
||||
const importData = await joi.validate(data, dataSchema);
|
||||
const importData = await dataSchema.validateAsync(data);
|
||||
|
||||
if (importData.features) {
|
||||
this.logger.info(
|
||||
|
@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const gravatar = require('gravatar');
|
||||
const Joi = require('joi');
|
||||
const Joi = require('@hapi/joi');
|
||||
|
||||
module.exports = class User {
|
||||
constructor({ name, email, permissions, imageUrl } = {}) {
|
||||
|
@ -72,7 +72,7 @@
|
||||
"errorhandler": "^1.5.1",
|
||||
"express": "^4.17.1",
|
||||
"gravatar": "^1.8.0",
|
||||
"joi": "^14.3.1",
|
||||
"@hapi/joi": "^16.1.8",
|
||||
"js-yaml": "^3.12.2",
|
||||
"knex": "^0.20.0",
|
||||
"log4js": "^6.0.0",
|
||||
|
70
yarn.lock
70
yarn.lock
@ -439,6 +439,44 @@
|
||||
dependencies:
|
||||
arrify "^1.0.1"
|
||||
|
||||
"@hapi/address@^2.1.2":
|
||||
version "2.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5"
|
||||
integrity sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ==
|
||||
|
||||
"@hapi/formula@^1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/formula/-/formula-1.2.0.tgz#994649c7fea1a90b91a0a1e6d983523f680e10cd"
|
||||
integrity sha512-UFbtbGPjstz0eWHb+ga/GM3Z9EzqKXFWIbSOFURU0A/Gku0Bky4bCk9/h//K2Xr3IrCfjFNhMm4jyZ5dbCewGA==
|
||||
|
||||
"@hapi/hoek@^8.2.4", "@hapi/hoek@^8.3.0":
|
||||
version "8.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.5.0.tgz#2f9ce301c8898e1c3248b0a8564696b24d1a9a5a"
|
||||
integrity sha512-7XYT10CZfPsH7j9F1Jmg1+d0ezOux2oM2GfArAzLwWe4mE2Dr3hVjsAL6+TFY49RRJlCdJDMw3nJsLFroTc8Kw==
|
||||
|
||||
"@hapi/joi@^16.1.8":
|
||||
version "16.1.8"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/joi/-/joi-16.1.8.tgz#84c1f126269489871ad4e2decc786e0adef06839"
|
||||
integrity sha512-wAsVvTPe+FwSrsAurNt5vkg3zo+TblvC5Bb1zMVK6SJzZqw9UrJnexxR+76cpePmtUZKHAPxcQ2Bf7oVHyahhg==
|
||||
dependencies:
|
||||
"@hapi/address" "^2.1.2"
|
||||
"@hapi/formula" "^1.2.0"
|
||||
"@hapi/hoek" "^8.2.4"
|
||||
"@hapi/pinpoint" "^1.0.2"
|
||||
"@hapi/topo" "^3.1.3"
|
||||
|
||||
"@hapi/pinpoint@^1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/pinpoint/-/pinpoint-1.0.2.tgz#025b7a36dbbf4d35bf1acd071c26b20ef41e0d13"
|
||||
integrity sha512-dtXC/WkZBfC5vxscazuiJ6iq4j9oNx1SHknmIr8hofarpKUZKmlUVYVIhNVzIEgK5Wrc4GMHL5lZtt1uS2flmQ==
|
||||
|
||||
"@hapi/topo@^3.1.3":
|
||||
version "3.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-3.1.6.tgz#68d935fa3eae7fdd5ab0d7f953f3205d8b2bfc29"
|
||||
integrity sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ==
|
||||
dependencies:
|
||||
"@hapi/hoek" "^8.3.0"
|
||||
|
||||
"@istanbuljs/load-nyc-config@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz#10602de5570baea82f8afbfa2630b24e7a8cfe5b"
|
||||
@ -2801,10 +2839,6 @@ hasha@^5.0.0:
|
||||
is-stream "^1.1.0"
|
||||
type-fest "^0.3.0"
|
||||
|
||||
hoek@6.x.x:
|
||||
version "6.1.2"
|
||||
resolved "https://registry.yarnpkg.com/hoek/-/hoek-6.1.2.tgz#99e6d070561839de74ee427b61aa476bd6bddfd6"
|
||||
|
||||
homedir-polyfill@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz#4c2bbc8a758998feebf5ed68580f76d46768b4bc"
|
||||
@ -3253,12 +3287,6 @@ isarray@1.0.0, isarray@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
||||
|
||||
isemail@3.x.x:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/isemail/-/isemail-3.2.0.tgz#59310a021931a9fb06bbb51e155ce0b3f236832c"
|
||||
dependencies:
|
||||
punycode "2.x.x"
|
||||
|
||||
isexe@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
||||
@ -3350,14 +3378,6 @@ jest-docblock@^21.0.0:
|
||||
version "21.2.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-21.2.0.tgz#51529c3b30d5fd159da60c27ceedc195faf8d414"
|
||||
|
||||
joi@^14.3.1:
|
||||
version "14.3.1"
|
||||
resolved "https://registry.yarnpkg.com/joi/-/joi-14.3.1.tgz#164a262ec0b855466e0c35eea2a885ae8b6c703c"
|
||||
dependencies:
|
||||
hoek "6.x.x"
|
||||
isemail "3.x.x"
|
||||
topo "3.x.x"
|
||||
|
||||
js-string-escape@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef"
|
||||
@ -4853,15 +4873,15 @@ pump@^3.0.0:
|
||||
end-of-stream "^1.1.0"
|
||||
once "^1.3.1"
|
||||
|
||||
punycode@2.x.x, punycode@^2.1.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
||||
|
||||
punycode@^1.4.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
|
||||
integrity sha1-wNWmOycYgArY4esPpSachN1BhF4=
|
||||
|
||||
punycode@^2.1.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
||||
|
||||
qs@6.7.0:
|
||||
version "6.7.0"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
|
||||
@ -5879,12 +5899,6 @@ toidentifier@1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
|
||||
integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
|
||||
|
||||
topo@3.x.x:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/topo/-/topo-3.0.3.tgz#d5a67fb2e69307ebeeb08402ec2a2a6f5f7ad95c"
|
||||
dependencies:
|
||||
hoek "6.x.x"
|
||||
|
||||
tough-cookie@~2.4.3:
|
||||
version "2.4.3"
|
||||
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781"
|
||||
|
Loading…
Reference in New Issue
Block a user