支持彩色编译信息输出

This commit is contained in:
undefined committed 2020-06-18 13:14:39 +08:00
1 parent f293e1f59e
commit d040b153a1
18 files changed
+127 -56

No files matched your search

+21 -9
View File
@@ -2,12 +2,13 @@
/* eslint-disable no-eval */ /* eslint-disable no-eval */
const os = require('os'); const os = require('os');
const fs = require('fs'); const fs = require('fs');
const yaml = require('js-yaml');
const builtinLib = [ const builtinLib = [
'axios', 'download', 'i18n', 'mail', 'markdown', 'axios', 'download', 'i18n', 'mail', 'markdown',
'md5', 'misc', 'paginate', 'hash.hydro', 'rank', 'md5', 'misc', 'paginate', 'hash.hydro', 'rank',
'template', 'validator', 'nav', 'sysinfo', 'testdata.convert.ini', 'template', 'validator', 'nav', 'sysinfo', 'testdata.convert.ini',
'readConfig', 'logger', 'readConfig', 'logger', 'useragent',
]; ];
const builtinModel = [ const builtinModel = [
@@ -60,17 +61,28 @@ async function locale(pending, fail) {
} }
} }
async function config(pending, fail, system) { async function setting(pending, fail, modelSetting, modelSystem) {
const map = {
system: modelSetting.SystemSetting,
account: modelSetting.AccountSetting,
preference: modelSetting.PreferenceSetting,
};
for (const i of pending) { for (const i of pending) {
const p = `${os.tmpdir()}/hydro/tmp/${i}/config.js`; const p = `${os.tmpdir()}/hydro/tmp/${i}/setting.yaml`;
if (fs.existsSync(p) && !fail.includes(i)) { if (fs.existsSync(p) && !fail.includes(i)) {
try { try {
global.Hydro.config[i] = eval('require')(p); const cfg = yaml.safeLoad(fs.readFileSync(p).toString());
for (const key in global.Hydro.config[i]) { for (const key in cfg) {
if (global.Hydro.config[i][key].default) { if (cfg[key].default && modelSystem) {
const current = await system.get(`${i}.${key}`); const current = await modelSystem.get(`${i}.${key}`);
if (!current) await system.set(`${i}.${key}`, global.Hydro.config[i][key].default); if (!current) await modelSystem.set(`${i}.${key}`, cfg[key].default);
} }
map[cfg[key].category || 'system'](
modelSetting.Setting(
i, `${i}.${key}`, cfg[key].range, cfg[key].default,
cfg[key].type || 'text', cfg[key].name || key, cfg[key].desc || '',
),
);
} }
} catch (e) { } catch (e) {
console.error(`Config Load Fail: ${i}`); console.error(`Config Load Fail: ${i}`);
@@ -177,5 +189,5 @@ module.exports = {
script, script,
service, service,
template, template,
config, setting,
}; };
+6 -5
View File
@@ -4,7 +4,7 @@ const os = require('os');
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const { const {
lib, service, model, config, lib, service, model, setting,
builtinLib, builtinHandler, builtinModel, builtinLib, builtinHandler, builtinModel,
} = require('./common'); } = require('./common');
@@ -61,13 +61,14 @@ async function load(call) {
await global.Hydro.model[m].ensureIndexes(); await global.Hydro.model[m].ensureIndexes();
} }
} }
const system = require('../model/system'); const modelSystem = require('../model/system');
const dbVer = await system.get('db.ver'); const modelSetting = require('../model/setting');
const dbVer = await modelSystem.get('db.ver');
if (dbVer !== 1) { if (dbVer !== 1) {
const ins = require('../script/install'); const ins = require('../script/install');
await ins.run({ username: 'Root', password: 'rootroot' }); await ins.run({ username: 'Root', password: 'rootroot' });
} }
await config(pending, fail); await setting(pending, fail, modelSetting, modelSystem);
for (const i in global.Hydro.service) { for (const i in global.Hydro.service) {
if (global.Hydro.service[i].postInit) { if (global.Hydro.service[i].postInit) {
try { try {
@@ -78,7 +79,7 @@ async function load(call) {
} }
} }
pending = []; pending = [];
return { fail, active }; return await modelSystem.get('server.worker');
} }
module.exports = load; module.exports = load;
+6
View File
@@ -56,6 +56,12 @@ async function load() {
JSON.stringify(m.locale), JSON.stringify(m.locale),
); );
} }
if (m.setting) {
fs.writeFileSync(
`${os.tmpdir()}/hydro/tmp/${id}/setting.yaml`,
m.setting,
);
}
if (m.template) { if (m.template) {
fs.writeFileSync( fs.writeFileSync(
`${os.tmpdir()}/hydro/tmp/${id}/template.json`, `${os.tmpdir()}/hydro/tmp/${id}/template.json`,
+3 -1
View File
@@ -1,7 +1,7 @@
/* eslint-disable no-await-in-loop */ /* eslint-disable no-await-in-loop */
/* eslint-disable import/no-dynamic-require */ /* eslint-disable import/no-dynamic-require */
const { const {
locale, template, lib, service, model, handler, script, locale, template, lib, service, model, handler, script, setting,
builtinLib, builtinScript, builtinHandler, builtinModel, builtinLib, builtinScript, builtinHandler, builtinModel,
} = require('./common'); } = require('./common');
@@ -34,6 +34,8 @@ async function load() {
for (const i of builtinModel) require(`../model/${i}`); for (const i of builtinModel) require(`../model/${i}`);
for (const i of builtinHandler) require(`../handler/${i}`); for (const i of builtinHandler) require(`../handler/${i}`);
await model(pending, fail); await model(pending, fail);
const modelSetting = require('../model/setting');
await setting(pending, fail, modelSetting);
await handler(pending, fail); await handler(pending, fail);
for (const i in global.Hydro.handler) { for (const i in global.Hydro.handler) {
await global.Hydro.handler[i](); await global.Hydro.handler[i]();
+2 -1
View File
@@ -33,7 +33,8 @@ class SystemDashboardHandler extends SystemHandler {
} }
async postRestart() { async postRestart() {
process.send({ event: 'restart' }); const count = await system.get('server.worker');
process.send({ event: 'restart', count });
this.back(); this.back();
} }
} }
+1 -1
View File
@@ -183,7 +183,7 @@ class ProblemSubmitHandler extends ProblemDetailHandler {
user.incDomain(domainId, this.user._id, 'nSubmit'), user.incDomain(domainId, this.user._id, 'nSubmit'),
]); ]);
this.response.body = { rid }; this.response.body = { rid };
this.response.redirect = `/r/${rid}`; this.response.redirect = this.url('record_detail', { rid });
} }
} }
+1 -1
View File
@@ -1,7 +1,7 @@
const crypto = require('crypto'); const crypto = require('crypto');
function hash(password, salt) { function hash(password, salt) {
return crypto.pbkdf2Sync(password, salt, 100000, 64, 'sha256').toString('hex'); return crypto.pbkdf2Sync(password, salt, 100000, 64, 'sha256').toString('hex').substr(0, 64);
} }
global.Hydro.lib['hash.hydro'] = module.exports = hash; global.Hydro.lib['hash.hydro'] = module.exports = hash;
+9 -1
View File
@@ -1,5 +1,13 @@
const ansiup = require('ansi_up');
const md5 = require('./md5'); const md5 = require('./md5');
// eslint-disable-next-line new-cap
const AU = new ansiup.default();
function ansiToHtml(str) {
return AU.ansi_to_html(str);
}
function gravatar(email, s = 32) { function gravatar(email, s = 32) {
return `//gravatar.loli.net/avatar/${md5((email || '').toString().trim().toLowerCase())}?d=mm&s=${s}`; return `//gravatar.loli.net/avatar/${md5((email || '').toString().trim().toLowerCase())}?d=mm&s=${s}`;
} }
@@ -64,5 +72,5 @@ function formatSeconds(seconds = 0) {
} }
global.Hydro.lib.misc = module.exports = { global.Hydro.lib.misc = module.exports = {
gravatar, datetimeSpan, paginate, size, formatSeconds, gravatar, datetimeSpan, paginate, size, formatSeconds, ansiToHtml,
}; };
+1
View File
@@ -23,6 +23,7 @@ class Nunjucks extends nunjucks.Environment {
if (safe) return markdown.safe.render(self); if (safe) return markdown.safe.render(self);
return markdown.unsafe.render(self); return markdown.unsafe.render(self);
}); });
this.addFilter('ansi', (self) => misc.ansiToHtml(self));
this.addFilter('base64_encode', (s) => Buffer.from(s).toString('base64')); this.addFilter('base64_encode', (s) => Buffer.from(s).toString('base64'));
} }
} }
+9
View File
@@ -0,0 +1,9 @@
const browser = require('detect-browser');
function get(str) {
const ua = browser.parseUserAgent(str);
console.log(ua);
return ua;
}
global.Hydro.lib.useragent = module.exports = get;
+12 -11
View File
@@ -2,7 +2,6 @@
/* eslint-disable no-await-in-loop */ /* eslint-disable no-await-in-loop */
/* eslint-disable no-eval */ /* eslint-disable no-eval */
const cluster = require('cluster'); const cluster = require('cluster');
const numCPUs = require('os').cpus().length;
async function terminate() { async function terminate() {
for (const task of global.onDestory) { for (const task of global.onDestory) {
@@ -16,16 +15,17 @@ async function entry(config) {
if (config.entry) { if (config.entry) {
// TODO newProcess // TODO newProcess
const loader = require(`./entry/${config.entry}`); const loader = require(`./entry/${config.entry}`);
await loader(entry); return await loader(entry);
} }
return null;
} }
async function stopWorker() { async function stopWorker() {
cluster.disconnect(); cluster.disconnect();
} }
async function startWorker() { async function startWorker(cnt) {
for (let i = 0; i < numCPUs; i++) { for (let i = 0; i < cnt; i++) {
cluster.fork(); cluster.fork();
} }
} }
@@ -46,7 +46,7 @@ async function messageHandler(worker, msg) {
console.log('Restarting'); console.log('Restarting');
await stopWorker(); await stopWorker();
console.log('Worker stopped'); console.log('Worker stopped');
await startWorker(); await startWorker(msg.count);
} else if (msg.event && msg.event === 'run') { } else if (msg.event && msg.event === 'run') {
await executeCommand(msg.command); await executeCommand(msg.command);
} }
@@ -74,6 +74,7 @@ async function load() {
process.on('unhandledRejection', (e) => console.error(e)); process.on('unhandledRejection', (e) => console.error(e));
process.on('SIGINT', terminate); process.on('SIGINT', terminate);
process.on('message', messageHandler); process.on('message', messageHandler);
cluster.on('message', messageHandler);
if (cluster.isMaster) { if (cluster.isMaster) {
console.log(`Master ${process.pid} Starting`); console.log(`Master ${process.pid} Starting`);
process.stdin.setEncoding('utf8'); process.stdin.setEncoding('utf8');
@@ -84,20 +85,20 @@ async function load() {
executeCommand(input); executeCommand(input);
} }
}); });
await entry({ entry: 'master' }); const cnt = await entry({ entry: 'master' });
cluster.on('exit', (worker, code, signal) => { cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} exit: ${code} ${signal}`); console.log(`Worker ${worker.process.pid} ${worker.id} exit: ${code} ${signal}`);
}); });
cluster.on('disconnect', (worker) => { cluster.on('disconnect', (worker) => {
console.log(`Worker ${worker.process.pid} disconnected`); console.log(`Worker ${worker.process.pid} ${worker.id} disconnected`);
}); });
cluster.on('listening', (worker, address) => { cluster.on('listening', (worker, address) => {
console.log(`Worker ${worker.process.pid} listening at `, address); console.log(`Worker ${worker.process.pid} ${worker.id} listening at `, address);
}); });
cluster.on('online', (worker) => { cluster.on('online', (worker) => {
console.log(`Worker ${worker.process.pid} is online`); console.log(`Worker ${worker.process.pid} ${worker.id} is online`);
}); });
await startWorker(); await startWorker(cnt);
} else { } else {
console.log(`Worker ${process.pid} Starting`); console.log(`Worker ${process.pid} Starting`);
await entry({ entry: 'worker' }); await entry({ entry: 'worker' });
+38 -15
View File
@@ -10,6 +10,13 @@ for (const country of countries) {
} }
const timezones = Array.from(tzs).sort().map((tz) => [tz, tz]); const timezones = Array.from(tzs).sort().map((tz) => [tz, tz]);
const PREFERENCE_SETTINGS = [];
const ACCOUNT_SETTINGS = [];
const SYSTEM_SETTINGS = [];
const SETTINGS = [];
const SETTINGS_BY_KEY = {};
const SYSTEM_SETTINGS_BY_KEY = {};
const Setting = ( const Setting = (
family, key, range = null, family, key, range = null,
value = null, ui = 'text', name = '', value = null, ui = 'text', name = '',
@@ -18,7 +25,28 @@ const Setting = (
family, key, range, value, ui, name, desc, family, key, range, value, ui, name, desc,
}); });
const PREFERENCE_SETTINGS = [ const PreferenceSetting = (...settings) => {
for (const setting of settings) {
PREFERENCE_SETTINGS.push(setting);
SETTINGS.push(setting);
SETTINGS_BY_KEY[setting.key] = setting;
}
};
const AccountSetting = (...settings) => {
for (const setting of settings) {
ACCOUNT_SETTINGS.push(setting);
SETTINGS.push(setting);
SETTINGS_BY_KEY[setting.key] = setting;
}
};
const SystemSetting = (...settings) => {
for (const setting of settings) {
SYSTEM_SETTINGS.push(setting);
SYSTEM_SETTINGS_BY_KEY[setting.key] = setting;
}
};
PreferenceSetting(
Setting('setting_display', 'viewLang', builtin.VIEW_LANGS.map((i) => [i.code, i.name]), Setting('setting_display', 'viewLang', builtin.VIEW_LANGS.map((i) => [i.code, i.name]),
options.default_locale, 'select', 'UI Language'), options.default_locale, 'select', 'UI Language'),
Setting('setting_display', 'timezone', timezones, Setting('setting_display', 'timezone', timezones,
@@ -28,9 +56,9 @@ const PREFERENCE_SETTINGS = [
Setting('setting_usage', 'codeTemplate', null, Setting('setting_usage', 'codeTemplate', null,
null, 'textarea', 'Default Code Template', null, 'textarea', 'Default Code Template',
'If left blank, the built-in template of the corresponding language will be used.'), 'If left blank, the built-in template of the corresponding language will be used.'),
]; );
const ACCOUNT_SETTINGS = [ AccountSetting(
Setting('setting_info', 'gravatar', null, Setting('setting_info', 'gravatar', null,
null, 'text', 'Gravatar Email', null, 'text', 'Gravatar Email',
'We use <a href="https://en.gravatar.com/" target="_blank">Gravatar</a> to present your avatar icon.'), 'We use <a href="https://en.gravatar.com/" target="_blank">Gravatar</a> to present your avatar icon.'),
@@ -43,14 +71,9 @@ const ACCOUNT_SETTINGS = [
Setting('setting_customize', 'backgroundImage', null, Setting('setting_customize', 'backgroundImage', null,
'/components/background/profile/backgrounds/1.jpg', 'text', 'Profile Background Image', '/components/background/profile/backgrounds/1.jpg', 'text', 'Profile Background Image',
'Choose the background image in your profile page.'), 'Choose the background image in your profile page.'),
]; );
const SETTINGS = [...PREFERENCE_SETTINGS, ...ACCOUNT_SETTINGS]; SystemSetting(
const SETTINGS_BY_KEY = {};
for (const setting of SETTINGS) SETTINGS_BY_KEY[setting.key] = setting;
const SYSTEM_SETTINGS = [
Setting('setting_basic', 'user.register', null, false, 'checkbox', 'Allow register'), Setting('setting_basic', 'user.register', null, false, 'checkbox', 'Allow register'),
Setting('setting_basic', 'user.create_domain', null, true, 'checkbox', 'Allow domain creation'), Setting('setting_basic', 'user.create_domain', null, true, 'checkbox', 'Allow domain creation'),
Setting('setting_smtp', 'smtp.user', null, null, 'text', 'SMTP Username'), Setting('setting_smtp', 'smtp.user', null, null, 'text', 'SMTP Username'),
@@ -59,6 +82,7 @@ const SYSTEM_SETTINGS = [
Setting('setting_smtp', 'smtp.port', null, 465, 'number', 'SMTP Server Port'), Setting('setting_smtp', 'smtp.port', null, 465, 'number', 'SMTP Server Port'),
Setting('setting_smtp', 'smtp.from', null, null, 'text', 'Mail From'), Setting('setting_smtp', 'smtp.from', null, null, 'text', 'Mail From'),
Setting('setting_smtp', 'smtp.secure', null, false, 'checkbox', 'SSL'), Setting('setting_smtp', 'smtp.secure', null, false, 'checkbox', 'SSL'),
Setting('setting_server', 'server.worker', null, 1, 'number', 'Server Workers Number'),
Setting('setting_server', 'server.hostname', null, null, 'text', 'Server Hostname'), Setting('setting_server', 'server.hostname', null, null, 'text', 'Server Hostname'),
Setting('setting_server', 'server.host', null, null, 'text', 'Server Host'), Setting('setting_server', 'server.host', null, null, 'text', 'Server Host'),
Setting('setting_server', 'server.url', null, null, 'text', 'Server BaseURL'), Setting('setting_server', 'server.url', null, null, 'text', 'Server BaseURL'),
@@ -70,14 +94,13 @@ const SYSTEM_SETTINGS = [
Setting('setting_constant', 'SOLUTION_PER_PAGE', null, 20, 'number', 'Solutions per Page'), Setting('setting_constant', 'SOLUTION_PER_PAGE', null, 20, 'number', 'Solutions per Page'),
Setting('setting_constant', 'TRAINING_PER_PAGE', null, 10, 'number', 'Training per Page'), Setting('setting_constant', 'TRAINING_PER_PAGE', null, 10, 'number', 'Training per Page'),
Setting('setting_constant', 'REPLY_PER_PAGE', null, 50, 'number', 'Reply per Page'), Setting('setting_constant', 'REPLY_PER_PAGE', null, 50, 'number', 'Reply per Page'),
]; );
const SYSTEM_SETTINGS_BY_KEY = {};
for (const setting of SYSTEM_SETTINGS) SYSTEM_SETTINGS_BY_KEY[setting.key] = setting;
global.Hydro.model.setting = module.exports = { global.Hydro.model.setting = module.exports = {
Setting, Setting,
PreferenceSetting,
AccountSetting,
SystemSetting,
PREFERENCE_SETTINGS, PREFERENCE_SETTINGS,
ACCOUNT_SETTINGS, ACCOUNT_SETTINGS,
SETTINGS, SETTINGS,
-1
View File
@@ -42,7 +42,6 @@ class USER {
checkPassword(password) { checkPassword(password) {
const h = global.Hydro.lib[`hash.${this.hashType || 'hydro'}`]; const h = global.Hydro.lib[`hash.${this.hashType || 'hydro'}`];
if (!h) throw new Error('Unknown hash method'); if (!h) throw new Error('Unknown hash method');
console.log(h(password, this.salt(), this), this.hash());
if (!(h(password, this.salt(), this) === this.hash())) { if (!(h(password, this.salt(), this) === this.hash())) {
throw new LoginError(this.uname); throw new LoginError(this.uname);
} }
-4
View File
@@ -487,13 +487,9 @@ async function start() {
app.use(morgan(':method :url :status :res[content-length] - :response-time ms')); app.use(morgan(':method :url :status :res[content-length] - :response-time ms'));
app.use(router.routes()).use(router.allowedMethods()); app.use(router.routes()).use(router.allowedMethods());
Route('notfound_handler', '*', Handler); Route('notfound_handler', '*', Handler);
if (cluster.worker.id === 1) {
const port = await system.get('server.port'); const port = await system.get('server.port');
server.listen(port); server.listen(port);
console.log('Server listening at: %s', port); console.log('Server listening at: %s', port);
} else {
server.listen();
}
} }
global.Hydro.service.server = module.exports = { global.Hydro.service.server = module.exports = {
+4 -4
View File
@@ -268,10 +268,10 @@ async function domainUser(src, report) {
for (const key in doc) { for (const key in doc) {
if (typeof mapper[key] === 'string') { if (typeof mapper[key] === 'string') {
d[mapper[key]] = doc[key]; d[mapper[key]] = doc[key];
} else if (typeof mapper[key] === 'object') {
d[mapper[key].field] = mapper[key].processer(doc[key]);
} else if (mapper[key] === null) { } else if (mapper[key] === null) {
// Ignore this key // Ignore this key
} else if (typeof mapper[key] === 'object') {
d[mapper[key].field] = mapper[key].processer(doc[key]);
} else { } else {
await report({ message: `Unknown key ${key} in collection domain.user` }); await report({ message: `Unknown key ${key} in collection domain.user` });
} }
@@ -348,10 +348,10 @@ async function task(name, src, report) {
for (const key in doc) { for (const key in doc) {
if (typeof mapper[key] === 'string') { if (typeof mapper[key] === 'string') {
d[mapper[key]] = doc[key]; d[mapper[key]] = doc[key];
} else if (typeof mapper[key] === 'object') {
d[mapper[key].field] = mapper[key].processer(doc[key]);
} else if (mapper[key] === null) { } else if (mapper[key] === null) {
// Ignore this key // Ignore this key
} else if (typeof mapper[key] === 'object') {
d[mapper[key].field] = mapper[key].processer(doc[key]);
} else { } else {
await report({ message: `Unknown key ${key} in collection ${name}` }); await report({ message: `Unknown key ${key} in collection ${name}` });
} }
+2
View File
@@ -7,8 +7,10 @@
"license": "GPL-3.0-only", "license": "GPL-3.0-only",
"dependencies": { "dependencies": {
"adm-zip": "^0.4.14", "adm-zip": "^0.4.14",
"ansi_up": "^4.0.4",
"axios": "^0.19.0", "axios": "^0.19.0",
"bson": "^4.0.2", "bson": "^4.0.2",
"detect-browser": "^5.1.1",
"js-yaml": "^3.13.1", "js-yaml": "^3.13.1",
"koa": "^2.11.0", "koa": "^2.11.0",
"koa-body": "^4.1.1", "koa-body": "^4.1.1",
+2 -2
View File
@@ -9,12 +9,12 @@
</div> </div>
{% if rdoc.compilerTexts.length %} {% if rdoc.compilerTexts.length %}
<div class="section__body"> <div class="section__body">
<pre class="compiler-text">{{ rdoc.compilerTexts|join('\n') }}</pre> <pre class="compiler-text">{{ rdoc.compilerTexts|join('\n')|ansi|safe }}</pre>
</div> </div>
{% endif %} {% endif %}
{% if rdoc.judgeTexts.length %} {% if rdoc.judgeTexts.length %}
<div class="section__body"> <div class="section__body">
<pre class="judge-text">{{ rdoc.judgeTexts|join('\n') }}</pre> <pre class="judge-text">{{ rdoc.judgeTexts|join('\n')|ansi|safe }}</pre>
</div> </div>
{% endif %} {% endif %}
{% if rdoc.testCases.length %} {% if rdoc.testCases.length %}
+10
View File
@@ -316,6 +316,11 @@ ansi-styles@^4.1.0:
"@types/color-name" "^1.1.1" "@types/color-name" "^1.1.1"
color-convert "^2.0.1" color-convert "^2.0.1"
ansi_up@^4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/ansi_up/-/ansi_up-4.0.4.tgz#5b8c35f0b02e4476f3f18cf89c3bf48d15d054f6"
integrity sha512-vRxC8q6QY918MbehO869biJW4tiunJdjOhi5fpY6NLOliBQlZhOkKgABJKJqH+JZfb/WfjvjN1chLWI6tODerw==
any-promise@^1.0.0, any-promise@^1.1.0: any-promise@^1.0.0, any-promise@^1.1.0:
version "1.3.0" version "1.3.0"
resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
@@ -1142,6 +1147,11 @@ destroy@^1.0.4:
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
detect-browser@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/detect-browser/-/detect-browser-5.1.1.tgz#a800db91d3fd60d0861669f5984f1be9ffbe009c"
integrity sha512-5n2aWI57qC3kZaK4j2zYsG6L1LrxgLptGCNhMQgdKhVn6cSdcq43pp6xHPfTHG3TYM6myF4tIPWiZtfdVDgb9w==
diffie-hellman@^5.0.0: diffie-hellman@^5.0.0:
version "5.0.3" version "5.0.3"
resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875"