displaying matching otp codes in entries

This commit is contained in:
antelle 2020-05-11 17:24:06 +02:00
parent 16d303d966
commit c6ce694dfe
No known key found for this signature in database
GPG Key ID: 094A2F2D6136A4EE
15 changed files with 171 additions and 70 deletions

View File

@ -1,5 +1,5 @@
{ {
"plugins": ["prettier", "import"], "plugins": ["prettier", "import", "babel"],
"extends": ["standard", "eslint:recommended", "plugin:prettier/recommended"], "extends": ["standard", "eslint:recommended", "plugin:prettier/recommended"],
"rules": { "rules": {
"semi": ["error", "always"], "semi": ["error", "always"],
@ -15,7 +15,7 @@
"no-useless-escape": "off", "no-useless-escape": "off",
"no-var": "error", "no-var": "error",
"prefer-const": "error", "prefer-const": "error",
"no-unused-expressions": "error", "no-unused-expressions": "off",
"strict": ["error", "never"], "strict": ["error", "never"],
"no-mixed-operators": "off", "no-mixed-operators": "off",
"prefer-promise-reject-errors": "off", "prefer-promise-reject-errors": "off",
@ -49,7 +49,8 @@
"import/no-relative-parent-imports": "error", "import/no-relative-parent-imports": "error",
"import/first": "error", "import/first": "error",
"import/no-namespace": "error", "import/no-namespace": "error",
"import/no-default-export": "error" "import/no-default-export": "error",
"babel/no-unused-expressions": "error"
}, },
"parserOptions": { "parserOptions": {
"sourceType": "module", "sourceType": "module",

View File

@ -3,10 +3,8 @@ import kdbxweb from 'kdbxweb';
import { RuntimeInfo } from 'const/runtime-info'; import { RuntimeInfo } from 'const/runtime-info';
import { Links } from 'const/links'; import { Links } from 'const/links';
import { DateFormat } from 'util/formatting/date-format'; import { DateFormat } from 'util/formatting/date-format';
import { MdToHtml } from 'util/formatting/md-to-html';
import { StringFormat } from 'util/formatting/string-format'; import { StringFormat } from 'util/formatting/string-format';
import { Locale } from 'util/locale'; import { Locale } from 'util/locale';
import { AppSettingsModel } from 'models/app-settings-model';
const Templates = { const Templates = {
db: require('templates/export/db.hbs'), db: require('templates/export/db.hbs'),

View File

@ -59,7 +59,7 @@ class View extends EventEmitter {
Tip.createTips(this.el); Tip.createTips(this.el);
this.debugLogger && this.debugLogger.debug('Render finished', this.debugLogger.ts(ts)); this.debugLogger?.debug('Render finished', this.debugLogger.ts(ts));
return this; return this;
} }
@ -129,7 +129,7 @@ class View extends EventEmitter {
eventsMap[event].push({ selector, method }); eventsMap[event].push({ selector, method });
} }
for (const [event, handlers] of Object.entries(eventsMap)) { for (const [event, handlers] of Object.entries(eventsMap)) {
this.debugLogger && this.debugLogger.debug('Bind', 'view', event, handlers); this.debugLogger?.debug('Bind', 'view', event, handlers);
const listener = e => this.eventListener(e, handlers); const listener = e => this.eventListener(e, handlers);
this.eventListeners[event] = listener; this.eventListeners[event] = listener;
this.el.addEventListener(event, listener); this.el.addEventListener(event, listener);
@ -151,8 +151,7 @@ class View extends EventEmitter {
this.unbindElementEvents(); this.unbindElementEvents();
for (const cfg of this.elementEventListeners) { for (const cfg of this.elementEventListeners) {
const els = this.el.querySelectorAll(cfg.selector); const els = this.el.querySelectorAll(cfg.selector);
this.debugLogger && this.debugLogger?.debug('Bind', 'element', cfg.event, cfg.selector, els.length);
this.debugLogger.debug('Bind', 'element', cfg.event, cfg.selector, els.length);
cfg.listener = e => this.eventListener(e, [cfg]); cfg.listener = e => this.eventListener(e, [cfg]);
for (const el of els) { for (const el of els) {
el.addEventListener(cfg.event, cfg.listener); el.addEventListener(cfg.event, cfg.listener);
@ -174,7 +173,7 @@ class View extends EventEmitter {
} }
eventListener(e, handlers) { eventListener(e, handlers) {
this.debugLogger && this.debugLogger.debug('Listener fired', e.type); this.debugLogger?.debug('Listener fired', e.type);
for (const { selector, method } of handlers) { for (const { selector, method } of handlers) {
if (selector) { if (selector) {
const closest = e.target.closest(selector); const closest = e.target.closest(selector);
@ -183,10 +182,10 @@ class View extends EventEmitter {
} }
} }
if (!this[method]) { if (!this[method]) {
this.debugLogger && this.debugLogger.debug('Method not defined', method); this.debugLogger?.debug('Method not defined', method);
continue; continue;
} }
this.debugLogger && this.debugLogger.debug('Handling event', e.type, method); this.debugLogger?.debug('Handling event', e.type, method);
this[method](e); this[method](e);
} }
} }
@ -202,7 +201,7 @@ class View extends EventEmitter {
this.el.remove(); this.el.remove();
this.removed = true; this.removed = true;
this.debugLogger && this.debugLogger.debug('Remove'); this.debugLogger?.debug('Remove');
} }
removeInnerViews() { removeInnerViews() {
@ -236,7 +235,7 @@ class View extends EventEmitter {
} }
toggle(visible) { toggle(visible) {
this.debugLogger && this.debugLogger.debug(visible ? 'Show' : 'Hide'); this.debugLogger?.debug(visible ? 'Show' : 'Hide');
if (visible === undefined) { if (visible === undefined) {
visible = this.hidden; visible = this.hidden;
} }

View File

@ -302,9 +302,37 @@ class AppModel {
getEntriesByFilter(filter) { getEntriesByFilter(filter) {
const preparedFilter = this.prepareFilter(filter); const preparedFilter = this.prepareFilter(filter);
const entries = new SearchResultCollection(); const entries = new SearchResultCollection();
this.files.forEach(file => {
file.forEachEntry(preparedFilter, entry => entries.push(entry)); const devicesToMatchOtpEntries = this.files.filter(file => file.external);
});
const matchedOtpEntrySet = this.settings.yubiKeyMatchEntries ? new Set() : undefined;
this.files
.filter(file => !file.external)
.forEach(file => {
file.forEachEntry(preparedFilter, entry => {
if (matchedOtpEntrySet) {
for (const device of devicesToMatchOtpEntries) {
const matchingEntry = device.getMatchingEntry(entry);
if (matchingEntry) {
matchedOtpEntrySet.add(matchingEntry);
}
}
}
entries.push(entry);
});
});
if (devicesToMatchOtpEntries.length) {
for (const device of devicesToMatchOtpEntries) {
device.forEachEntry(preparedFilter, entry => {
if (!matchedOtpEntrySet || !matchedOtpEntrySet.has(entry)) {
entries.push(entry);
}
});
}
}
return entries; return entries;
} }
@ -382,14 +410,17 @@ class AppModel {
getEntryTemplates() { getEntryTemplates() {
const entryTemplates = []; const entryTemplates = [];
this.files.forEach(file => { this.files.forEach(file => {
file.forEachEntryTemplate && file.forEachEntryTemplate?.(entry => {
file.forEachEntryTemplate(entry => { entryTemplates.push({ file, entry });
entryTemplates.push({ file, entry }); });
});
}); });
return entryTemplates; return entryTemplates;
} }
canCreateEntries() {
return this.files.some(f => f.active && !f.readOnly);
}
createNewEntry(args) { createNewEntry(args) {
const sel = this.getFirstSelectedGroupForCreation(); const sel = this.getFirstSelectedGroupForCreation();
if (args && args.template) { if (args && args.template) {
@ -1185,8 +1216,18 @@ class AppModel {
return device; return device;
} }
canCreateEntries() { getMatchingOtpEntry(entry) {
return this.files.some(f => f.active && !f.readOnly); if (!this.settings.yubiKeyMatchEntries) {
return null;
}
for (const file of this.files) {
if (file.external) {
const matchingEntry = file.getMatchingEntry(entry);
if (matchingEntry) {
return matchingEntry;
}
}
}
} }
} }

View File

@ -58,6 +58,7 @@ class EntryModel extends Model {
this.expires = entry.times.expires ? entry.times.expiryTime : undefined; this.expires = entry.times.expires ? entry.times.expiryTime : undefined;
this.expired = entry.times.expires && entry.times.expiryTime <= new Date(); this.expired = entry.times.expires && entry.times.expiryTime <= new Date();
this.historyLength = entry.history.length; this.historyLength = entry.history.length;
this.titleUserLower = `${this.title}:${this.user}`.toLowerCase();
this._buildCustomIcon(); this._buildCustomIcon();
this._buildSearchText(); this._buildSearchText();
this._buildSearchTags(); this._buildSearchTags();

View File

@ -4,6 +4,7 @@ import { ExternalEntryCollection } from 'collections/external-entry-collection';
class ExternalDeviceModel extends Model { class ExternalDeviceModel extends Model {
entries = new ExternalEntryCollection(); entries = new ExternalEntryCollection();
groups = []; groups = [];
entryMap = {};
close() {} close() {}
@ -17,6 +18,20 @@ class ExternalDeviceModel extends Model {
} }
} }
} }
entryId(title, user) {
return `${title}:${user}`.toLowerCase();
}
getMatchingEntry(entry) {
return this.entryMap[this.entryId(entry.title, entry.user)];
}
_buildEntryMap() {
for (const entry of this.entries) {
this.entryMap[entry.id.toLowerCase()] = entry;
}
}
} }
ExternalDeviceModel.defineModelProperties({ ExternalDeviceModel.defineModelProperties({
@ -28,7 +43,8 @@ ExternalDeviceModel.defineModelProperties({
groups: undefined, groups: undefined,
name: undefined, name: undefined,
shortName: undefined, shortName: undefined,
deviceClassName: undefined deviceClassName: undefined,
entryMap: undefined
}); });
export { ExternalDeviceModel }; export { ExternalDeviceModel };

View File

@ -100,6 +100,9 @@ class YubiKeyOtpModel extends ExternalOtpDeviceModel {
if (yubiKeys && yubiKeys.length) { if (yubiKeys && yubiKeys.length) {
openNextYubiKey(); openNextYubiKey();
} else { } else {
if (openSuccess) {
this._openComplete();
}
callback(openSuccess ? null : openErrors[0]); callback(openSuccess ? null : openErrors[0]);
} }
}); });
@ -135,7 +138,7 @@ class YubiKeyOtpModel extends ExternalOtpDeviceModel {
this.entries.push( this.entries.push(
new ExternalOtpEntryModel({ new ExternalOtpEntryModel({
id: title + ':' + user, id: this.entryId(title, user),
device: this, device: this,
deviceSubId: serial, deviceSubId: serial,
icon: 'clock-o', icon: 'clock-o',
@ -145,8 +148,6 @@ class YubiKeyOtpModel extends ExternalOtpDeviceModel {
}) })
); );
} }
this.active = true;
Events.on('usb-devices-changed', this.onUsbDevicesChanged);
callback(); callback();
} }
}); });
@ -187,6 +188,12 @@ class YubiKeyOtpModel extends ExternalOtpDeviceModel {
}); });
} }
_openComplete() {
this.active = true;
this._buildEntryMap();
Events.on('usb-devices-changed', this.onUsbDevicesChanged);
}
cancelOpen() { cancelOpen() {
logger.info('Cancel open'); logger.info('Cancel open');
Events.off('usb-devices-changed', this.onUsbDevicesChanged); Events.off('usb-devices-changed', this.onUsbDevicesChanged);

View File

@ -468,11 +468,11 @@ class StorageBase {
if (token && token.error) { if (token && token.error) {
return callback && callback('OAuth code exchange error: ' + token.error); return callback && callback('OAuth code exchange error: ' + token.error);
} }
callback && callback(); callback?.();
}, },
error: err => { error: err => {
this.logger.error('Error exchanging OAuth code', err); this.logger.error('Error exchanging OAuth code', err);
callback && callback('OAuth code exchange error: ' + err); callback?.('OAuth code exchange error: ' + err);
} }
}); });
} }
@ -507,7 +507,7 @@ class StorageBase {
this._oauthToken = null; this._oauthToken = null;
} }
this.logger.error('Error exchanging refresh token', err); this.logger.error('Error exchanging refresh token', err);
callback && callback('Error exchanging refresh token'); callback?.('Error exchanging refresh token');
} }
}); });
} }

View File

@ -16,6 +16,7 @@ import { FieldViewReadOnlyWithOptions } from 'views/fields/field-view-read-only-
function createDetailsFields(detailsView) { function createDetailsFields(detailsView) {
const model = detailsView.model; const model = detailsView.model;
const otpEntry = detailsView.matchingOtpEntry;
const fieldViews = []; const fieldViews = [];
const fieldViewsAside = []; const fieldViewsAside = [];
@ -185,18 +186,35 @@ function createDetailsFields(detailsView) {
} }
}) })
); );
if (otpEntry) {
fieldViews.push(
new FieldViewOtp({
name: '$otp',
title: Locale.detOtpField,
value() {
return otpEntry.otpGenerator;
},
sequence: '{TOTP}',
readonly: true,
needsTouch: otpEntry.needsTouch,
deviceShortName: otpEntry.device.shortName
})
);
}
for (const field of Object.keys(model.fields)) { for (const field of Object.keys(model.fields)) {
if (field === 'otp' && model.otpGenerator) { if (field === 'otp' && model.otpGenerator) {
fieldViews.push( if (!otpEntry) {
FieldViewOtp({ fieldViews.push(
name: '$' + field, FieldViewOtp({
title: field, name: '$' + field,
value() { title: field,
return model.otpGenerator; value() {
}, return model.otpGenerator;
sequence: '{TOTP}' },
}) sequence: '{TOTP}'
); })
);
}
} else { } else {
fieldViews.push( fieldViews.push(
new FieldViewCustom({ new FieldViewCustom({

View File

@ -120,7 +120,6 @@ class DetailsView extends View {
this.template = template; this.template = template;
super.render(model); super.render(model);
this.setSelectedColor(this.model.color); this.setSelectedColor(this.model.color);
this.model.initOtpGenerator();
this.addFieldViews(); this.addFieldViews();
this.createScroll({ this.createScroll({
root: this.$el.find('.details__body')[0], root: this.$el.find('.details__body')[0],
@ -437,12 +436,25 @@ class DetailsView extends View {
showEntry(entry) { showEntry(entry) {
this.model = entry; this.model = entry;
this.initOtp();
this.render(); this.render();
if (entry && !entry.title && entry.isJustCreated) { if (entry && !entry.title && entry.isJustCreated) {
this.editTitle(); this.editTitle();
} }
} }
initOtp() {
this.matchingOtpEntry = null;
if (!this.model || this.model.external) {
return;
}
this.matchingOtpEntry = this.appModel.getMatchingOtpEntry(this.model);
this.model.initOtpGenerator();
this.matchingOtpEntry?.initOtpGenerator();
}
copyKeyPress(editView) { copyKeyPress(editView) {
if (!editView || this.isHidden()) { if (!editView || this.isHidden()) {
return false; return false;
@ -948,7 +960,8 @@ class DetailsView extends View {
autoType(sequence) { autoType(sequence) {
const entry = this.model; const entry = this.model;
if (entry.external && (!sequence || sequence.includes('{TOTP}'))) { const hasOtp = sequence?.includes('{TOTP}') || (entry.external && !sequence);
if (hasOtp) {
const otpField = this.getFieldView('$otp'); const otpField = this.getFieldView('$otp');
otpField.refreshOtp(err => { otpField.refreshOtp(err => {
if (!err) { if (!err) {

View File

@ -3,7 +3,6 @@ import { View } from 'framework/views/view';
import { Scrollable } from 'framework/views/scrollable'; import { Scrollable } from 'framework/views/scrollable';
import template from 'templates/import-csv.hbs'; import template from 'templates/import-csv.hbs';
import { EntryModel } from 'models/entry-model'; import { EntryModel } from 'models/entry-model';
import { escape } from 'util/fn';
class ImportCsvView extends View { class ImportCsvView extends View {
parent = '.app__body'; parent = '.app__body';

View File

@ -1,9 +1,10 @@
import { Events } from 'framework/events';
import { View } from 'framework/views/view'; import { View } from 'framework/views/view';
import { AppSettingsModel } from 'models/app-settings-model'; import { AppSettingsModel } from 'models/app-settings-model';
import { YubiKeyOtpModel } from 'models/external/yubikey-otp-model'; import { YubiKeyOtpModel } from 'models/external/yubikey-otp-model';
import template from 'templates/settings/settings-devices.hbs';
import { Links } from 'const/links'; import { Links } from 'const/links';
import { UsbListener } from 'comp/app/usb-listener'; import { UsbListener } from 'comp/app/usb-listener';
import template from 'templates/settings/settings-devices.hbs';
class SettingsDevicesView extends View { class SettingsDevicesView extends View {
template = template; template = template;
@ -61,6 +62,7 @@ class SettingsDevicesView extends View {
changeYubiKeyMatchEntries(e) { changeYubiKeyMatchEntries(e) {
AppSettingsModel.yubiKeyMatchEntries = e.target.checked; AppSettingsModel.yubiKeyMatchEntries = e.target.checked;
this.render(); this.render();
Events.emit('refresh');
} }
changeYubiKeyShowChalResp(e) { changeYubiKeyShowChalResp(e) {

View File

@ -20,7 +20,7 @@ if (!gotTheLock) {
app.quit(); app.quit();
} }
perfTimestamps && perfTimestamps.push({ name: 'single instance lock', ts: process.hrtime() }); perfTimestamps?.push({ name: 'single instance lock', ts: process.hrtime() });
let openFile = process.argv.filter(arg => /\.kdbx$/i.test(arg))[0]; let openFile = process.argv.filter(arg => /\.kdbx$/i.test(arg))[0];
const userDataDir = const userDataDir =
@ -52,7 +52,7 @@ const themeBgColors = {
}; };
const defaultBgColor = '#282C34'; const defaultBgColor = '#282C34';
perfTimestamps && perfTimestamps.push({ name: 'defining args', ts: process.hrtime() }); perfTimestamps?.push({ name: 'defining args', ts: process.hrtime() });
setDevAppIcon(); setDevAppIcon();
setEnv(); setEnv();
@ -71,7 +71,7 @@ app.on('window-all-closed', () => {
} }
}); });
app.on('ready', () => { app.on('ready', () => {
perfTimestamps && perfTimestamps.push({ name: 'app on ready', ts: process.hrtime() }); perfTimestamps?.push({ name: 'app on ready', ts: process.hrtime() });
appReady = true; appReady = true;
setAppOptions(); setAppOptions();
setSystemAppearance(); setSystemAppearance();
@ -155,7 +155,7 @@ app.setGlobalShortcuts = setGlobalShortcuts;
function setAppOptions() { function setAppOptions() {
app.commandLine.appendSwitch('disable-background-timer-throttling'); app.commandLine.appendSwitch('disable-background-timer-throttling');
perfTimestamps && perfTimestamps.push({ name: 'setting app options', ts: process.hrtime() }); perfTimestamps?.push({ name: 'setting app options', ts: process.hrtime() });
} }
function readAppSettings() { function readAppSettings() {
@ -164,8 +164,7 @@ function readAppSettings() {
} catch (e) { } catch (e) {
return null; return null;
} finally { } finally {
perfTimestamps && perfTimestamps?.push({ name: 'reading app settings', ts: process.hrtime() });
perfTimestamps.push({ name: 'reading app settings', ts: process.hrtime() });
} }
} }
@ -175,8 +174,7 @@ function setSystemAppearance() {
electron.systemPreferences.appLevelAppearance = 'dark'; electron.systemPreferences.appLevelAppearance = 'dark';
} }
} }
perfTimestamps && perfTimestamps?.push({ name: 'setting system appearance', ts: process.hrtime() });
perfTimestamps.push({ name: 'setting system appearance', ts: process.hrtime() });
} }
function createMainWindow() { function createMainWindow() {
@ -198,17 +196,17 @@ function createMainWindow() {
windowOptions.icon = path.join(__dirname, 'icon.png'); windowOptions.icon = path.join(__dirname, 'icon.png');
} }
mainWindow = new electron.BrowserWindow(windowOptions); mainWindow = new electron.BrowserWindow(windowOptions);
perfTimestamps && perfTimestamps.push({ name: 'creating main window', ts: process.hrtime() }); perfTimestamps?.push({ name: 'creating main window', ts: process.hrtime() });
setMenu(); setMenu();
perfTimestamps && perfTimestamps.push({ name: 'setting menu', ts: process.hrtime() }); perfTimestamps?.push({ name: 'setting menu', ts: process.hrtime() });
mainWindow.loadURL(htmlPath); mainWindow.loadURL(htmlPath);
if (showDevToolsOnStart) { if (showDevToolsOnStart) {
mainWindow.openDevTools({ mode: 'bottom' }); mainWindow.openDevTools({ mode: 'bottom' });
} }
mainWindow.once('ready-to-show', () => { mainWindow.once('ready-to-show', () => {
perfTimestamps && perfTimestamps.push({ name: 'main window ready', ts: process.hrtime() }); perfTimestamps?.push({ name: 'main window ready', ts: process.hrtime() });
if (startMinimized) { if (startMinimized) {
emitRemoteEvent('launcher-started-minimized'); emitRemoteEvent('launcher-started-minimized');
} else { } else {
@ -216,7 +214,7 @@ function createMainWindow() {
} }
ready = true; ready = true;
notifyOpenFile(); notifyOpenFile();
perfTimestamps && perfTimestamps.push({ name: 'main window shown', ts: process.hrtime() }); perfTimestamps?.push({ name: 'main window shown', ts: process.hrtime() });
reportStartProfile(); reportStartProfile();
}); });
mainWindow.webContents.on('context-menu', onContextMenu); mainWindow.webContents.on('context-menu', onContextMenu);
@ -242,12 +240,10 @@ function createMainWindow() {
mainWindow.on('session-end', () => { mainWindow.on('session-end', () => {
emitRemoteEvent('os-lock'); emitRemoteEvent('os-lock');
}); });
perfTimestamps && perfTimestamps?.push({ name: 'configuring main window', ts: process.hrtime() });
perfTimestamps.push({ name: 'configuring main window', ts: process.hrtime() });
restoreMainWindowPosition(); restoreMainWindowPosition();
perfTimestamps && perfTimestamps?.push({ name: 'restoring main window position', ts: process.hrtime() });
perfTimestamps.push({ name: 'restoring main window position', ts: process.hrtime() });
} }
function restoreMainWindow() { function restoreMainWindow() {
@ -464,8 +460,7 @@ function setGlobalShortcuts(appSettings) {
} catch (e) {} } catch (e) {}
} }
} }
perfTimestamps && perfTimestamps?.push({ name: 'setting global shortcuts', ts: process.hrtime() });
perfTimestamps.push({ name: 'setting global shortcuts', ts: process.hrtime() });
} }
function subscribePowerEvents() { function subscribePowerEvents() {
@ -478,8 +473,7 @@ function subscribePowerEvents() {
electron.powerMonitor.on('lock-screen', () => { electron.powerMonitor.on('lock-screen', () => {
emitRemoteEvent('os-lock'); emitRemoteEvent('os-lock');
}); });
perfTimestamps && perfTimestamps?.push({ name: 'subscribing to power events', ts: process.hrtime() });
perfTimestamps.push({ name: 'subscribing to power events', ts: process.hrtime() });
} }
function setEnv() { function setEnv() {
@ -491,7 +485,7 @@ function setEnv() {
// https://github.com/electron/electron/issues/9046 // https://github.com/electron/electron/issues/9046
process.env.XDG_CURRENT_DESKTOP = 'Unity'; process.env.XDG_CURRENT_DESKTOP = 'Unity';
} }
perfTimestamps && perfTimestamps.push({ name: 'setting env', ts: process.hrtime() }); perfTimestamps?.push({ name: 'setting env', ts: process.hrtime() });
} }
function restorePreferences() { function restorePreferences() {
@ -524,7 +518,7 @@ function restorePreferences() {
} }
} }
perfTimestamps && perfTimestamps.push({ name: 'restoring preferences', ts: process.hrtime() }); perfTimestamps?.push({ name: 'restoring preferences', ts: process.hrtime() });
} }
function deleteOldTempFiles() { function deleteOldTempFiles() {
@ -541,8 +535,7 @@ function deleteOldTempFiles() {
} }
app.oldTempFilesDeleted = true; // this is added to prevent file deletion on restart app.oldTempFilesDeleted = true; // this is added to prevent file deletion on restart
}, 1000); }, 1000);
perfTimestamps && perfTimestamps?.push({ name: 'deleting old temp files', ts: process.hrtime() });
perfTimestamps.push({ name: 'deleting old temp files', ts: process.hrtime() });
} }
function deleteRecursive(dir) { function deleteRecursive(dir) {
@ -577,8 +570,7 @@ function hookRequestHeaders() {
} }
callback({ requestHeaders: details.requestHeaders }); callback({ requestHeaders: details.requestHeaders });
}); });
perfTimestamps && perfTimestamps?.push({ name: 'setting request handlers', ts: process.hrtime() });
perfTimestamps.push({ name: 'setting request handlers', ts: process.hrtime() });
} }
// If a display is disconnected while KeeWeb is minimized, Electron does not // If a display is disconnected while KeeWeb is minimized, Electron does not

13
package-lock.json generated
View File

@ -5519,6 +5519,14 @@
} }
} }
}, },
"eslint-plugin-babel": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-babel/-/eslint-plugin-babel-5.3.0.tgz",
"integrity": "sha512-HPuNzSPE75O+SnxHIafbW5QB45r2w78fxqwK3HmjqIUoPfPzVrq6rD+CINU3yzoDSzEhUkX07VUphbF73Lth/w==",
"requires": {
"eslint-rule-composer": "^0.3.0"
}
},
"eslint-plugin-es": { "eslint-plugin-es": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.0.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.0.tgz",
@ -5637,6 +5645,11 @@
"resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-4.0.1.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-4.0.1.tgz",
"integrity": "sha512-v/KBnfyaOMPmZc/dmc6ozOdWqekGp7bBGq4jLAecEfPGmfKiWS4sA8sC0LqiV9w5qmXAtXVn4M3p1jSyhY85SQ==" "integrity": "sha512-v/KBnfyaOMPmZc/dmc6ozOdWqekGp7bBGq4jLAecEfPGmfKiWS4sA8sC0LqiV9w5qmXAtXVn4M3p1jSyhY85SQ=="
}, },
"eslint-rule-composer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz",
"integrity": "sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg=="
},
"eslint-scope": { "eslint-scope": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz",

View File

@ -34,6 +34,7 @@
"eslint": "^6.8.0", "eslint": "^6.8.0",
"eslint-config-prettier": "^6.11.0", "eslint-config-prettier": "^6.11.0",
"eslint-config-standard": "^14.1.1", "eslint-config-standard": "^14.1.1",
"eslint-plugin-babel": "^5.3.0",
"eslint-plugin-import": "^2.20.2", "eslint-plugin-import": "^2.20.2",
"eslint-plugin-node": "^11.1.0", "eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^3.1.3", "eslint-plugin-prettier": "^3.1.3",