Commit 8701c515 authored by Robert Knight's avatar Robert Knight

Merge branch 'master' into move-pending-update-state-to-store

parents d6c00fd0 64cb045a
......@@ -9,7 +9,10 @@
"indent": "off",
"react/self-closing-comp": "error",
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "error"
"react-hooks/exhaustive-deps": "error",
// Suppressed to make ESLint v6 migration easier.
"no-prototype-builtins": "off"
},
"parserOptions": {
"ecmaVersion": 2018,
......
......@@ -47,7 +47,7 @@
"enzyme-adapter-preact-pure": "^2.0.0",
"escape-html": "^1.0.3",
"escape-string-regexp": "^1.0.5",
"eslint": "^5.12.1",
"eslint": "^6.0.1",
"eslint-config-hypothesis": "^1.0.0",
"eslint-plugin-mocha": "^5.2.1",
"eslint-plugin-react": "^7.12.4",
......@@ -88,7 +88,7 @@
"npm-packlist": "^1.1.12",
"postcss": "^7.0.13",
"postcss-url": "^8.0.0",
"preact": "10.0.0-beta.3",
"preact": "10.0.0-rc.0",
"prettier": "1.18.2",
"query-string": "^3.0.1",
"raf": "^3.1.0",
......
......@@ -54,7 +54,6 @@ function AnnotationController(
annotationMapper,
api,
bridge,
drafts,
flash,
groups,
permissions,
......@@ -176,7 +175,7 @@ function AnnotationController(
// created by the annotate button) or it has edits not yet saved to the
// server - then open the editor on AnnotationController instantiation.
if (!newlyCreatedByHighlightButton) {
if (isNew(self.annotation) || drafts.get(self.annotation)) {
if (isNew(self.annotation) || store.getDraft(self.annotation)) {
self.edit();
}
}
......@@ -217,7 +216,7 @@ function AnnotationController(
});
} else {
// User isn't logged in, save to drafts.
drafts.update(self.annotation, self.state());
store.createDraft(self.annotation, self.state());
}
}
......@@ -292,8 +291,8 @@ function AnnotationController(
* @description Switches the view to an editor.
*/
this.edit = function() {
if (!drafts.get(self.annotation)) {
drafts.update(self.annotation, self.state());
if (!store.getDraft(self.annotation)) {
store.createDraft(self.annotation, self.state());
}
};
......@@ -304,7 +303,7 @@ function AnnotationController(
* (i.e. the annotation editor form should be open), `false` otherwise.
*/
this.editing = function() {
return drafts.get(self.annotation) && !self.isSaving;
return store.getDraft(self.annotation) && !self.isSaving;
};
/**
......@@ -430,7 +429,7 @@ function AnnotationController(
* @description Reverts an edit in progress and returns to the viewer.
*/
this.revert = function() {
drafts.remove(self.annotation);
store.removeDraft(self.annotation);
if (isNew(self.annotation)) {
$rootScope.$broadcast(events.ANNOTATION_DELETED, self.annotation);
}
......@@ -465,7 +464,7 @@ function AnnotationController(
const event = isNew(self.annotation)
? events.ANNOTATION_CREATED
: events.ANNOTATION_UPDATED;
drafts.remove(self.annotation);
store.removeDraft(self.annotation);
$rootScope.$broadcast(event, updatedModel);
})
......@@ -495,7 +494,7 @@ function AnnotationController(
if (!isReply(self.annotation)) {
permissions.setDefault(privacy);
}
drafts.update(self.annotation, {
store.createDraft(self.annotation, {
tags: self.state().tags,
text: self.state().text,
isPrivate: privacy === 'private',
......@@ -570,7 +569,7 @@ function AnnotationController(
};
this.setText = function(text) {
drafts.update(self.annotation, {
store.createDraft(self.annotation, {
isPrivate: self.state().isPrivate,
tags: self.state().tags,
text: text,
......@@ -578,7 +577,7 @@ function AnnotationController(
};
this.setTags = function(tags) {
drafts.update(self.annotation, {
store.createDraft(self.annotation, {
isPrivate: self.state().isPrivate,
tags: tags,
text: self.state().text,
......@@ -586,7 +585,7 @@ function AnnotationController(
};
this.state = function() {
const draft = drafts.get(self.annotation);
const draft = store.getDraft(self.annotation);
if (draft) {
return draft;
}
......
......@@ -43,7 +43,6 @@ function HypothesisAppController(
store,
auth,
bridge,
drafts,
features,
flash,
frameSync,
......@@ -149,18 +148,19 @@ function HypothesisAppController(
const promptToLogout = function() {
// TODO - Replace this with a UI which doesn't look terrible.
let text = '';
if (drafts.count() === 1) {
const drafts = store.countDrafts();
if (drafts === 1) {
text =
'You have an unsaved annotation.\n' +
'Do you really want to discard this draft?';
} else if (drafts.count() > 1) {
} else if (drafts > 1) {
text =
'You have ' +
drafts.count() +
drafts +
' unsaved annotations.\n' +
'Do you really want to discard these drafts?';
}
return drafts.count() === 0 || $window.confirm(text);
return drafts === 0 || $window.confirm(text);
};
// Log the user out.
......@@ -168,11 +168,12 @@ function HypothesisAppController(
if (!promptToLogout()) {
return;
}
store.clearGroups();
drafts.unsaved().forEach(function(draft) {
$rootScope.$emit(events.ANNOTATION_DELETED, draft);
store.unsavedAnnotations().forEach(function(annotation) {
$rootScope.$emit(events.ANNOTATION_DELETED, annotation);
});
drafts.discard();
store.discardAllDrafts();
if (serviceConfig(settings)) {
// Let the host page handle the signup request
......
'use strict';
const propTypes = require('prop-types');
const { createElement } = require('preact');
const { withServices } = require('../util/service-context');
const SvgIcon = require('./svg-icon');
/**
* Render a call-to-action to log in or sign up. This message is intended to be
* displayed to non-auth'd users when viewing a single annotation in a
* direct-linked context (i.e. URL with syntax `/#annotations:<annotation_id>`)
*/
function LoggedOutMessage({ onLogin, serviceUrl }) {
return (
<div className="logged-out-message">
<span>
This is a public annotation created with Hypothesis. <br />
To reply or make your own annotations on this document,{' '}
<a
className="logged-out-message__link"
href={serviceUrl('signup')}
target="_blank"
rel="noopener noreferrer"
>
create a free account
</a>{' '}
or{' '}
<a className="logged-out-message__link" href="" onClick={onLogin}>
log in
</a>
.
</span>
<div className="logged-out-message__logo">
<a href="https://hypothes.is">
<SvgIcon name="logo" className="logged-out-message__logo-icon" />
</a>
</div>
</div>
);
}
LoggedOutMessage.propTypes = {
onLogin: propTypes.func.isRequired,
serviceUrl: propTypes.func.isRequired,
};
LoggedOutMessage.injectedProps = ['serviceUrl'];
module.exports = withServices(LoggedOutMessage);
'use strict';
module.exports = {
controllerAs: 'vm',
//@ngInject
controller: function(serviceUrl) {
this.serviceUrl = serviceUrl;
},
bindings: {
/**
* Called when the user clicks on the "Log in" text.
*/
onLogin: '&',
},
template: require('../templates/loggedout-message.html'),
};
'use strict';
const SearchClient = require('../search-client');
const events = require('../events');
const isThirdPartyService = require('../util/is-third-party-service');
const tabs = require('../tabs');
/**
* Returns the group ID of the first annotation in `results` whose
* ID is `annId`.
*/
function getGroupID(annId, results) {
const annot = results.find(function(annot) {
return annot.id === annId;
});
if (!annot) {
return null;
}
return annot.group;
}
// @ngInject
function SidebarContentController(
$scope,
analytics,
annotations,
store,
annotationMapper,
api,
drafts,
features,
frameSync,
groups,
rootThread,
settings,
streamer,
streamFilter
streamer
) {
const self = this;
......@@ -91,61 +71,7 @@ function SidebarContentController(
}
}
const searchClients = [];
function _resetAnnotations() {
annotationMapper.unloadAnnotations(store.savedAnnotations());
}
function _loadAnnotationsFor(uris, group) {
const searchClient = new SearchClient(api.search, {
// If no group is specified, we are fetching annotations from
// all groups in order to find out which group contains the selected
// annotation, therefore we need to load all chunks before processing
// the results
incremental: !!group,
});
searchClients.push(searchClient);
searchClient.on('results', function(results) {
if (store.hasSelectedAnnotations()) {
// Focus the group containing the selected annotation and filter
// annotations to those from this group
let groupID = getGroupID(store.getFirstSelectedAnnotationId(), results);
if (!groupID) {
// If the selected annotation is not available, fall back to
// loading annotations for the currently focused group
groupID = groups.focused().id;
}
results = results.filter(function(result) {
return result.group === groupID;
});
groups.focus(groupID);
}
if (results.length) {
annotationMapper.loadAnnotations(results);
}
});
searchClient.on('end', function() {
// Remove client from list of active search clients.
//
// $evalAsync is required here because search results are emitted
// asynchronously. A better solution would be that the loading state is
// tracked as part of the app state.
$scope.$evalAsync(function() {
searchClients.splice(searchClients.indexOf(searchClient), 1);
});
store.frames().forEach(function(frame) {
if (0 <= uris.indexOf(frame.uri)) {
store.updateFrameAnnotationFetchStatus(frame.uri, true);
}
});
});
searchClient.get({ uri: uris, group: group });
}
this.isLoading = function() {
this.isLoading = () => {
if (
!store.frames().some(function(frame) {
return frame.uri;
......@@ -154,47 +80,9 @@ function SidebarContentController(
// The document's URL isn't known so the document must still be loading.
return true;
}
if (searchClients.length > 0) {
// We're still waiting for annotation search results from the API.
return true;
}
return false;
return store.isFetchingAnnotations();
};
/**
* Load annotations for all URLs associated with `frames`.
*/
function loadAnnotations() {
_resetAnnotations();
searchClients.forEach(function(client) {
client.cancel();
});
// If there is no selection, load annotations only for the focused group.
//
// If there is a selection, we load annotations for all groups, find out
// which group the first selected annotation is in and then filter the
// results on the client by that group.
//
// In the common case where the total number of annotations on
// a page that are visible to the user is not greater than
// the batch size, this saves an extra roundtrip to the server
// to fetch the selected annotation in order to determine which group
// it is in before fetching the remaining annotations.
const group = store.hasSelectedAnnotations() ? null : groups.focused().id;
const searchUris = store.searchUris();
if (searchUris.length > 0) {
_loadAnnotationsFor(searchUris, group);
streamFilter.resetFilter().addClause('/uri', 'one_of', searchUris);
streamer.setConfig('filter', { filter: streamFilter.getFilter() });
}
}
$scope.$on('sidebarOpened', function() {
analytics.track(analytics.events.SIDEBAR_OPENED);
......@@ -234,27 +122,25 @@ function SidebarContentController(
// Re-fetch annotations when focused group, logged-in user or connected frames
// change.
$scope.$watch(
() => [groups.focused(), store.profile().userid, ...store.searchUris()],
([currentGroup], [prevGroup]) => {
if (!currentGroup) {
// When switching accounts, groups are cleared and so the focused group
() => [
store.focusedGroupId(),
store.profile().userid,
...store.searchUris(),
],
([currentGroupId], [prevGroupId]) => {
if (!currentGroupId) {
// When switching accounts, groups are cleared and so the focused group id
// will be null for a brief period of time.
store.clearSelectedAnnotations();
return;
}
if (!prevGroup || currentGroup.id !== prevGroup.id) {
// The focused group may be changed during loading annotations as a result
// of switching to the group containing a direct-linked annotation.
//
// In that case, we don't want to trigger reloading annotations again.
if (this.isLoading()) {
return;
}
if (!prevGroupId || currentGroupId !== prevGroupId) {
store.clearSelectedAnnotations();
}
loadAnnotations();
const searchUris = store.searchUris();
annotations.load(searchUris, currentGroupId);
},
true
);
......@@ -278,7 +164,7 @@ function SidebarContentController(
this.scrollTo = scrollToAnnotation;
this.selectedGroupUnavailable = function() {
return !this.isLoading() && store.getState().directLinkedGroupFetchFailed;
return store.getState().directLinkedGroupFetchFailed;
};
this.selectedAnnotationUnavailable = function() {
......@@ -311,7 +197,9 @@ function SidebarContentController(
// selection is available to the user, show the CTA.
const selectedID = store.getFirstSelectedAnnotationId();
return (
!this.isLoading() && !!selectedID && store.annotationExists(selectedID)
!store.isFetchingAnnotations() &&
!!selectedID &&
store.annotationExists(selectedID)
);
};
}
......
......@@ -16,6 +16,7 @@ const icons = {
help: require('../../images/icons/help.svg'),
leave: require('../../images/icons/leave.svg'),
lock: require('../../images/icons/lock.svg'),
logo: require('../../images/icons/logo.svg'),
public: require('../../images/icons/public.svg'),
refresh: require('../../images/icons/refresh.svg'),
share: require('../../images/icons/share.svg'),
......
......@@ -107,7 +107,6 @@ describe('annotation', function() {
let fakeAnalytics;
let fakeAnnotationMapper;
let fakeStore;
let fakeDrafts;
let fakeFlash;
let fakeGroups;
let fakePermissions;
......@@ -136,7 +135,7 @@ describe('annotation', function() {
// A new annotation won't have any saved drafts yet.
if (!annotation.id) {
fakeDrafts.get.returns(null);
fakeStore.getDraft.returns(null);
}
return {
......@@ -190,12 +189,13 @@ describe('annotation', function() {
fakeStore = {
hasPendingDeletion: sinon.stub(),
updateFlagStatus: sandbox.stub().returns(true),
};
fakeDrafts = {
update: sandbox.stub(),
remove: sandbox.stub(),
get: sandbox.stub().returns(null),
// draft store
countDrafts: sandbox.stub().returns(0),
createDraft: sandbox.stub(),
discardAllDrafts: sandbox.stub(),
getDraft: sandbox.stub().returns(null),
getDraftIfNotEmpty: sandbox.stub().returns(null),
removeDraft: sandbox.stub(),
};
fakeFlash = {
......@@ -258,7 +258,6 @@ describe('annotation', function() {
$provide.value('store', fakeStore);
$provide.value('api', fakeApi);
$provide.value('bridge', fakeBridge);
$provide.value('drafts', fakeDrafts);
$provide.value('flash', fakeFlash);
$provide.value('groups', fakeGroups);
$provide.value('permissions', fakePermissions);
......@@ -371,7 +370,7 @@ describe('annotation', function() {
createDirective(annotation);
assert.notCalled(fakeApi.annotation.create);
assert.called(fakeDrafts.update);
assert.called(fakeStore.createDraft);
});
it('opens the sidebar when trying to save highlights while logged out', () => {
......@@ -413,7 +412,7 @@ describe('annotation', function() {
it('creates drafts for new annotations on initialization', function() {
const annotation = fixtures.newAnnotation();
createDirective(annotation);
assert.calledWith(fakeDrafts.update, annotation, {
assert.calledWith(fakeStore.createDraft, annotation, {
isPrivate: false,
tags: annotation.tags,
text: annotation.text,
......@@ -425,13 +424,13 @@ describe('annotation', function() {
const controller = createDirective(annotation).controller;
assert.notOk(controller.editing());
assert.notCalled(fakeDrafts.update);
assert.notCalled(fakeStore.createDraft);
});
it('edits annotations with drafts on initialization', function() {
const annotation = fixtures.oldAnnotation();
// The drafts service has some draft changes for this annotation.
fakeDrafts.get.returns({ text: 'foo', tags: [] });
// The drafts store has some draft changes for this annotation.
fakeStore.getDraft.returns({ text: 'foo', tags: [] });
const controller = createDirective(annotation).controller;
......@@ -447,13 +446,13 @@ describe('annotation', function() {
it('returns true if the annotation has a draft', function() {
const controller = createDirective().controller;
fakeDrafts.get.returns({ tags: [], text: '', isPrivate: false });
fakeStore.getDraft.returns({ tags: [], text: '', isPrivate: false });
assert.isTrue(controller.editing());
});
it('returns false if the annotation has a draft but is being saved', function() {
const controller = createDirective().controller;
fakeDrafts.get.returns({ tags: [], text: '', isPrivate: false });
fakeStore.getDraft.returns({ tags: [], text: '', isPrivate: false });
controller.isSaving = true;
assert.isFalse(controller.editing());
});
......@@ -620,7 +619,7 @@ describe('annotation', function() {
const parts = createDirective();
parts.controller.setPrivacy('private');
assert.calledWith(
fakeDrafts.update,
fakeStore.createDraft,
parts.controller.annotation,
sinon.match({
isPrivate: true,
......@@ -632,7 +631,7 @@ describe('annotation', function() {
const parts = createDirective();
parts.controller.setPrivacy('shared');
assert.calledWith(
fakeDrafts.update,
fakeStore.createDraft,
parts.controller.annotation,
sinon.match({
isPrivate: false,
......@@ -914,7 +913,7 @@ describe('annotation', function() {
function(testCase) {
const ann = fixtures.publicAnnotation();
ann.group = testCase.group.id;
fakeDrafts.get.returns(testCase.draft);
fakeStore.getDraft.returns(testCase.draft);
fakeGroups.get.returns(testCase.group);
const controller = createDirective(ann).controller;
......@@ -992,7 +991,7 @@ describe('annotation', function() {
it('removes the draft when saving an annotation succeeds', function() {
const controller = createController();
return controller.save().then(function() {
assert.calledWith(fakeDrafts.remove, annotation);
assert.calledWith(fakeStore.removeDraft, annotation);
});
});
......@@ -1046,7 +1045,7 @@ describe('annotation', function() {
.stub()
.returns(Promise.reject({ status: -1 }));
return controller.save().then(function() {
assert.notCalled(fakeDrafts.remove);
assert.notCalled(fakeStore.removeDraft);
});
});
......@@ -1064,7 +1063,7 @@ describe('annotation', function() {
beforeEach(function() {
annotation = fixtures.defaultAnnotation();
fakeDrafts.get.returns({ text: 'unsaved change' });
fakeStore.getDraft.returns({ text: 'unsaved change' });
});
function createController() {
......@@ -1094,7 +1093,7 @@ describe('annotation', function() {
describe('drafts', function() {
it('starts editing immediately if there is a draft', function() {
fakeDrafts.get.returns({
fakeStore.getDraft.returns({
tags: ['unsaved'],
text: 'unsaved-text',
});
......@@ -1103,7 +1102,7 @@ describe('annotation', function() {
});
it('uses the text and tags from the draft if present', function() {
fakeDrafts.get.returns({
fakeStore.getDraft.returns({
tags: ['unsaved-tag'],
text: 'unsaved-text',
});
......@@ -1116,15 +1115,15 @@ describe('annotation', function() {
const parts = createDirective();
parts.controller.edit();
parts.controller.revert();
assert.calledWith(fakeDrafts.remove, parts.annotation);
assert.calledWith(fakeStore.removeDraft, parts.annotation);
});
it('removes the draft when changes are saved', function() {
const annotation = fixtures.defaultAnnotation();
const controller = createDirective(annotation).controller;
fakeDrafts.get.returns({ text: 'unsaved changes' });
fakeStore.getDraft.returns({ text: 'unsaved changes' });
return controller.save().then(function() {
assert.calledWith(fakeDrafts.remove, annotation);
assert.calledWith(fakeStore.removeDraft, annotation);
});
});
});
......@@ -1135,7 +1134,7 @@ describe('annotation', function() {
.controller;
controller.edit();
controller.revert();
assert.calledWith(fakeDrafts.remove, controller.annotation);
assert.calledWith(fakeStore.removeDraft, controller.annotation);
});
it('deletes the annotation if it was new', function() {
......
......@@ -15,7 +15,6 @@ describe('sidebar.components.hypothesis-app', function() {
let fakeAnalytics = null;
let fakeAuth = null;
let fakeBridge = null;
let fakeDrafts = null;
let fakeFeatures = null;
let fakeFlash = null;
let fakeFrameSync = null;
......@@ -63,6 +62,10 @@ describe('sidebar.components.hypothesis-app', function() {
clearSelectedAnnotations: sandbox.spy(),
getState: sinon.stub(),
clearGroups: sinon.stub(),
// draft store
countDrafts: sandbox.stub().returns(0),
discardAllDrafts: sandbox.stub(),
unsavedAnnotations: sandbox.stub().returns([]),
};
fakeAnalytics = {
......@@ -72,15 +75,6 @@ describe('sidebar.components.hypothesis-app', function() {
fakeAuth = {};
fakeDrafts = {
contains: sandbox.stub(),
remove: sandbox.spy(),
all: sandbox.stub().returns([]),
discard: sandbox.spy(),
count: sandbox.stub().returns(0),
unsaved: sandbox.stub().returns([]),
};
fakeFeatures = {
fetch: sandbox.spy(),
flagEnabled: sandbox.stub().returns(false),
......@@ -123,7 +117,6 @@ describe('sidebar.components.hypothesis-app', function() {
$provide.value('store', fakeStore);
$provide.value('auth', fakeAuth);
$provide.value('analytics', fakeAnalytics);
$provide.value('drafts', fakeDrafts);
$provide.value('features', fakeFeatures);
$provide.value('flash', fakeFlash);
$provide.value('frameSync', fakeFrameSync);
......@@ -428,7 +421,7 @@ describe('sidebar.components.hypothesis-app', function() {
// Tests shared by both of the contexts below.
function doSharedTests() {
it('prompts the user if there are drafts', function() {
fakeDrafts.count.returns(1);
fakeStore.countDrafts.returns(1);
const ctrl = createController();
ctrl.logout();
......@@ -445,7 +438,7 @@ describe('sidebar.components.hypothesis-app', function() {
});
it('emits "annotationDeleted" for each unsaved draft annotation', function() {
fakeDrafts.unsaved = sandbox
fakeStore.unsavedAnnotations = sandbox
.stub()
.returns(['draftOne', 'draftTwo', 'draftThree']);
const ctrl = createController();
......@@ -473,12 +466,12 @@ describe('sidebar.components.hypothesis-app', function() {
ctrl.logout();
assert(fakeDrafts.discard.calledOnce);
assert(fakeStore.discardAllDrafts.calledOnce);
});
it('does not emit "annotationDeleted" if the user cancels the prompt', function() {
const ctrl = createController();
fakeDrafts.count.returns(1);
fakeStore.countDrafts.returns(1);
$rootScope.$emit = sandbox.stub();
fakeWindow.confirm.returns(false);
......@@ -489,17 +482,17 @@ describe('sidebar.components.hypothesis-app', function() {
it('does not discard drafts if the user cancels the prompt', function() {
const ctrl = createController();
fakeDrafts.count.returns(1);
fakeStore.countDrafts.returns(1);
fakeWindow.confirm.returns(false);
ctrl.logout();
assert(fakeDrafts.discard.notCalled);
assert(fakeStore.discardAllDrafts.notCalled);
});
it('does not prompt if there are no drafts', function() {
const ctrl = createController();
fakeDrafts.count.returns(0);
fakeStore.countDrafts.returns(0);
ctrl.logout();
......@@ -535,7 +528,7 @@ describe('sidebar.components.hypothesis-app', function() {
});
it('does not send LOGOUT_REQUESTED if the user cancels the prompt', function() {
fakeDrafts.count.returns(1);
fakeStore.countDrafts.returns(1);
fakeWindow.confirm.returns(false);
createController().logout();
......
'use strict';
const { createElement } = require('preact');
const { shallow } = require('enzyme');
const LoggedOutMessage = require('../logged-out-message');
describe('LoggedOutMessage', () => {
const createLoggedOutMessage = props => {
return shallow(
<LoggedOutMessage
onLogin={sinon.stub()}
serviceUrl={sinon.stub()}
{...props}
/>
).dive(); // Dive needed because this component uses `withServices`
};
it('should link to signup', () => {
const fakeServiceUrl = sinon.stub().returns('signup_link');
const wrapper = createLoggedOutMessage({ serviceUrl: fakeServiceUrl });
const signupLink = wrapper.find('.logged-out-message__link').at(0);
assert.calledWith(fakeServiceUrl, 'signup');
assert.equal(signupLink.prop('href'), 'signup_link');
});
it('should have a login click handler', () => {
const fakeOnLogin = sinon.stub();
const wrapper = createLoggedOutMessage({ onLogin: fakeOnLogin });
const loginLink = wrapper.find('.logged-out-message__link').at(1);
assert.equal(loginLink.prop('onClick'), fakeOnLogin);
});
});
......@@ -5,32 +5,6 @@ const EventEmitter = require('tiny-emitter');
const events = require('../../events');
const sidebarContent = require('../sidebar-content');
const util = require('../../directive/test/util');
let searchClients;
class FakeSearchClient extends EventEmitter {
constructor(searchFn, opts) {
super();
assert.ok(searchFn);
searchClients.push(this);
this.cancel = sinon.stub();
this.incremental = !!opts.incremental;
this.get = sinon.spy(function(query) {
assert.ok(query.uri);
for (let i = 0; i < query.uri.length; i++) {
const uri = query.uri[i];
this.emit('results', [{ id: uri + '123', group: '__world__' }]);
this.emit('results', [{ id: uri + '456', group: 'private-group' }]);
}
this.emit('end');
});
}
}
class FakeRootThread extends EventEmitter {
constructor() {
......@@ -47,16 +21,11 @@ describe('sidebar.components.sidebar-content', function() {
let store;
let ctrl;
let fakeAnalytics;
let fakeAnnotationMapper;
let fakeDrafts;
let fakeFeatures;
let fakeAnnotations;
let fakeFrameSync;
let fakeGroups;
let fakeRootThread;
let fakeSettings;
let fakeApi;
let fakeStreamer;
let fakeStreamFilter;
let sandbox;
before(function() {
......@@ -70,7 +39,6 @@ describe('sidebar.components.sidebar-content', function() {
beforeEach(() => {
angular.mock.module(function($provide) {
searchClients = [];
sandbox = sinon.sandbox.create();
fakeAnalytics = {
......@@ -78,65 +46,32 @@ describe('sidebar.components.sidebar-content', function() {
events: {},
};
fakeAnnotationMapper = {
loadAnnotations: sandbox.stub(),
unloadAnnotations: sandbox.stub(),
};
fakeFrameSync = {
focusAnnotations: sinon.stub(),
scrollToAnnotation: sinon.stub(),
};
fakeDrafts = {
unsaved: sandbox.stub().returns([]),
};
fakeFeatures = {
flagEnabled: sandbox.stub().returns(true),
};
fakeStreamer = {
setConfig: sandbox.stub(),
connect: sandbox.stub(),
reconnect: sandbox.stub(),
};
fakeStreamFilter = {
resetFilter: sandbox.stub().returnsThis(),
addClause: sandbox.stub().returnsThis(),
getFilter: sandbox.stub().returns({}),
};
fakeGroups = {
focused: sinon.stub().returns({ id: 'foo' }),
focus: sinon.stub(),
fakeAnnotations = {
load: sinon.stub(),
};
fakeRootThread = new FakeRootThread();
fakeSettings = {};
fakeApi = {
search: sinon.stub(),
};
$provide.value('analytics', fakeAnalytics);
$provide.value('annotationMapper', fakeAnnotationMapper);
$provide.value('api', fakeApi);
$provide.value('drafts', fakeDrafts);
$provide.value('features', fakeFeatures);
$provide.value('frameSync', fakeFrameSync);
$provide.value('rootThread', fakeRootThread);
$provide.value('streamer', fakeStreamer);
$provide.value('streamFilter', fakeStreamFilter);
$provide.value('groups', fakeGroups);
$provide.value('annotations', fakeAnnotations);
$provide.value('settings', fakeSettings);
});
sidebarContent.$imports.$mock({
'../search-client': FakeSearchClient,
});
});
afterEach(() => {
......@@ -149,25 +84,17 @@ describe('sidebar.components.sidebar-content', function() {
});
}
function createSidebarContent(
{ userid } = { userid: 'acct:person@example.com' }
) {
return util.createDirective(document, 'sidebarContent', {
auth: {
status: userid ? 'logged-in' : 'logged-out',
userid: userid,
},
search: sinon.stub().returns({ query: sinon.stub() }),
onLogin: sinon.stub(),
});
}
const makeSidebarContentController = () => {
angular.mock.inject(function($componentController, _store_, _$rootScope_) {
$rootScope = _$rootScope_;
$scope = $rootScope.$new();
store = _store_;
store.updateFrameAnnotationFetchStatus = sinon.stub();
store.clearGroups();
store.loadGroups([{ id: 'group-id' }]);
store.focusGroup('group-id');
ctrl = $componentController(
'sidebarContent',
{ $scope: $scope },
......@@ -186,6 +113,23 @@ describe('sidebar.components.sidebar-content', function() {
return sandbox.restore();
});
describe('isLoading', () => {
it("returns true if the document's url isn't known", () => {
assert.isTrue(ctrl.isLoading());
});
it('returns true if annotations are still being fetched', () => {
setFrames([{ uri: 'http://www.example.com' }]);
store.annotationFetchStarted('tag:foo');
assert.isTrue(ctrl.isLoading());
});
it('returns false if annotations have been fetched', () => {
setFrames([{ uri: 'http://www.example.com' }]);
assert.isFalse(ctrl.isLoading());
});
});
describe('showSelectedTabs', () => {
beforeEach(() => {
setFrames([{ uri: 'http://www.example.com' }]);
......@@ -214,211 +158,10 @@ describe('sidebar.components.sidebar-content', function() {
});
});
describe('#loadAnnotations', function() {
it('unloads any existing annotations', function() {
// When new clients connect, all existing annotations should be unloaded
// before reloading annotations for each currently-connected client
store.addAnnotations([{ id: '123' }]);
const uri1 = 'http://example.com/page-a';
let frames = [{ uri: uri1 }];
setFrames(frames);
$scope.$digest();
fakeAnnotationMapper.unloadAnnotations = sandbox.spy();
const uri2 = 'http://example.com/page-b';
frames = frames.concat({ uri: uri2 });
setFrames(frames);
$scope.$digest();
assert.calledWith(
fakeAnnotationMapper.unloadAnnotations,
store.getState().annotations
);
});
it('loads all annotations for a frame', function() {
const uri = 'http://example.com';
setFrames([{ uri: uri }]);
$scope.$digest();
const loadSpy = fakeAnnotationMapper.loadAnnotations;
assert.calledWith(loadSpy, [sinon.match({ id: uri + '123' })]);
assert.calledWith(loadSpy, [sinon.match({ id: uri + '456' })]);
});
it('loads all annotations for a frame with multiple urls', function() {
const uri = 'http://example.com/test.pdf';
const fingerprint = 'urn:x-pdf:fingerprint';
setFrames([
{
uri: uri,
metadata: {
documentFingerprint: 'fingerprint',
link: [
{
href: fingerprint,
},
{
href: uri,
},
],
},
},
]);
$scope.$digest();
const loadSpy = fakeAnnotationMapper.loadAnnotations;
assert.calledWith(loadSpy, [sinon.match({ id: uri + '123' })]);
assert.calledWith(loadSpy, [sinon.match({ id: fingerprint + '123' })]);
assert.calledWith(loadSpy, [sinon.match({ id: uri + '456' })]);
assert.calledWith(loadSpy, [sinon.match({ id: fingerprint + '456' })]);
});
it('loads all annotations for all frames', function() {
const uris = ['http://example.com', 'http://foobar.com'];
setFrames(
uris.map(function(uri) {
return { uri: uri };
})
);
$scope.$digest();
const loadSpy = fakeAnnotationMapper.loadAnnotations;
assert.calledWith(loadSpy, [sinon.match({ id: uris[0] + '123' })]);
assert.calledWith(loadSpy, [sinon.match({ id: uris[0] + '456' })]);
assert.calledWith(loadSpy, [sinon.match({ id: uris[1] + '123' })]);
assert.calledWith(loadSpy, [sinon.match({ id: uris[1] + '456' })]);
});
it('updates annotation fetch status for all frames', function() {
const frameUris = ['http://example.com', 'http://foobar.com'];
setFrames(
frameUris.map(function(frameUri) {
return { uri: frameUri };
})
);
$scope.$digest();
const updateSpy = store.updateFrameAnnotationFetchStatus;
assert.isTrue(updateSpy.calledWith(frameUris[0], true));
assert.isTrue(updateSpy.calledWith(frameUris[1], true));
});
context('when there is a direct-linked group error', () => {
beforeEach(() => {
setFrames([{ uri: 'http://www.example.com' }]);
fakeSettings.group = 'group-id';
store.setDirectLinkedGroupFetchFailed();
$scope.$digest();
});
[null, 'acct:person@example.com'].forEach(userid => {
it('displays same group error message regardless of login state', () => {
const element = createSidebarContent({ userid });
const sidebarContentError = element.find('.sidebar-content-error');
const errorMessage = sidebarContentError.attr(
'logged-in-error-message'
);
assert.equal(errorMessage, "'This group is not available.'");
});
});
it('selectedGroupUnavailable returns true', () => {
assert.isTrue(ctrl.selectedGroupUnavailable());
});
});
context('when there is a direct-linked group selection', () => {
beforeEach(() => {
setFrames([{ uri: 'http://www.example.com' }]);
fakeSettings.group = 'group-id';
store.loadGroups([{ id: fakeSettings.group }]);
store.focusGroup(fakeSettings.group);
fakeGroups.focused.returns({ id: fakeSettings.group });
$scope.$digest();
});
it('selectedGroupUnavailable returns false', () => {
assert.isFalse(ctrl.selectedGroupUnavailable());
});
it('fetches annotations for the direct-linked group', () => {
assert.calledWith(searchClients[0].get, {
uri: ['http://www.example.com'],
group: 'group-id',
});
});
});
context('when there is a direct-linked annotation selection', function() {
const uri = 'http://example.com';
const id = uri + '123';
beforeEach(function() {
setFrames([{ uri: uri }]);
store.selectAnnotations([id]);
$scope.$digest();
});
it("switches to the selected annotation's group", function() {
assert.calledWith(fakeGroups.focus, '__world__');
assert.calledOnce(fakeAnnotationMapper.loadAnnotations);
assert.calledWith(fakeAnnotationMapper.loadAnnotations, [
{ id: uri + '123', group: '__world__' },
]);
});
it('fetches annotations for all groups', function() {
assert.calledWith(searchClients[0].get, { uri: [uri], group: null });
});
it('loads annotations in one batch', function() {
assert.notOk(searchClients[0].incremental);
});
});
context('when there is no selection', function() {
const uri = 'http://example.com';
beforeEach(function() {
setFrames([{ uri: uri }]);
fakeGroups.focused.returns({ id: 'a-group' });
$scope.$digest();
});
it('fetches annotations for the current group', function() {
assert.calledWith(searchClients[0].get, {
uri: [uri],
group: 'a-group',
});
});
it('loads annotations in batches', function() {
assert.ok(searchClients[0].incremental);
});
});
context('when the selected annotation is not available', function() {
const uri = 'http://example.com';
const id = uri + 'does-not-exist';
beforeEach(function() {
setFrames([{ uri: uri }]);
store.selectAnnotations([id]);
fakeGroups.focused.returns({ id: 'private-group' });
$scope.$digest();
});
it('loads annotations from the focused group instead', function() {
assert.calledWith(fakeGroups.focus, 'private-group');
assert.calledWith(fakeAnnotationMapper.loadAnnotations, [
{ group: 'private-group', id: 'http://example.com456' },
]);
});
});
});
function connectFrameAndPerformInitialFetch() {
setFrames([{ uri: 'https://a-page.com' }]);
$scope.$digest();
fakeAnnotationMapper.loadAnnotations.reset();
fakeAnnotations.load.reset();
}
context('when the search URIs of connected frames change', () => {
......@@ -429,7 +172,11 @@ describe('sidebar.components.sidebar-content', function() {
$scope.$digest();
assert.called(fakeAnnotationMapper.loadAnnotations);
assert.calledWith(
fakeAnnotations.load,
['https://a-page.com', 'https://new-frame.com'],
'group-id'
);
});
});
......@@ -444,7 +191,11 @@ describe('sidebar.components.sidebar-content', function() {
store.updateSession(newProfile);
$scope.$digest();
assert.called(fakeAnnotationMapper.loadAnnotations);
assert.calledWith(
fakeAnnotations.load,
['https://a-page.com'],
'group-id'
);
});
it('does not reload annotations if the user ID is the same', () => {
......@@ -457,7 +208,7 @@ describe('sidebar.components.sidebar-content', function() {
store.updateSession(newProfile);
$scope.$digest();
assert.notCalled(fakeAnnotationMapper.loadAnnotations);
assert.notCalled(fakeAnnotations.load);
});
});
......@@ -486,33 +237,30 @@ describe('sidebar.components.sidebar-content', function() {
// annotations loaded.
store.addAnnotations([{ id: '123' }]);
store.addAnnotations = sinon.stub();
fakeDrafts.unsaved.returns([{ id: uri + '123' }, { id: uri + '456' }]);
setFrames([{ uri: uri }]);
$scope.$digest();
fakeAnnotations.load = sinon.stub();
});
function changeGroup() {
fakeGroups.focused.returns({ id: 'different-group' });
$scope.$digest();
}
it('should load annotations for the new group', () => {
const loadSpy = fakeAnnotationMapper.loadAnnotations;
changeGroup();
store.loadGroups([{ id: 'different-group' }]);
store.focusGroup('different-group');
assert.calledWith(fakeAnnotationMapper.unloadAnnotations, [
sinon.match({ id: '123' }),
]);
$scope.$digest();
assert.calledWith(loadSpy, [sinon.match({ id: uri + '123' })]);
assert.calledWith(loadSpy, [sinon.match({ id: uri + '456' })]);
assert.calledWith(
fakeAnnotations.load,
['http://example.com'],
'different-group'
);
});
it('should clear the selection', () => {
store.selectAnnotations(['123']);
store.loadGroups([{ id: 'different-group' }]);
store.focusGroup('different-group');
changeGroup();
$scope.$digest();
assert.isFalse(store.hasSelectedAnnotations());
});
......@@ -562,10 +310,9 @@ describe('sidebar.components.sidebar-content', function() {
});
it("doesn't show a message if the document isn't loaded yet", function() {
// No search requests have been sent yet.
searchClients = [];
// There is a selection but the selected annotation isn't available.
store.selectAnnotations(['missing']);
store.annotationFetchStarted();
$scope.$digest();
assert.isFalse(ctrl.selectedAnnotationUnavailable());
......
......@@ -171,7 +171,10 @@ function startAngularApp(config) {
wrapReactComponent(require('./components/help-link'))
)
.component('helpPanel', require('./components/help-panel'))
.component('loggedoutMessage', require('./components/loggedout-message'))
.component(
'loggedOutMessage',
wrapReactComponent(require('./components/logged-out-message'))
)
.component('markdown', require('./components/markdown'))
.component(
'moderationBanner',
......@@ -204,11 +207,11 @@ function startAngularApp(config) {
.service('analytics', require('./services/analytics'))
.service('annotationMapper', require('./services/annotation-mapper'))
.service('annotations', require('./services/annotations'))
.service('api', require('./services/api'))
.service('apiRoutes', require('./services/api-routes'))
.service('auth', require('./services/oauth-auth'))
.service('bridge', require('../shared/bridge'))
.service('drafts', require('./services/drafts'))
.service('features', require('./services/features'))
.service('flash', require('./services/flash'))
.service('frameSync', require('./services/frame-sync').default)
......
'use strict';
const SearchClient = require('../search-client');
// @ngInject
function annotations(annotationMapper, api, store, streamer, streamFilter) {
let searchClient = null;
/**
* Load annotations for all URIs and groupId.
*
* @param {string[]} uris
* @param {string} groupId
*/
function load(uris, groupId) {
annotationMapper.unloadAnnotations(store.savedAnnotations());
// Cancel previously running search client.
if (searchClient) {
searchClient.cancel();
}
if (uris.length > 0) {
searchAndLoad(uris, groupId);
streamFilter.resetFilter().addClause('/uri', 'one_of', uris);
streamer.setConfig('filter', { filter: streamFilter.getFilter() });
}
}
function searchAndLoad(uris, groupId) {
searchClient = new SearchClient(api.search, {
incremental: true,
});
searchClient.on('results', results => {
if (results.length) {
annotationMapper.loadAnnotations(results);
}
});
searchClient.on('error', error => {
console.error(error);
});
searchClient.on('end', () => {
// Remove client as it's no longer active.
searchClient = null;
store.frames().forEach(function(frame) {
if (0 <= uris.indexOf(frame.uri)) {
store.updateFrameAnnotationFetchStatus(frame.uri, true);
}
});
store.annotationFetchFinished();
});
store.annotationFetchStarted();
searchClient.get({ uri: uris, group: groupId });
}
return {
load,
};
}
module.exports = annotations;
'use strict';
/**
* Return true if a given `draft` is empty and can be discarded without losing
* any user input
*/
function isEmpty(draft) {
if (!draft) {
return true;
}
return !draft.text && draft.tags.length === 0;
}
/**
* The drafts service provides temporary storage for unsaved edits to new or
* existing annotations.
*
* A draft consists of:
*
* 1. `model` which is the original annotation domain model object which the
* draft is associated with. Domain model objects are never returned from
* the drafts service, they're only used to identify the correct draft to
* return.
*
* 2. `isPrivate` (boolean), `tags` (array of objects) and `text` (string)
* which are the user's draft changes to the annotation. These are returned
* from the drafts service by `drafts.get()`.
*
*/
function DraftStore() {
this._drafts = [];
/**
* Returns true if 'draft' is a draft for a given
* annotation.
*
* Annotations are matched by ID or local tag.
*/
function match(draft, model) {
return (
(draft.model.$tag && model.$tag === draft.model.$tag) ||
(draft.model.id && model.id === draft.model.id)
);
}
/**
* Returns the number of drafts - both unsaved new annotations, and unsaved
* edits to saved annotations - currently stored.
*/
this.count = function count() {
return this._drafts.length;
};
/**
* Returns a list of local tags of new annotations for which unsaved drafts
* exist.
*
* @return {Array<{$tag: string}>}
*/
this.unsaved = function unsaved() {
return this._drafts
.filter(function(draft) {
return !draft.model.id;
})
.map(function(draft) {
return draft.model;
});
};
/** Retrieve the draft changes for an annotation. */
this.get = function get(model) {
for (let i = 0; i < this._drafts.length; i++) {
const draft = this._drafts[i];
if (match(draft, model)) {
return {
isPrivate: draft.isPrivate,
tags: draft.tags,
text: draft.text,
};
}
}
return null;
};
/**
* Returns the draft changes for an annotation, or null if no draft exists
* or the draft is empty.
*/
this.getIfNotEmpty = function(model) {
const draft = this.get(model);
return isEmpty(draft) ? null : draft;
};
/**
* Update the draft version for a given annotation, replacing any
* existing draft.
*/
this.update = function update(model, changes) {
const newDraft = {
model: { id: model.id, $tag: model.$tag },
isPrivate: changes.isPrivate,
tags: changes.tags,
text: changes.text,
};
this.remove(model);
this._drafts.push(newDraft);
};
/** Remove the draft version of an annotation. */
this.remove = function remove(model) {
this._drafts = this._drafts.filter(function(draft) {
return !match(draft, model);
});
};
/** Remove all drafts. */
this.discard = function discard() {
this._drafts = [];
};
}
module.exports = function() {
return new DraftStore();
};
......@@ -286,7 +286,10 @@ function auth(
oauthClient(),
]);
await client.revokeToken(token.accessToken);
// eslint-disable-next-line require-atomic-updates
tokenInfoPromise = Promise.resolve(null);
localStorage.removeItem(storageKey());
}
......
......@@ -40,7 +40,7 @@ const sortFns = {
* The root thread is then displayed by viewer.html
*/
// @ngInject
function RootThread($rootScope, store, drafts, searchFilter, viewFilter) {
function RootThread($rootScope, store, searchFilter, viewFilter) {
/**
* Build the root conversation thread from the given UI state.
*
......@@ -86,10 +86,10 @@ function RootThread($rootScope, store, drafts, searchFilter, viewFilter) {
store
.getState()
.annotations.filter(function(ann) {
return metadata.isNew(ann) && !drafts.getIfNotEmpty(ann);
return metadata.isNew(ann) && !store.getDraftIfNotEmpty(ann);
})
.forEach(function(ann) {
drafts.remove(ann);
store.removeDraft(ann);
$rootScope.$broadcast(events.ANNOTATION_DELETED, ann);
});
}
......
'use strict';
const annotations = require('../annotations');
const EventEmitter = require('tiny-emitter');
let searchClients;
let longRunningSearchClient = false;
class FakeSearchClient extends EventEmitter {
constructor(searchFn, opts) {
super();
assert.ok(searchFn);
searchClients.push(this);
this.cancel = sinon.stub();
this.incremental = !!opts.incremental;
this.get = sinon.spy(query => {
assert.ok(query.uri);
for (let i = 0; i < query.uri.length; i++) {
const uri = query.uri[i];
this.emit('results', [{ id: uri + '123', group: '__world__' }]);
this.emit('results', [{ id: uri + '456', group: 'private-group' }]);
}
if (!longRunningSearchClient) {
this.emit('end');
}
});
}
}
describe('annotations', () => {
let fakeStore;
let fakeApi;
let fakeAnnotationMapper;
let fakeStreamer;
let fakeStreamFilter;
let fakeUris;
let fakeGroupId;
beforeEach(() => {
sinon.stub(console, 'error');
searchClients = [];
longRunningSearchClient = false;
fakeAnnotationMapper = {
loadAnnotations: sinon.stub(),
unloadAnnotations: sinon.stub(),
};
fakeApi = {
search: sinon.stub(),
};
fakeStore = {
getState: sinon.stub(),
frames: sinon.stub(),
searchUris: sinon.stub(),
savedAnnotations: sinon.stub(),
hasSelectedAnnotations: sinon.stub(),
updateFrameAnnotationFetchStatus: sinon.stub(),
annotationFetchStarted: sinon.stub(),
annotationFetchFinished: sinon.stub(),
};
fakeStreamer = {
setConfig: sinon.stub(),
connect: sinon.stub(),
reconnect: sinon.stub(),
};
fakeStreamFilter = {
resetFilter: sinon.stub().returns({
addClause: sinon.stub(),
}),
getFilter: sinon.stub().returns({}),
};
fakeUris = ['http://example.com'];
fakeGroupId = 'group-id';
annotations.$imports.$mock({
'../search-client': FakeSearchClient,
});
});
afterEach(() => {
console.error.restore();
annotations.$imports.$restore();
});
function service() {
fakeStore.frames.returns(
fakeUris.map(uri => {
return { uri: uri };
})
);
return annotations(
fakeAnnotationMapper,
fakeApi,
fakeStore,
fakeStreamer,
fakeStreamFilter
);
}
describe('load', () => {
it('unloads any existing annotations', () => {
// When new clients connect, all existing annotations should be unloaded
// before reloading annotations for each currently-connected client.
fakeStore.savedAnnotations.returns([
{ id: fakeUris[0] + '123' },
{ id: fakeUris[0] + '456' },
]);
const svc = service();
svc.load(fakeUris, fakeGroupId);
assert.calledWith(fakeAnnotationMapper.unloadAnnotations, [
sinon.match({ id: fakeUris[0] + '123' }),
sinon.match({ id: fakeUris[0] + '456' }),
]);
});
it('loads all annotations for a URI', () => {
const svc = service();
svc.load(fakeUris, fakeGroupId);
assert.calledWith(fakeAnnotationMapper.loadAnnotations, [
sinon.match({ id: fakeUris[0] + '123' }),
]);
assert.calledWith(fakeAnnotationMapper.loadAnnotations, [
sinon.match({ id: fakeUris[0] + '456' }),
]);
});
it('loads all annotations for a frame with multiple URIs', () => {
const uri = 'http://example.com/test.pdf';
const fingerprint = 'urn:x-pdf:fingerprint';
fakeUris = [uri, fingerprint];
const svc = service();
// Override the default frames set by the service call above.
fakeStore.frames.returns([
{
uri: uri,
metadata: {
documentFingerprint: 'fingerprint',
link: [
{
href: fingerprint,
},
{
href: uri,
},
],
},
},
]);
svc.load(fakeUris, fakeGroupId);
assert.calledWith(fakeAnnotationMapper.loadAnnotations, [
sinon.match({ id: uri + '123' }),
]);
assert.calledWith(fakeAnnotationMapper.loadAnnotations, [
sinon.match({ id: fingerprint + '123' }),
]);
assert.calledWith(fakeAnnotationMapper.loadAnnotations, [
sinon.match({ id: uri + '456' }),
]);
assert.calledWith(fakeAnnotationMapper.loadAnnotations, [
sinon.match({ id: fingerprint + '456' }),
]);
});
it('loads all annotations for all URIs', () => {
fakeUris = ['http://example.com', 'http://foobar.com'];
const svc = service();
svc.load(fakeUris, fakeGroupId);
[
fakeUris[0] + '123',
fakeUris[0] + '456',
fakeUris[1] + '123',
fakeUris[1] + '456',
].forEach(uri => {
assert.calledWith(fakeAnnotationMapper.loadAnnotations, [
sinon.match({ id: uri }),
]);
});
});
it('updates annotation fetch status for all frames', () => {
fakeUris = ['http://example.com', 'http://foobar.com'];
const svc = service();
svc.load(fakeUris, fakeGroupId);
assert.calledWith(
fakeStore.updateFrameAnnotationFetchStatus,
fakeUris[0],
true
);
assert.calledWith(
fakeStore.updateFrameAnnotationFetchStatus,
fakeUris[1],
true
);
});
it('fetches annotations for the specified group', () => {
const svc = service();
svc.load(fakeUris, fakeGroupId);
assert.calledWith(searchClients[0].get, {
uri: fakeUris,
group: fakeGroupId,
});
});
it('loads annotations in batches', () => {
const svc = service();
svc.load(fakeUris, fakeGroupId);
assert.ok(searchClients[0].incremental);
});
it("cancels previously search client if it's still running", () => {
const svc = service();
// Issue a long running load annotations request.
longRunningSearchClient = true;
svc.load(fakeUris, fakeGroupId);
// Issue another load annotations request while the
// previous annotation load is still running.
svc.load(fakeUris, fakeGroupId);
assert.calledOnce(searchClients[0].cancel);
});
it('does not load annotations if URIs list is empty', () => {
fakeUris = [];
const svc = service();
svc.load(fakeUris, fakeGroupId);
assert.notCalled(fakeAnnotationMapper.loadAnnotations);
});
it('calls annotationFetchStarted when it starts searching for annotations', () => {
const svc = service();
svc.load(fakeUris, fakeGroupId);
assert.calledOnce(fakeStore.annotationFetchStarted);
});
it('calls annotationFetchFinished when all annotations have been found', () => {
const svc = service();
svc.load(fakeUris, fakeGroupId);
assert.calledOnce(fakeStore.annotationFetchFinished);
});
it('logs an error to the console if the search client runs into an error', () => {
const svc = service();
const error = new Error('search for annotations failed');
svc.load(fakeUris, fakeGroupId);
searchClients[0].emit('error', error);
assert.calledWith(console.error, error);
});
});
});
'use strict';
const draftsService = require('../drafts');
const fixtures = {
draftWithText: {
isPrivate: false,
text: 'some text',
tags: [],
},
draftWithTags: {
isPrivate: false,
text: '',
tags: ['atag'],
},
emptyDraft: {
isPrivate: false,
text: '',
tags: [],
},
};
describe('drafts', function() {
let drafts;
beforeEach(function() {
drafts = draftsService();
});
describe('#getIfNotEmpty', function() {
it('returns the draft if it has tags', function() {
const model = { id: 'foo' };
drafts.update(model, fixtures.draftWithTags);
assert.deepEqual(drafts.getIfNotEmpty(model), fixtures.draftWithTags);
});
it('returns the draft if it has text', function() {
const model = { id: 'foo' };
drafts.update(model, fixtures.draftWithText);
assert.deepEqual(drafts.getIfNotEmpty(model), fixtures.draftWithText);
});
it('returns null if the text and tags are empty', function() {
const model = { id: 'foo' };
drafts.update(model, fixtures.emptyDraft);
assert.isNull(drafts.getIfNotEmpty(model));
});
});
describe('#update', function() {
it('should save changes', function() {
const model = { id: 'foo' };
assert.notOk(drafts.get(model));
drafts.update(model, { isPrivate: true, tags: ['foo'], text: 'edit' });
assert.deepEqual(drafts.get(model), {
isPrivate: true,
tags: ['foo'],
text: 'edit',
});
});
it('should replace existing drafts', function() {
const model = { id: 'foo' };
drafts.update(model, { isPrivate: true, tags: ['foo'], text: 'foo' });
drafts.update(model, { isPrivate: true, tags: ['foo'], text: 'bar' });
assert.equal(drafts.get(model).text, 'bar');
});
it('should replace existing drafts with the same ID', function() {
const modelA = { id: 'foo' };
const modelB = { id: 'foo' };
drafts.update(modelA, { isPrivate: true, tags: ['foo'], text: 'foo' });
drafts.update(modelB, { isPrivate: true, tags: ['foo'], text: 'bar' });
assert.equal(drafts.get(modelA).text, 'bar');
});
it('should replace drafts with the same tag', function() {
const modelA = { $tag: 'foo' };
const modelB = { $tag: 'foo' };
drafts.update(modelA, { isPrivate: true, tags: ['foo'], text: 'foo' });
drafts.update(modelB, { isPrivate: true, tags: ['foo'], text: 'bar' });
assert.equal(drafts.get(modelA).text, 'bar');
});
});
describe('#remove', function() {
it('should remove drafts', function() {
const model = { id: 'foo' };
drafts.update(model, { text: 'bar' });
drafts.remove(model);
assert.notOk(drafts.get(model));
});
});
describe('#unsaved', function() {
it('should return drafts for unsaved annotations', function() {
const model = { $tag: 'local-tag', id: undefined };
drafts.update(model, { text: 'bar' });
assert.deepEqual(drafts.unsaved(), [model]);
});
it('should not return drafts for saved annotations', function() {
const model = { id: 'foo' };
drafts.update(model, { text: 'baz' });
assert.deepEqual(drafts.unsaved(), []);
});
});
});
......@@ -27,7 +27,6 @@ const fixtures = immutable({
describe('rootThread', function() {
let fakeStore;
let fakeBuildThread;
let fakeDrafts;
let fakeSearchFilter;
let fakeViewFilter;
......@@ -62,15 +61,12 @@ describe('rootThread', function() {
addAnnotations: sinon.stub(),
setCollapsed: sinon.stub(),
selectTab: sinon.stub(),
getDraftIfNotEmpty: sinon.stub().returns(null),
removeDraft: sinon.stub(),
};
fakeBuildThread = sinon.stub().returns(fixtures.emptyThread);
fakeDrafts = {
getIfNotEmpty: sinon.stub().returns(null),
remove: sinon.stub(),
};
fakeSearchFilter = {
generateFacetedFilter: sinon.stub(),
};
......@@ -82,7 +78,6 @@ describe('rootThread', function() {
angular
.module('app', [])
.value('store', fakeStore)
.value('drafts', fakeDrafts)
.value('searchFilter', fakeSearchFilter)
.value('viewFilter', fakeViewFilter)
.service('rootThread', rootThreadFactory);
......@@ -401,16 +396,16 @@ describe('rootThread', function() {
});
it('removes drafts for new and empty annotations', function() {
fakeDrafts.getIfNotEmpty.returns(null);
fakeStore.getDraftIfNotEmpty.returns(null);
const annotation = annotationFixtures.newEmptyAnnotation();
$rootScope.$broadcast(events.BEFORE_ANNOTATION_CREATED, annotation);
assert.calledWith(fakeDrafts.remove, existingNewAnnot);
assert.calledWith(fakeStore.removeDraft, existingNewAnnot);
});
it('deletes new and empty annotations', function() {
fakeDrafts.getIfNotEmpty.returns(null);
fakeStore.getDraftIfNotEmpty.returns(null);
const annotation = annotationFixtures.newEmptyAnnotation();
$rootScope.$broadcast(events.BEFORE_ANNOTATION_CREATED, annotation);
......@@ -419,14 +414,14 @@ describe('rootThread', function() {
});
it('does not remove annotations that have non-empty drafts', function() {
fakeDrafts.getIfNotEmpty.returns(fixtures.nonEmptyDraft);
fakeStore.getDraftIfNotEmpty.returns(fixtures.nonEmptyDraft);
$rootScope.$broadcast(
events.BEFORE_ANNOTATION_CREATED,
annotationFixtures.newAnnotation()
);
assert.notCalled(fakeDrafts.remove);
assert.notCalled(fakeStore.removeDraft);
assert.notCalled(onDelete);
});
......@@ -439,7 +434,7 @@ describe('rootThread', function() {
annotationFixtures.newAnnotation()
);
assert.notCalled(fakeDrafts.remove);
assert.notCalled(fakeStore.removeDraft);
assert.notCalled(onDelete);
});
});
......
......@@ -4,6 +4,11 @@
* Unicode combining characters
* from http://xregexp.com/addons/unicode/unicode-categories.js line:30
*/
// Lint warning suppressed to faciliate ESLint upgrade. The warning may still
// be valid.
//
// eslint-disable-next-line no-misleading-character-class
const COMBINING_MARKS = /[\u0300-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08E4-\u08FE\u0900-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B62\u0B63\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C01-\u0C03\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C82\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0D02\u0D03\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D82\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102B-\u103E\u1056-\u1059\u105E-\u1060\u1062-\u1064\u1067-\u106D\u1071-\u1074\u1082-\u108D\u108F\u109A-\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4-\u17D3\u17DD\u180B-\u180D\u18A9\u1920-\u192B\u1930-\u193B\u19B0-\u19C0\u19C8\u19C9\u1A17-\u1A1B\u1A55-\u1A5E\u1A60-\u1A7C\u1A7F\u1B00-\u1B04\u1B34-\u1B44\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAD\u1BE6-\u1BF3\u1C24-\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF2-\u1CF4\u1DC0-\u1DE6\u1DFC-\u1DFF\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA880\uA881\uA8B4-\uA8C4\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAA7B\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEB-\uAAEF\uAAF5\uAAF6\uABE3-\uABEA\uABEC\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE26]/g;
// @ngInject
......
......@@ -37,6 +37,7 @@ const debugMiddleware = require('./debug-middleware');
const activity = require('./modules/activity');
const annotations = require('./modules/annotations');
const directLinked = require('./modules/direct-linked');
const drafts = require('./modules/drafts');
const frames = require('./modules/frames');
const links = require('./modules/links');
const groups = require('./modules/groups');
......@@ -89,6 +90,7 @@ function store($rootScope, settings) {
activity,
annotations,
directLinked,
drafts,
frames,
links,
groups,
......
'use strict';
const util = require('../util');
/**
* The drafts store provides temporary storage for unsaved edits to new or
* existing annotations.
*/
function init() {
return {
drafts: [],
};
}
/**
* Helper class to encapsulate the draft properties and a few simple methods.
*
* A draft consists of:
*
* 1. `annotation` which is the original annotation object which the
* draft is associated with. If this is just a draft, then this may
* not have an id yet and instead, $tag is used.
*
* 2. `isPrivate` (boolean), `tags` (array of objects) and `text` (string)
* which are the user's draft changes to the annotation. These are returned
* from the drafts store selector by `drafts.getDraft()`.
*/
class Draft {
constructor(annotation, changes) {
this.annotation = { id: annotation.id, $tag: annotation.$tag };
this.isPrivate = changes.isPrivate;
this.tags = changes.tags;
this.text = changes.text;
}
/**
* Returns true if this draft matches a given annotation.
*
* Annotations are matched by ID or local tag.
*/
match(annotation) {
return (
(this.annotation.$tag && annotation.$tag === this.annotation.$tag) ||
(this.annotation.id && annotation.id === this.annotation.id)
);
}
/**
* Return true if this draft is empty and can be discarded without losing
* any user input.
*/
isEmpty() {
return !this.text && this.tags.length === 0;
}
}
/* Reducer */
const update = {
DISCARD_ALL_DRAFTS: function() {
return {
drafts: [],
};
},
REMOVE_DRAFT: function(state, action) {
const drafts = state.drafts.filter(draft => {
return !draft.match(action.annotation);
});
return {
drafts,
};
},
UPDATE_DRAFT: function(state, action) {
// removes a matching existing draft, then adds
const drafts = state.drafts.filter(draft => {
return !draft.match(action.draft.annotation);
});
drafts.push(action.draft); // push ok since its a copy
return {
drafts,
};
},
};
const actions = util.actionTypes(update);
/* Actions */
/**
* Create or update the draft version for a given annotation by
* replacing any existing draft or simply creating a new one.
*/
function createDraft(annotation, changes) {
return {
type: actions.UPDATE_DRAFT,
draft: new Draft(annotation, changes),
};
}
/** Remove all drafts. */
function discardAllDrafts() {
return {
type: actions.DISCARD_ALL_DRAFTS,
};
}
/** Remove the draft version of an annotation. */
function removeDraft(annotation) {
return {
type: actions.REMOVE_DRAFT,
annotation,
};
}
/* Selectors */
/**
* Returns the number of drafts - both unsaved new annotations, and unsaved
* edits to saved annotations - currently stored.
*
* @return {number}
*/
function countDrafts(state) {
return state.drafts.length;
}
/**
* Retrieve the draft changes for an annotation.
*
* @return {Draft|null}
*/
function getDraft(state, annotation) {
for (let i = 0; i < state.drafts.length; i++) {
const draft = state.drafts[i];
if (draft.match(annotation)) {
return draft;
}
}
return null;
}
/**
* Returns the draft changes for an annotation, or null if no draft exists
* or the draft is empty.
*
* @return {Draft|null}
*/
function getDraftIfNotEmpty(state, annotation) {
const draft = getDraft(state, annotation);
if (!draft) {
return null;
}
return draft.isEmpty() ? null : draft;
}
/**
* Returns a list of draft annotations which have no id.
*
* @return {Object[]}
*/
function unsavedAnnotations(state) {
return state.drafts
.filter(draft => !draft.annotation.id)
.map(draft => draft.annotation);
}
module.exports = {
init,
update,
actions: {
createDraft,
discardAllDrafts,
removeDraft,
},
selectors: {
countDrafts,
getDraft,
getDraftIfNotEmpty,
unsavedAnnotations,
},
Draft,
};
'use strict';
const immutable = require('seamless-immutable');
const drafts = require('../drafts');
const { Draft } = require('../drafts');
const createStore = require('../../create-store');
const fixtures = immutable({
draftWithText: {
isPrivate: false,
text: 'some text',
tags: [],
},
draftWithTags: {
isPrivate: false,
text: '',
tags: ['atag'],
},
emptyDraft: {
isPrivate: false,
text: '',
tags: [],
},
annotation: {
id: 'my_annotation',
$tag: 'my_annotation_tag',
},
});
describe('Drafts Store', () => {
let store;
beforeEach(() => {
store = createStore([drafts]);
});
describe('Draft', () => {
it('constructor', () => {
const draft = new Draft(fixtures.annotation, fixtures.draftWithText);
assert.deepEqual(draft, {
annotation: {
...fixtures.annotation,
},
...fixtures.draftWithText,
});
});
describe('#isEmpty', () => {
it('returns false if draft has tags or text', () => {
const draft = new Draft(fixtures.annotation, fixtures.draftWithText);
assert.isFalse(draft.isEmpty());
});
it('returns true if draft has no tags or text', () => {
const draft = new Draft(fixtures.annotation, fixtures.emptyDraft);
assert.isTrue(draft.isEmpty());
});
});
describe('#match', () => {
it('matches an annotation with the same tag or id', () => {
const draft = new Draft(fixtures.annotation, fixtures.draftWithText);
assert.isTrue(
draft.match({
id: fixtures.annotation.id,
})
);
assert.isTrue(
draft.match({
$tag: fixtures.annotation.$tag,
})
);
});
it('does not match an annotation with a different tag or id', () => {
const draft = new Draft(fixtures.annotation, fixtures.draftWithText);
assert.isFalse(
draft.match({
id: 'fake',
})
);
assert.isFalse(
draft.match({
$tag: 'fake',
})
);
});
});
});
describe('#getDraftIfNotEmpty', () => {
it('returns the draft if it has tags', () => {
store.createDraft(fixtures.annotation, fixtures.draftWithTags);
assert.deepEqual(
store.getDraftIfNotEmpty(fixtures.annotation).annotation,
fixtures.annotation
);
});
it('returns the draft if it has text', () => {
store.createDraft(fixtures.annotation, fixtures.draftWithText);
assert.deepEqual(
store.getDraftIfNotEmpty(fixtures.annotation).annotation,
fixtures.annotation
);
});
it('returns null if the text and tags are empty', () => {
store.createDraft(fixtures.annotation, fixtures.emptyDraft);
assert.isNull(store.getDraftIfNotEmpty(fixtures.annotation));
});
it('returns null if there is no matching draft', () => {
assert.isNull(store.getDraftIfNotEmpty('fake'));
});
});
describe('#createDraft', () => {
it('should save changes', () => {
assert.notOk(store.getDraft(fixtures.annotation));
store.createDraft(fixtures.annotation, fixtures.draftWithText);
assert.deepEqual(
store.getDraft(fixtures.annotation),
new Draft(fixtures.annotation, fixtures.draftWithText)
);
});
it('should replace existing drafts with the same ID', () => {
const fakeAnnotation = {
id: 'my_annotation',
};
const fakeDraft = {
isPrivate: true,
tags: ['foo'],
text: '',
};
store.createDraft(fakeAnnotation, {
...fakeDraft,
text: 'foo',
});
assert.equal(store.getDraft(fakeAnnotation).text, 'foo');
// now replace the draft
store.createDraft(fakeAnnotation, {
...fakeDraft,
text: 'bar',
});
assert.equal(store.getDraft(fakeAnnotation).text, 'bar');
});
it('should replace existing drafts with the same tag', () => {
const fakeAnnotation = {
$tag: 'my_annotation_tag',
};
const fakeDraft = {
isPrivate: true,
tags: ['foo'],
text: '',
};
store.createDraft(fakeAnnotation, {
...fakeDraft,
text: 'foo',
});
assert.equal(store.getDraft(fakeAnnotation).text, 'foo');
// now replace the draft
store.createDraft(fakeAnnotation, {
...fakeDraft,
text: 'bar',
});
assert.equal(store.getDraft(fakeAnnotation).text, 'bar');
});
});
describe('#countDrafts', () => {
it('should count drafts', () => {
assert.equal(store.countDrafts(), 0);
store.createDraft({ id: '1' }, fixtures.draftWithText);
assert.equal(store.countDrafts(), 1);
// since same id, this performs a replace, should still be 1 count
store.createDraft({ id: '1' }, fixtures.draftWithText);
assert.equal(store.countDrafts(), 1);
store.createDraft({ id: '2' }, fixtures.draftWithText);
assert.equal(store.countDrafts(), 2);
});
});
describe('#discardAllDrafts', () => {
it('should remove all drafts', () => {
store.createDraft({ id: '1' }, fixtures.draftWithText);
store.createDraft({ id: '2' }, fixtures.draftWithText);
store.discardAllDrafts(fixtures.annotation);
assert.equal(store.countDrafts(), 0);
});
});
describe('#removeDraft', () => {
it('should remove drafts', () => {
store.createDraft(fixtures.annotation, fixtures.draftWithText);
assert.isOk(store.getDraft(fixtures.annotation));
store.removeDraft(fixtures.annotation);
assert.isNotOk(store.getDraft(fixtures.annotation));
});
});
describe('#unsavedAnnotations', () => {
it('should return unsaved annotations which have drafts', () => {
const fakeAnnotation1 = {
$tag: 'local-tag1',
id: undefined,
};
const fakeAnnotation2 = {
$tag: 'local-tag2',
id: undefined,
};
store.createDraft(fakeAnnotation1, fixtures.draftWithText);
store.createDraft(fakeAnnotation2, fixtures.draftWithText);
assert.deepEqual(store.unsavedAnnotations(), [
fakeAnnotation1,
fakeAnnotation2,
]);
});
it('should not return saved annotations which have drafts', () => {
store.createDraft(fixtures.annotation, fixtures.draftWithText);
assert.deepEqual(store.unsavedAnnotations(), []);
});
});
});
<!-- message to display to loggedout users when they visit direct linked annotations -->
<li class="loggedout-message">
<span>
This is a public annotation created with Hypothesis.
<br>
To reply or make your own annotations on this document,
<a class="loggedout-message__link" href="{{vm.serviceUrl('signup')}}" target="_blank">create a free account</a>
or
<a class="loggedout-message__link" href="" ng-click="vm.onLogin()">log in</a>.
</span>
<span class="loggedout-message-logo">
<a href="https://hypothes.is">
<i class="h-icon-hypothesis-logo loggedout-message-logo__icon"></i>
</a>
</span>
</li>
......@@ -46,5 +46,5 @@
thread="vm.rootThread">
</thread-list>
<loggedout-message ng-if="vm.shouldShowLoggedOutMessage()" on-login="vm.onLogin()">
</loggedout-message>
<logged-out-message ng-if="vm.shouldShowLoggedOutMessage()" on-login="vm.onLogin()">
</logged-out-message>
......@@ -54,7 +54,6 @@ describe('annotation threading', function() {
angular
.module('app', [])
.service('store', require('../../store'))
.service('drafts', require('../../services/drafts'))
.service('rootThread', require('../../services/root-thread'))
.service('searchFilter', require('../../services/search-filter'))
.service('viewFilter', require('../../services/view-filter'))
......
.loggedout-message {
.logged-out-message {
margin: 25px auto;
width: 70%;
text-align: center;
......@@ -7,7 +7,7 @@
flex-direction: column;
}
.loggedout-message__link {
.logged-out-message__link {
text-decoration: underline;
color: $grey-5;
......@@ -16,11 +16,13 @@
}
}
.loggedout-message-logo {
.logged-out-message__logo {
margin-top: 25px;
display: flex;
justify-content: center;
}
.loggedout-message-logo__icon {
.logged-out-message__logo-icon {
font-size: 30px;
color: $grey-4;
......
......@@ -30,7 +30,7 @@ $base-line-height: 20px;
@import './components/group-list';
@import './components/group-list-item';
@import './components/help-panel';
@import './components/loggedout-message';
@import './components/logged-out-message';
@import './components/markdown';
@import './components/menu';
@import './components/menu-item';
......
......@@ -10,13 +10,13 @@
"@babel/highlight" "^7.0.0"
"@babel/core@^7.1.6":
version "7.5.0"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.5.0.tgz#6ed6a2881ad48a732c5433096d96d1b0ee5eb734"
integrity sha512-6Isr4X98pwXqHvtigw71CKgmhL1etZjPs5A67jL/w0TkLM9eqmFR40YrnJvEc1WnMZFsskjsmid8bHZyxKEAnw==
version "7.5.4"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.5.4.tgz#4c32df7ad5a58e9ea27ad025c11276324e0b4ddd"
integrity sha512-+DaeBEpYq6b2+ZmHx3tHspC+ZRflrvLqwfv8E3hNr5LVQoyBnL8RPKSBCg+rK2W2My9PWlujBiqd0ZPsR9Q6zQ==
dependencies:
"@babel/code-frame" "^7.0.0"
"@babel/generator" "^7.5.0"
"@babel/helpers" "^7.5.0"
"@babel/helpers" "^7.5.4"
"@babel/parser" "^7.5.0"
"@babel/template" "^7.4.4"
"@babel/traverse" "^7.5.0"
......@@ -126,19 +126,7 @@
dependencies:
"@babel/types" "^7.0.0"
"@babel/helper-module-transforms@^7.1.0":
version "7.1.0"
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.1.0.tgz#470d4f9676d9fad50b324cdcce5fbabbc3da5787"
integrity sha512-0JZRd2yhawo79Rcm4w0LwSMILFmFXjugG3yqf+P/UsKsRS1mJCmMwwlHDlMg7Avr9LrvSpp4ZSULO9r8jpCzcw==
dependencies:
"@babel/helper-module-imports" "^7.0.0"
"@babel/helper-simple-access" "^7.1.0"
"@babel/helper-split-export-declaration" "^7.0.0"
"@babel/template" "^7.1.0"
"@babel/types" "^7.0.0"
lodash "^4.17.10"
"@babel/helper-module-transforms@^7.4.4":
"@babel/helper-module-transforms@^7.1.0", "@babel/helper-module-transforms@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.4.4.tgz#96115ea42a2f139e619e98ed46df6019b94414b8"
integrity sha512-3Z1yp8TVQf+B4ynN7WoHPKS8EkdTbgAEy0nU0rs/1Kw4pDgmvYH3rz3aI11KgxKCba2cn7N+tqzV1mY2HMN96w==
......@@ -162,14 +150,7 @@
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250"
integrity sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==
"@babel/helper-regex@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.0.0.tgz#2c1718923b57f9bbe64705ffe5640ac64d9bdb27"
integrity sha512-TR0/N0NDCcUIUEbqV6dCO+LptmmSQFQ7q70lfcEB4URsjD0E1HzicrwUH+ap6BAQ2jhCX9Q4UqZy4wilujWlkg==
dependencies:
lodash "^4.17.10"
"@babel/helper-regex@^7.4.4":
"@babel/helper-regex@^7.0.0", "@babel/helper-regex@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.4.4.tgz#a47e02bc91fb259d2e6727c2a30013e3ac13c4a2"
integrity sha512-Y5nuB/kESmR3tKjU8Nkn1wMGEx1tjJX076HBMeL3XLQCu6vA/YRzuTW0bbb+qRnXvQGn+d6Rx953yffl8vEy7Q==
......@@ -187,17 +168,7 @@
"@babel/traverse" "^7.1.0"
"@babel/types" "^7.0.0"
"@babel/helper-replace-supers@^7.1.0":
version "7.1.0"
resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.1.0.tgz#5fc31de522ec0ef0899dc9b3e7cf6a5dd655f362"
integrity sha512-BvcDWYZRWVuDeXTYZWxekQNO5D4kO55aArwZOTFXw6rlLQA8ZaDicJR1sO47h+HrnCiDFiww0fSPV0d713KBGQ==
dependencies:
"@babel/helper-member-expression-to-functions" "^7.0.0"
"@babel/helper-optimise-call-expression" "^7.0.0"
"@babel/traverse" "^7.1.0"
"@babel/types" "^7.0.0"
"@babel/helper-replace-supers@^7.4.4":
"@babel/helper-replace-supers@^7.1.0", "@babel/helper-replace-supers@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.4.4.tgz#aee41783ebe4f2d3ab3ae775e1cc6f1a90cefa27"
integrity sha512-04xGEnd+s01nY1l15EuMS1rfKktNF+1CkKmHoErDppjAAZL+IUBZpzT748x262HF7fibaQPhbvWUl5HeSt1EXg==
......@@ -215,13 +186,6 @@
"@babel/template" "^7.1.0"
"@babel/types" "^7.0.0"
"@babel/helper-split-export-declaration@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz#3aae285c0311c2ab095d997b8c9a94cad547d813"
integrity sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==
dependencies:
"@babel/types" "^7.0.0"
"@babel/helper-split-export-declaration@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz#ff94894a340be78f53f06af038b205c49d993677"
......@@ -230,28 +194,28 @@
"@babel/types" "^7.4.4"
"@babel/helper-wrap-function@^7.1.0":
version "7.1.0"
resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.1.0.tgz#8cf54e9190706067f016af8f75cb3df829cc8c66"
integrity sha512-R6HU3dete+rwsdAfrOzTlE9Mcpk4RjU3aX3gi9grtmugQY0u79X7eogUvfXA5sI81Mfq1cn6AgxihfN33STjJA==
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.2.0.tgz#c4e0012445769e2815b55296ead43a958549f6fa"
integrity sha512-o9fP1BZLLSrYlxYEYyl2aS+Flun5gtjTIG8iln+XuEzQTs0PLagAGSXUcqruJwD5fM48jzIEggCKpIfWTcR7pQ==
dependencies:
"@babel/helper-function-name" "^7.1.0"
"@babel/template" "^7.1.0"
"@babel/traverse" "^7.1.0"
"@babel/types" "^7.0.0"
"@babel/types" "^7.2.0"
"@babel/helpers@^7.5.0":
version "7.5.1"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.5.1.tgz#65407c741a56ddd59dd86346cd112da3de912db3"
integrity sha512-rVOTDv8sH8kNI72Unenusxw6u+1vEepZgLxeV+jHkhsQlYhzVhzL1EpfoWT7Ub3zpWSv2WV03V853dqsnyoQzA==
"@babel/helpers@^7.5.4":
version "7.5.4"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.5.4.tgz#2f00608aa10d460bde0ccf665d6dcf8477357cf0"
integrity sha512-6LJ6xwUEJP51w0sIgKyfvFMJvIb9mWAfohJp0+m6eHJigkFdcH8duZ1sfhn0ltJRzwUIT/yqqhdSfRpCpL7oow==
dependencies:
"@babel/template" "^7.4.4"
"@babel/traverse" "^7.5.0"
"@babel/types" "^7.5.0"
"@babel/highlight@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0.tgz#f710c38c8d458e6dd9a201afb637fcb781ce99e4"
integrity sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==
version "7.5.0"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.5.0.tgz#56d11312bd9248fa619591d02472be6e8cb32540"
integrity sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==
dependencies:
chalk "^2.0.0"
esutils "^2.0.2"
......@@ -271,6 +235,14 @@
"@babel/helper-remap-async-to-generator" "^7.1.0"
"@babel/plugin-syntax-async-generators" "^7.2.0"
"@babel/plugin-proposal-dynamic-import@^7.5.0":
version "7.5.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.5.0.tgz#e532202db4838723691b10a67b8ce509e397c506"
integrity sha512-x/iMjggsKTFHYC6g11PL7Qy58IK8H5zqfm9e6hu4z1iH2IRyAp9u9dL80zA6R76yFovETFLKz2VJIC2iIPBuFw==
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-syntax-dynamic-import" "^7.2.0"
"@babel/plugin-proposal-json-strings@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz#568ecc446c6148ae6b267f02551130891e29f317"
......@@ -279,10 +251,10 @@
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-syntax-json-strings" "^7.2.0"
"@babel/plugin-proposal-object-rest-spread@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.4.4.tgz#1ef173fcf24b3e2df92a678f027673b55e7e3005"
integrity sha512-dMBG6cSPBbHeEBdFXeQ2QLc5gUpg4Vkaz8octD4aoW/ISO+jBOcsuxYL7bsb5WSu8RLP6boxrBIALEHgoHtO9g==
"@babel/plugin-proposal-object-rest-spread@^7.5.4":
version "7.5.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.5.4.tgz#250de35d867ce8260a31b1fdac6c4fc1baa99331"
integrity sha512-KCx0z3y7y8ipZUMAEEJOyNi11lMb/FOPUjjB113tfowgw0c16EGYos7worCKBcUAh2oG+OBnoUhsnTSoLpV9uA==
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-syntax-object-rest-spread" "^7.2.0"
......@@ -311,6 +283,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-syntax-dynamic-import@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.2.0.tgz#69c159ffaf4998122161ad8ebc5e6d1f55df8612"
integrity sha512-mVxuJ0YroI/h/tbFTPGZR8cv6ai+STMKNBq0f8hFxsxWjl94qqhsb+wXbpNMDPU3cfR1TIsVFzU3nXyZMqyK4w==
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-syntax-json-strings@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz#72bd13f6ffe1d25938129d2a186b11fd62951470"
......@@ -346,10 +325,10 @@
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-transform-async-to-generator@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.4.4.tgz#a3f1d01f2f21cadab20b33a82133116f14fb5894"
integrity sha512-YiqW2Li8TXmzgbXw+STsSqPBPFnGviiaSp6CYOq55X8GQ2SGVLrXB6pNid8HkqkZAzOH6knbai3snhP7v0fNwA==
"@babel/plugin-transform-async-to-generator@^7.5.0":
version "7.5.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.5.0.tgz#89a3848a0166623b5bc481164b5936ab947e887e"
integrity sha512-mqvkzwIGkq0bEF1zLRRiTdjfomZJDV33AH3oQzHVGkI2VzEmXLpKKOBvEVaFZBJdN0XTyH38s9j/Kiqr68dggg==
dependencies:
"@babel/helper-module-imports" "^7.0.0"
"@babel/helper-plugin-utils" "^7.0.0"
......@@ -391,10 +370,10 @@
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-transform-destructuring@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.4.4.tgz#9d964717829cc9e4b601fc82a26a71a4d8faf20f"
integrity sha512-/aOx+nW0w8eHiEHm+BTERB2oJn5D127iye/SUQl7NjHy0lf+j7h4MKMMSOwdazGq9OxgiNADncE+SRJkCxjZpQ==
"@babel/plugin-transform-destructuring@^7.5.0":
version "7.5.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.5.0.tgz#f6c09fdfe3f94516ff074fe877db7bc9ef05855a"
integrity sha512-YbYgbd3TryYYLGyC7ZR+Tq8H/+bCmwoaxHfJHupom5ECstzbRLTch6gOQbhEY9Z4hiCNHEURgq06ykFv9JZ/QQ==
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
......@@ -407,10 +386,10 @@
"@babel/helper-regex" "^7.4.4"
regexpu-core "^4.5.4"
"@babel/plugin-transform-duplicate-keys@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.2.0.tgz#d952c4930f312a4dbfff18f0b2914e60c35530b3"
integrity sha512-q+yuxW4DsTjNceUiTzK0L+AfQ0zD9rWaTLiUqHA8p0gxx7lu1EylenfzjeIWNkPy6e/0VG/Wjw9uf9LueQwLOw==
"@babel/plugin-transform-duplicate-keys@^7.5.0":
version "7.5.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.5.0.tgz#c5dbf5106bf84cdf691222c0974c12b1df931853"
integrity sha512-igcziksHizyQPlX9gfSjHkE2wmoCH3evvD2qR5w29/Dk0SMKE/eOI7f1HhBdNhR/zxJDqrgpoDTq5YSLH/XMsQ==
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
......@@ -451,30 +430,33 @@
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-transform-modules-amd@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.2.0.tgz#82a9bce45b95441f617a24011dc89d12da7f4ee6"
integrity sha512-mK2A8ucqz1qhrdqjS9VMIDfIvvT2thrEsIQzbaTdc5QFzhDjQv2CkJJ5f6BXIkgbmaoax3zBr2RyvV/8zeoUZw==
"@babel/plugin-transform-modules-amd@^7.5.0":
version "7.5.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.5.0.tgz#ef00435d46da0a5961aa728a1d2ecff063e4fb91"
integrity sha512-n20UsQMKnWrltocZZm24cRURxQnWIvsABPJlw/fvoy9c6AgHZzoelAIzajDHAQrDpuKFFPPcFGd7ChsYuIUMpg==
dependencies:
"@babel/helper-module-transforms" "^7.1.0"
"@babel/helper-plugin-utils" "^7.0.0"
babel-plugin-dynamic-import-node "^2.3.0"
"@babel/plugin-transform-modules-commonjs@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.4.4.tgz#0bef4713d30f1d78c2e59b3d6db40e60192cac1e"
integrity sha512-4sfBOJt58sEo9a2BQXnZq+Q3ZTSAUXyK3E30o36BOGnJ+tvJ6YSxF0PG6kERvbeISgProodWuI9UVG3/FMY6iw==
"@babel/plugin-transform-modules-commonjs@^7.5.0":
version "7.5.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.5.0.tgz#425127e6045231360858eeaa47a71d75eded7a74"
integrity sha512-xmHq0B+ytyrWJvQTc5OWAC4ii6Dhr0s22STOoydokG51JjWhyYo5mRPXoi+ZmtHQhZZwuXNN+GG5jy5UZZJxIQ==
dependencies:
"@babel/helper-module-transforms" "^7.4.4"
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/helper-simple-access" "^7.1.0"
babel-plugin-dynamic-import-node "^2.3.0"
"@babel/plugin-transform-modules-systemjs@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.4.4.tgz#dc83c5665b07d6c2a7b224c00ac63659ea36a405"
integrity sha512-MSiModfILQc3/oqnG7NrP1jHaSPryO6tA2kOMmAQApz5dayPxWiHqmq4sWH2xF5LcQK56LlbKByCd8Aah/OIkQ==
"@babel/plugin-transform-modules-systemjs@^7.5.0":
version "7.5.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.5.0.tgz#e75266a13ef94202db2a0620977756f51d52d249"
integrity sha512-Q2m56tyoQWmuNGxEtUyeEkm6qJYFqs4c+XyXH5RAuYxObRNz9Zgj/1g2GMnjYp2EUyEy7YTrxliGCXzecl/vJg==
dependencies:
"@babel/helper-hoist-variables" "^7.4.4"
"@babel/helper-plugin-utils" "^7.0.0"
babel-plugin-dynamic-import-node "^2.3.0"
"@babel/plugin-transform-modules-umd@^7.2.0":
version "7.2.0"
......@@ -615,38 +597,40 @@
regexpu-core "^4.5.4"
"@babel/preset-env@^7.1.6":
version "7.4.5"
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.4.5.tgz#2fad7f62983d5af563b5f3139242755884998a58"
integrity sha512-f2yNVXM+FsR5V8UwcFeIHzHWgnhXg3NpRmy0ADvALpnhB0SLbCvrCRr4BLOUYbQNLS+Z0Yer46x9dJXpXewI7w==
version "7.5.4"
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.5.4.tgz#64bc15041a3cbb0798930319917e70fcca57713d"
integrity sha512-hFnFnouyRNiH1rL8YkX1ANCNAUVC8Djwdqfev8i1415tnAG+7hlA5zhZ0Q/3Q5gkop4HioIPbCEWAalqcbxRoQ==
dependencies:
"@babel/helper-module-imports" "^7.0.0"
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-proposal-async-generator-functions" "^7.2.0"
"@babel/plugin-proposal-dynamic-import" "^7.5.0"
"@babel/plugin-proposal-json-strings" "^7.2.0"
"@babel/plugin-proposal-object-rest-spread" "^7.4.4"
"@babel/plugin-proposal-object-rest-spread" "^7.5.4"
"@babel/plugin-proposal-optional-catch-binding" "^7.2.0"
"@babel/plugin-proposal-unicode-property-regex" "^7.4.4"
"@babel/plugin-syntax-async-generators" "^7.2.0"
"@babel/plugin-syntax-dynamic-import" "^7.2.0"
"@babel/plugin-syntax-json-strings" "^7.2.0"
"@babel/plugin-syntax-object-rest-spread" "^7.2.0"
"@babel/plugin-syntax-optional-catch-binding" "^7.2.0"
"@babel/plugin-transform-arrow-functions" "^7.2.0"
"@babel/plugin-transform-async-to-generator" "^7.4.4"
"@babel/plugin-transform-async-to-generator" "^7.5.0"
"@babel/plugin-transform-block-scoped-functions" "^7.2.0"
"@babel/plugin-transform-block-scoping" "^7.4.4"
"@babel/plugin-transform-classes" "^7.4.4"
"@babel/plugin-transform-computed-properties" "^7.2.0"
"@babel/plugin-transform-destructuring" "^7.4.4"
"@babel/plugin-transform-destructuring" "^7.5.0"
"@babel/plugin-transform-dotall-regex" "^7.4.4"
"@babel/plugin-transform-duplicate-keys" "^7.2.0"
"@babel/plugin-transform-duplicate-keys" "^7.5.0"
"@babel/plugin-transform-exponentiation-operator" "^7.2.0"
"@babel/plugin-transform-for-of" "^7.4.4"
"@babel/plugin-transform-function-name" "^7.4.4"
"@babel/plugin-transform-literals" "^7.2.0"
"@babel/plugin-transform-member-expression-literals" "^7.2.0"
"@babel/plugin-transform-modules-amd" "^7.2.0"
"@babel/plugin-transform-modules-commonjs" "^7.4.4"
"@babel/plugin-transform-modules-systemjs" "^7.4.4"
"@babel/plugin-transform-modules-amd" "^7.5.0"
"@babel/plugin-transform-modules-commonjs" "^7.5.0"
"@babel/plugin-transform-modules-systemjs" "^7.5.0"
"@babel/plugin-transform-modules-umd" "^7.2.0"
"@babel/plugin-transform-named-capturing-groups-regex" "^7.4.5"
"@babel/plugin-transform-new-target" "^7.4.4"
......@@ -661,7 +645,7 @@
"@babel/plugin-transform-template-literals" "^7.4.4"
"@babel/plugin-transform-typeof-symbol" "^7.2.0"
"@babel/plugin-transform-unicode-regex" "^7.4.4"
"@babel/types" "^7.4.4"
"@babel/types" "^7.5.0"
browserslist "^4.6.0"
core-js-compat "^3.1.1"
invariant "^2.2.2"
......@@ -749,10 +733,10 @@
deprecation "^2.0.0"
once "^1.4.0"
"@octokit/request@^4.0.1":
version "4.1.0"
resolved "https://registry.yarnpkg.com/@octokit/request/-/request-4.1.0.tgz#e85dc377113baf2fe24433af8feb20e8a32e21b0"
integrity sha512-RvpQAba4i+BNH0z8i0gPRc1ShlHidj4puQjI/Tno6s+Q3/Mzb0XRSHJiOhpeFrZ22V7Mwjq1E7QS27P5CgpWYA==
"@octokit/request@^5.0.0":
version "5.0.1"
resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.0.1.tgz#6705c9a883db0ac0f58cee717e806b6575d4a199"
integrity sha512-SHOk/APYpfrzV1RNf7Ux8SZi+vZXhMIB2dBr4TQR6ExMX8R4jcy/0gHw26HLe1dWV7Wxe9WzYyDSEC0XwnoCSQ==
dependencies:
"@octokit/endpoint" "^5.1.0"
"@octokit/request-error" "^1.0.1"
......@@ -760,17 +744,17 @@
is-plain-object "^3.0.0"
node-fetch "^2.3.0"
once "^1.4.0"
universal-user-agent "^2.1.0"
universal-user-agent "^3.0.0"
"@octokit/rest@^16.9.0":
version "16.28.2"
resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-16.28.2.tgz#3fc3b8700046ab29ab1e2a4bdf49f89e94f7ba27"
integrity sha512-csuYiHvJ1P/GFDadVn0QhwO83R1+YREjcwCY7ZIezB6aJTRIEidJZj+R7gAkUhT687cqYb4cXTZsDVu9F+Fmug==
version "16.28.4"
resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-16.28.4.tgz#2f8ef08305033bc91256530d6a3c98eada700660"
integrity sha512-ZBsfD46t3VNkwealxm5zloVgQta8d8o4KYBR/hMAZ582IgjmSDKZdkjyv5w37IUCM3tcPZWKUT+kml9pEIC2GA==
dependencies:
"@octokit/request" "^4.0.1"
"@octokit/request" "^5.0.0"
"@octokit/request-error" "^1.0.2"
atob-lite "^2.0.0"
before-after-hook "^1.4.0"
before-after-hook "^2.0.0"
btoa-lite "^1.0.0"
deprecation "^2.0.0"
lodash.get "^4.4.2"
......@@ -778,7 +762,7 @@
lodash.uniq "^4.5.0"
octokit-pagination-methods "^1.1.0"
once "^1.4.0"
universal-user-agent "^2.0.0"
universal-user-agent "^3.0.0"
url-template "^2.0.8"
"@sinonjs/commons@^1", "@sinonjs/commons@^1.0.2", "@sinonjs/commons@^1.4.0":
......@@ -885,10 +869,10 @@ agent-base@^4.1.0:
dependencies:
es6-promisify "^5.0.0"
ajv@^6.5.5, ajv@^6.9.1:
version "6.9.1"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.9.1.tgz#a4d3683d74abc5670e75f0b16520f70a20ea8dc1"
integrity sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==
ajv@^6.10.0, ajv@^6.5.5, ajv@^6.9.1:
version "6.10.1"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.1.tgz#ebf8d3af22552df9dd049bfbe50cc2390e823593"
integrity sha512-w1YQaVGNC6t2UCPjEawK/vo/dG8OOrVtUmhBT1uJJYxbl5kU2Tj3v6LGqBcsysN1yhuCStJCCA3GqdvKY8sqXQ==
dependencies:
fast-deep-equal "^2.0.1"
fast-json-stable-stringify "^2.0.0"
......@@ -989,6 +973,14 @@ anymatch@^2.0.0:
micromatch "^3.1.4"
normalize-path "^2.1.1"
anymatch@^3.0.1:
version "3.0.3"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.0.3.tgz#2fb624fe0e84bccab00afee3d0006ed310f22f09"
integrity sha512-c6IvoeBECQlMVuYUjSwimnhmztImpErfxJzWZhIQinIvQWoGOnB0dLIgifbPHQt5heS6mNlaZG16f06H3C8t1g==
dependencies:
normalize-path "^3.0.0"
picomatch "^2.0.4"
ap@~0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/ap/-/ap-0.2.0.tgz#ae0942600b29912f0d2b14ec60c45e8f330b6110"
......@@ -1341,6 +1333,13 @@ babel-plugin-angularjs-annotate@^0.10.0:
"@babel/types" "^7.2.0"
simple-is "~0.2.0"
babel-plugin-dynamic-import-node@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz#f00f507bdaa3c3e3ff6e7e5e98d90a7acab96f7f"
integrity sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==
dependencies:
object.assign "^4.1.0"
babel-plugin-istanbul@^5.1.0:
version "5.1.4"
resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-5.1.4.tgz#841d16b9a58eeb407a0ddce622ba02fe87a752ba"
......@@ -1452,10 +1451,10 @@ beeper@^1.0.0:
resolved "https://registry.yarnpkg.com/beeper/-/beeper-1.1.1.tgz#e6d5ea8c5dad001304a70b22638447f69cb2f809"
integrity sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=
before-after-hook@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-1.4.0.tgz#2b6bf23dca4f32e628fd2747c10a37c74a4b484d"
integrity sha512-l5r9ir56nda3qu14nAXIlyq1MmUSs0meCIaFAh8HwkFwP1F8eToOuS3ah2VAHHcY04jaYD7FpJC5JTXHYRbkzg==
before-after-hook@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.1.0.tgz#b6c03487f44e24200dd30ca5e6a1979c5d2fb635"
integrity sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A==
better-assert@~1.0.0:
version "1.0.2"
......@@ -1469,6 +1468,11 @@ binary-extensions@^1.0.0:
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205"
integrity sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=
binary-extensions@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c"
integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==
binaryextensions@2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/binaryextensions/-/binaryextensions-2.1.2.tgz#c83c3d74233ba7674e4f313cb2a2b70f54e94b7c"
......@@ -1541,6 +1545,13 @@ braces@^2.3.1, braces@^2.3.2:
split-string "^3.0.2"
to-regex "^3.0.1"
braces@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
dependencies:
fill-range "^7.0.1"
brorand@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
......@@ -1718,6 +1729,15 @@ browserslist@^4.6.0, browserslist@^4.6.3:
electron-to-chromium "^1.3.164"
node-releases "^1.1.23"
browserslist@^4.6.2:
version "4.6.4"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.6.4.tgz#fd0638b3f8867fec2c604ed0ed9300379f8ec7c2"
integrity sha512-ErJT8qGfRt/VWHSr1HeqZzz50DvxHtr1fVL1m5wf20aGrG8e1ce8fpZ2EjZEfs09DDZYSvtRaDlMpWslBf8Low==
dependencies:
caniuse-lite "^1.0.30000981"
electron-to-chromium "^1.3.188"
node-releases "^1.1.25"
btoa-lite@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/btoa-lite/-/btoa-lite-1.0.0.tgz#337766da15801210fdd956c22e9c6891ab9d0337"
......@@ -1836,7 +1856,7 @@ camelcase@^5.0.0:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42"
integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==
caniuse-lite@^1.0.30000975, caniuse-lite@^1.0.30000980:
caniuse-lite@^1.0.30000975, caniuse-lite@^1.0.30000980, caniuse-lite@^1.0.30000981:
version "1.0.30000981"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000981.tgz#5b6828803362363e5a1deba2eb550185cf6cec8f"
integrity sha512-JTByHj4DQgL2crHNMK6PibqAMrqqb/Vvh0JrsTJVSWG4VSUrT16EklkuRZofurlMjgA9e+zlCM4Y39F3kootMQ==
......@@ -1905,7 +1925,7 @@ cheerio@^1.0.0-rc.2:
lodash "^4.15.0"
parse5 "^3.0.1"
chokidar@^2.0.0, chokidar@^2.0.3, chokidar@^2.1.1:
chokidar@^2.0.0, chokidar@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.2.tgz#9c23ea40b01638439e0513864d362aeacc5ad058"
integrity sha512-IwXUx0FXc5ibYmPC2XeEj5mpXoV66sR+t3jqu2NS2GYwCktt3KF1/Qqjws/NkegajBA4RbZ5+DDwlOiJsxDHEg==
......@@ -1924,6 +1944,21 @@ chokidar@^2.0.0, chokidar@^2.0.3, chokidar@^2.1.1:
optionalDependencies:
fsevents "^1.2.7"
chokidar@^3.0.0:
version "3.0.2"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.0.2.tgz#0d1cd6d04eb2df0327446188cd13736a3367d681"
integrity sha512-c4PR2egjNjI1um6bamCQ6bUNPDiyofNQruHvKgHQ4gDUP/ITSVSzNsiI5OWtHOsX323i5ha/kk4YmOZ1Ktg7KA==
dependencies:
anymatch "^3.0.1"
braces "^3.0.2"
glob-parent "^5.0.0"
is-binary-path "^2.1.0"
is-glob "^4.0.1"
normalize-path "^3.0.0"
readdirp "^3.1.1"
optionalDependencies:
fsevents "^2.0.6"
chownr@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181"
......@@ -2071,13 +2106,13 @@ collection-visit@^1.0.0:
object-visit "^1.0.0"
color-convert@^1.9.0:
version "1.9.1"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed"
integrity sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==
version "1.9.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
dependencies:
color-name "^1.1.1"
color-name "1.1.3"
color-name@^1.1.1:
color-name@1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
......@@ -2255,24 +2290,29 @@ copy-props@^2.0.1:
is-plain-object "^2.0.1"
core-js-compat@^3.1.1:
version "3.1.3"
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.1.3.tgz#0cc3ba4c7f62928c2837e1cffbe8dc78b4f1ae14"
integrity sha512-EP018pVhgwsKHz3YoN1hTq49aRe+h017Kjz0NQz3nXV0cCRMvH3fLQl+vEPGr4r4J5sk4sU3tUC7U1aqTCeJeA==
version "3.1.4"
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.1.4.tgz#e4d0c40fbd01e65b1d457980fe4112d4358a7408"
integrity sha512-Z5zbO9f1d0YrJdoaQhphVAnKPimX92D6z8lCGphH89MNRxlL1prI9ExJPqVwP0/kgkQCv8c4GJGT8X16yUncOg==
dependencies:
browserslist "^4.6.0"
core-js-pure "3.1.3"
semver "^6.1.0"
browserslist "^4.6.2"
core-js-pure "3.1.4"
semver "^6.1.1"
core-js-pure@3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.1.3.tgz#4c90752d5b9471f641514f3728f51c1e0783d0b5"
integrity sha512-k3JWTrcQBKqjkjI0bkfXS0lbpWPxYuHWfMMjC1VDmzU4Q58IwSbuXSo99YO/hUHlw/EB4AlfA2PVxOGkrIq6dA==
core-js-pure@3.1.4:
version "3.1.4"
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.1.4.tgz#5fa17dc77002a169a3566cc48dc774d2e13e3769"
integrity sha512-uJ4Z7iPNwiu1foygbcZYJsJs1jiXrTTCvxfLDXNhI/I+NHbSIEyr548y4fcsCEyWY0XgfAG/qqaunJ1SThHenA==
core-js@^2.2.0, core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.7:
core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.7:
version "2.6.9"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2"
integrity sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==
core-js@^3.1.3:
version "3.1.4"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.1.4.tgz#3a2837fc48e582e1ae25907afcd6cf03b0cc7a07"
integrity sha512-YNZN8lt82XIMLnLirj9MhKDFZHalwzzrL9YLt6eb0T5D0EDl4IQ90IGkua8mHbnxNrkj1d8hbdizMc0Qmg1WnQ==
core-util-is@1.0.2, core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
......@@ -2855,7 +2895,7 @@ ee-first@1.1.1:
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
electron-to-chromium@^1.3.164:
electron-to-chromium@^1.3.164, electron-to-chromium@^1.3.188:
version "1.3.188"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.188.tgz#e28e1afe4bb229989e280bfd3b395c7ec03c8b7a"
integrity sha512-tEQcughYIMj8WDMc59EGEtNxdGgwal/oLLTDw+NEqJRJwGflQvH3aiyiexrWeZOETP4/ko78PVr6gwNhdozvuQ==
......@@ -3138,13 +3178,13 @@ eslint-visitor-keys@^1.0.0:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d"
integrity sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==
eslint@^5.12.1:
version "5.16.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea"
integrity sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==
eslint@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.0.1.tgz#4a32181d72cb999d6f54151df7d337131f81cda7"
integrity sha512-DyQRaMmORQ+JsWShYsSg4OPTjY56u1nCjAmICrE8vLWqyLKxhFXOthwMj1SA8xwfrv0CofLNVnqbfyhwCkaO0w==
dependencies:
"@babel/code-frame" "^7.0.0"
ajv "^6.9.1"
ajv "^6.10.0"
chalk "^2.1.0"
cross-spawn "^6.0.5"
debug "^4.0.1"
......@@ -3152,18 +3192,19 @@ eslint@^5.12.1:
eslint-scope "^4.0.3"
eslint-utils "^1.3.1"
eslint-visitor-keys "^1.0.0"
espree "^5.0.1"
espree "^6.0.0"
esquery "^1.0.1"
esutils "^2.0.2"
file-entry-cache "^5.0.1"
functional-red-black-tree "^1.0.1"
glob "^7.1.2"
glob-parent "^3.1.0"
globals "^11.7.0"
ignore "^4.0.6"
import-fresh "^3.0.0"
imurmurhash "^0.1.4"
inquirer "^6.2.2"
js-yaml "^3.13.0"
is-glob "^4.0.0"
js-yaml "^3.13.1"
json-stable-stringify-without-jsonify "^1.0.1"
levn "^0.3.0"
lodash "^4.17.11"
......@@ -3171,7 +3212,6 @@ eslint@^5.12.1:
mkdirp "^0.5.1"
natural-compare "^1.4.0"
optionator "^0.8.2"
path-is-inside "^1.0.2"
progress "^2.0.0"
regexpp "^2.0.1"
semver "^5.5.1"
......@@ -3180,10 +3220,10 @@ eslint@^5.12.1:
table "^5.2.3"
text-table "^0.2.0"
espree@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a"
integrity sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==
espree@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/espree/-/espree-6.0.0.tgz#716fc1f5a245ef5b9a7fdb1d7b0d3f02322e75f6"
integrity sha512-lJvCS6YbCn3ImT3yKkPe0+tJ+mH6ljhGNjHQH9mRtiO6gjhVAOhVXW1yjnwqGwTkK3bGbye+hb00nFNmu0l/1Q==
dependencies:
acorn "^6.0.7"
acorn-jsx "^5.0.0"
......@@ -3541,6 +3581,13 @@ fill-range@^4.0.0:
repeat-string "^1.6.1"
to-regex-range "^2.1.0"
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
dependencies:
to-regex-range "^5.0.1"
finalhandler@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.0.tgz#ce0b6855b45853e791b2fcc680046d88253dd7f5"
......@@ -3665,9 +3712,9 @@ flush-write-stream@^1.0.2:
readable-stream "^2.0.4"
focus-visible@^5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/focus-visible/-/focus-visible-5.0.1.tgz#8bbe2d938bf88f067a252d72ac3a1cb2c3cb4767"
integrity sha512-xmuzsbC+LsF8iHlbBUvLsM6sfqyeUCAJTq3GK9JboFAdCqdDt/d4XoeE9jWT3cct2NFs/BKZ8IM8Yjkk1wgO3Q==
version "5.0.2"
resolved "https://registry.yarnpkg.com/focus-visible/-/focus-visible-5.0.2.tgz#4fae9cf40458b73c10701c9774c462e3ccd53caf"
integrity sha512-zT2fj/bmOgEBjqGbURGlowTmCwsIs3bRDMr/sFZz8Ly7VkEiwuCn9swNTL3pPuf8Oua2de7CLuKdnuNajWdDsQ==
follow-redirects@^1.0.0:
version "1.4.1"
......@@ -3775,6 +3822,11 @@ fsevents@^1.2.7:
nan "^2.12.1"
node-pre-gyp "^0.12.0"
fsevents@^2.0.6:
version "2.0.7"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.0.7.tgz#382c9b443c6cbac4c57187cdda23aa3bf1ccfc2a"
integrity sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ==
fstream@^1.0.0, fstream@^1.0.12:
version "1.0.12"
resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045"
......@@ -3877,6 +3929,13 @@ glob-parent@^3.1.0:
is-glob "^3.1.0"
path-dirname "^1.0.0"
glob-parent@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.0.0.tgz#1dc99f0f39b006d3e92c2c284068382f0c20e954"
integrity sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==
dependencies:
is-glob "^4.0.1"
glob-stream@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-6.1.0.tgz#7045c99413b3eb94888d83ab46d0b404cc7bdde4"
......@@ -3933,7 +3992,7 @@ glob@^6.0.4:
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^7.0.0, glob@^7.0.3, glob@^7.1.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@~7.1.1:
glob@^7.0.0, glob@^7.0.3, glob@^7.1.0, glob@^7.1.1, glob@^7.1.3, glob@~7.1.1:
version "7.1.4"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255"
integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==
......@@ -3965,7 +4024,12 @@ global-prefix@^1.0.1:
is-windows "^1.0.1"
which "^1.2.14"
globals@^11.1.0, globals@^11.7.0:
globals@^11.1.0:
version "11.12.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
globals@^11.7.0:
version "11.10.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-11.10.0.tgz#1e09776dffda5e01816b3bb4077c8b59c24eaa50"
integrity sha512-0GZF1RiPKU97IHUO5TORo9w1PwrH/NBPl+fS7oMLdaTRiYmYbwK4NWoZWrAdd0/abG9R2BU+OiwyQpTpE6pdfQ==
......@@ -4623,6 +4687,13 @@ is-binary-path@^1.0.0:
dependencies:
binary-extensions "^1.0.0"
is-binary-path@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
dependencies:
binary-extensions "^2.0.0"
is-boolean-object@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.0.tgz#98f8b28030684219a95f375cfbd88ce3405dff93"
......@@ -4747,6 +4818,13 @@ is-glob@^4.0.0:
dependencies:
is-extglob "^2.1.1"
is-glob@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
dependencies:
is-extglob "^2.1.1"
is-negated-glob@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-negated-glob/-/is-negated-glob-1.0.0.tgz#6910bca5da8c95e784b5751b976cf5a10fee36d2"
......@@ -4769,6 +4847,11 @@ is-number@^4.0.0:
resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff"
integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==
is-number@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
is-odd@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-odd/-/is-odd-2.0.0.tgz#7646624671fd7ea558ccd9a2795182f2958f1b24"
......@@ -5011,9 +5094,9 @@ js-base64@^2.1.8:
integrity sha512-H7ErYLM34CvDMto3GbD6xD0JLUGYXR3QTcH6B/tr4Hi/QpSThnCsIp+Sy5FRTw3B0d6py4HcNkW7nO/wdtGWEw==
js-levenshtein@^1.1.3:
version "1.1.4"
resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.4.tgz#3a56e3cbf589ca0081eb22cd9ba0b1290a16d26e"
integrity sha512-PxfGzSs0ztShKrUYPIn5r0MtyAhYcCwmndozzpz8YObbPnD1jFxzlBGbRnX2mIu6Z13xN6+PTu05TQFnZFlzow==
version "1.1.6"
resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d"
integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==
js-polyfills@^0.1.16:
version "0.1.42"
......@@ -5030,7 +5113,7 @@ js-string-escape@^1.0.0:
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
js-yaml@3.13.1, js-yaml@^3.12.0, js-yaml@^3.13.0, js-yaml@^3.13.1, js-yaml@^3.9.0:
js-yaml@3.13.1, js-yaml@^3.12.0, js-yaml@^3.13.1, js-yaml@^3.9.0:
version "3.13.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==
......@@ -5198,17 +5281,17 @@ karma-sinon@^1.0.5:
integrity sha1-TjRD8oMP3s/2JNN0cWPxIX2qKpo=
karma@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/karma/-/karma-4.1.0.tgz#d07387c9743a575b40faf73e8a3eb5421c2193e1"
integrity sha512-xckiDqyNi512U4dXGOOSyLKPwek6X/vUizSy2f3geYevbLj+UIdvNwbn7IwfUIL2g1GXEPWt/87qFD1fBbl/Uw==
version "4.2.0"
resolved "https://registry.yarnpkg.com/karma/-/karma-4.2.0.tgz#27e88b310cde090d016980ff5444e3a239196fca"
integrity sha512-fmCuxN1rwJxTdZfOXK5LjlmS4Ana/OvzNMpkyLL/TLE8hmgSkpVpMYQ7RTVa8TNKRVQDZNl5W1oF5cfKfgIMlA==
dependencies:
bluebird "^3.3.0"
body-parser "^1.16.1"
braces "^2.3.2"
chokidar "^2.0.3"
braces "^3.0.2"
chokidar "^3.0.0"
colors "^1.1.0"
connect "^3.6.0"
core-js "^2.2.0"
core-js "^3.1.3"
di "^0.0.1"
dom-serialize "^2.2.0"
flatted "^2.0.0"
......@@ -5529,9 +5612,9 @@ lodash.uniq@^4.5.0:
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
lodash@^4.0.0, lodash@^4.0.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@~4.17.4:
version "4.17.11"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
version "4.17.14"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.14.tgz#9ce487ae66c96254fe20b599f21b6816028078ba"
integrity sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw==
log-symbols@2.2.0, log-symbols@^2.1.0:
version "2.2.0"
......@@ -5967,11 +6050,16 @@ ms@2.0.0:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
ms@2.1.1, ms@^2.1.1:
ms@2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
ms@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
multipipe@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/multipipe/-/multipipe-0.1.2.tgz#2a8f2ddf70eed564dff2d57f1e1a137d9f05078b"
......@@ -6129,10 +6217,10 @@ node-pre-gyp@^0.12.0:
semver "^5.3.0"
tar "^4"
node-releases@^1.1.23:
version "1.1.23"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.23.tgz#de7409f72de044a2fa59c097f436ba89c39997f0"
integrity sha512-uq1iL79YjfYC0WXoHbC/z28q/9pOl8kSHaXdWmAAc8No+bDwqkZbzIJz55g/MUsPgSGm9LZ7QSUbzTcH5tz47w==
node-releases@^1.1.23, node-releases@^1.1.25:
version "1.1.25"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.25.tgz#0c2d7dbc7fed30fbe02a9ee3007b8c90bf0133d3"
integrity sha512-fI5BXuk83lKEoZDdH3gRhtsNgh05/wZacuXkgbiYkceE7+QIMXOg98n9ZV7mz27B+kFHnqHcUpscZZlGRSmTpQ==
dependencies:
semver "^5.3.0"
......@@ -6306,12 +6394,7 @@ object-is@^1.0.1:
resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.1.tgz#0aa60ec9989a0b3ed795cf4d06f62cf1ad6539b6"
integrity sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY=
object-keys@^1.0.11:
version "1.0.12"
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.12.tgz#09c53855377575310cca62f55bb334abff7b3ed2"
integrity sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==
object-keys@^1.0.12:
object-keys@^1.0.11, object-keys@^1.0.12:
version "1.1.1"
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
......@@ -6722,11 +6805,6 @@ path-is-absolute@^1.0.0, path-is-absolute@^1.0.1:
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
path-is-inside@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=
path-key@^2.0.0, path-key@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
......@@ -6828,6 +6906,11 @@ phantomjs-prebuilt@^2.1.7:
request-progress "^2.0.1"
which "^1.2.10"
picomatch@^2.0.4:
version "2.0.7"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.0.7.tgz#514169d8c7cd0bdbeecc8a2609e34a7163de69f6"
integrity sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA==
pify@^2.0.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
......@@ -6905,10 +6988,10 @@ preact-render-to-string@^4.1.0:
dependencies:
pretty-format "^3.8.0"
preact@10.0.0-beta.3:
version "10.0.0-beta.3"
resolved "https://registry.yarnpkg.com/preact/-/preact-10.0.0-beta.3.tgz#dabd0628d941f9e7908f68bb008e012ddff1b28f"
integrity sha512-JDeA+CVFBlv+r+v/tScG8hzOcOedXfBLUA5IhStqpfc7rPGY3T85pdlu4aGo2tV0AoZzQfO4LTnKkQEnKJ3UxQ==
preact@10.0.0-rc.0:
version "10.0.0-rc.0"
resolved "https://registry.yarnpkg.com/preact/-/preact-10.0.0-rc.0.tgz#084316631ae6f0a965e889e7715cccb74c4a5d6e"
integrity sha512-mOOF2FoSt52uYPa+n2cvgI0NI+yo6Rxyamvu5z/8GHod3UhtExWhBcOeTawriR4HiGuR5VHENJEJ/dtBWMZZaQ==
prelude-ls@~1.1.2:
version "1.1.2"
......@@ -7237,6 +7320,13 @@ readdirp@^2.2.1:
micromatch "^3.1.10"
readable-stream "^2.0.2"
readdirp@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.1.1.tgz#b158123ac343c8b0f31d65680269cc0fc1025db1"
integrity sha512-XXdSXZrQuvqoETj50+JAitxz1UPdt5dupjT6T5nVB+WvjMv2XKYj+s7hPeAVCXvmJrL36O4YYyWlIC3an2ePiQ==
dependencies:
picomatch "^2.0.4"
rechoir@^0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384"
......@@ -7258,17 +7348,17 @@ redux-thunk@^2.1.0:
integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==
redux@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.1.tgz#436cae6cc40fbe4727689d7c8fae44808f1bfef5"
integrity sha512-R7bAtSkk7nY6O/OYMVR9RiBI+XghjF9rlbl5806HJbQph0LJVHZrU5oaO4q70eUKiqMRqm4y07KLTlMZ2BlVmg==
version "4.0.4"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.4.tgz#4ee1aeb164b63d6a1bcc57ae4aa0b6e6fa7a3796"
integrity sha512-vKv4WdiJxOWKxK0yRoaK3Y4pxxB0ilzVx6dszU2W8wLxlb2yikRph4iV/ymtdJ6ZxpBLFbyrxklnT5yBbQSl3Q==
dependencies:
loose-envify "^1.4.0"
symbol-observable "^1.2.0"
regenerate-unicode-properties@^8.0.2:
version "8.0.2"
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.0.2.tgz#7b38faa296252376d363558cfbda90c9ce709662"
integrity sha512-SbA/iNrBUf6Pv2zU8Ekv1Qbhv92yxL4hiDa2siuxs4KKn4oOoMDHXjAf7+Nz9qinUQ46B1LcWEi/PhJfPWpZWQ==
version "8.1.0"
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e"
integrity sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA==
dependencies:
regenerate "^1.4.0"
......@@ -7303,9 +7393,9 @@ regex-not@^1.0.0, regex-not@^1.0.2:
safe-regex "^1.1.0"
regexp-tree@^0.1.6:
version "0.1.10"
resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.10.tgz#d837816a039c7af8a8d64d7a7c3cf6a1d93450bc"
integrity sha512-K1qVSbcedffwuIslMwpe6vGlj+ZXRnGkvjAtFHfDZZZuEdA/h0dxljAPu9vhUo6Rrx2U2AwJ+nSQ6hK+lrP5MQ==
version "0.1.11"
resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.11.tgz#c9c7f00fcf722e0a56c7390983a7a63dd6c272f3"
integrity sha512-7/l/DgapVVDzZobwMCCgMlqiqyLFJ0cduo/j+3BcDJIB+yJdsYCfKuI3l/04NV+H/rfNRdPIDbXNZHM9XvQatg==
regexpp@^2.0.1:
version "2.0.1"
......@@ -7523,13 +7613,20 @@ resolve@1.1.7:
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=
resolve@^1.1.4, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.1, resolve@^1.3.2, resolve@^1.4.0:
resolve@^1.1.4, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.1, resolve@^1.4.0:
version "1.10.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.1.tgz#664842ac960795bbe758221cdccda61fb64b5f18"
integrity sha512-KuIe4mf++td/eFb6wkaPbMDnP6kObCaEtIDuHOUED6MNUo4K670KZUHuuvYPZDxNF0WVLw49n06M2m2dXphEzA==
dependencies:
path-parse "^1.0.6"
resolve@^1.3.2:
version "1.11.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.11.1.tgz#ea10d8110376982fef578df8fc30b9ac30a07a3e"
integrity sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==
dependencies:
path-parse "^1.0.6"
resolve@~0.6.1:
version "0.6.3"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-0.6.3.tgz#dd957982e7e736debdf53b58a4dd91754575dd46"
......@@ -7662,11 +7759,16 @@ semver-greatest-satisfied-range@^1.1.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b"
integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==
semver@^6.0.0, semver@^6.1.0:
semver@^6.0.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.1.0.tgz#e95dc415d45ecf03f2f9f83b264a6b11f49c0cca"
integrity sha512-kCqEOOHoBcFs/2Ccuk4Xarm/KiWRSLEX9CAZF8xkJ6ZPlIoTZ8V5f7J16vYLJqDbR7KrxTJpR2lqjIEm2Qx9cQ==
semver@^6.1.1:
version "6.2.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.2.0.tgz#4d813d9590aaf8a9192693d6c85b9344de5901db"
integrity sha512-jdFC1VdUGT/2Scgbimf7FSx9iJLXoqfglSF+gJeuNWVpiE37OIbc1jywR/GJyFdz3mnkz2/id0L0J/cr0izR5A==
semver@~5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
......@@ -8469,6 +8571,13 @@ to-regex-range@^2.1.0:
is-number "^3.0.0"
repeat-string "^1.6.1"
to-regex-range@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
dependencies:
is-number "^7.0.0"
to-regex@^3.0.1, to-regex@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce"
......@@ -8665,9 +8774,9 @@ unicode-match-property-value-ecmascript@^1.1.0:
integrity sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g==
unicode-property-aliases-ecmascript@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.4.tgz#5a533f31b4317ea76f17d807fa0d116546111dd0"
integrity sha512-2WSLa6OdYd2ng8oqiGIWnJqyFArvhn+5vgx5GTxMbUYjCYKUcuKS62YLFF0R/BDGlB1yzXjQOLtPAfHsgirEpg==
version "1.0.5"
resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz#a9cc6cc7ce63a0a3023fc99e341b94431d405a57"
integrity sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw==
union-value@^1.0.0:
version "1.0.0"
......@@ -8687,13 +8796,20 @@ unique-stream@^2.0.2:
json-stable-stringify-without-jsonify "^1.0.1"
through2-filter "^3.0.0"
universal-user-agent@^2.0.0, universal-user-agent@^2.0.1, universal-user-agent@^2.1.0:
universal-user-agent@^2.0.1:
version "2.1.0"
resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-2.1.0.tgz#5abfbcc036a1ba490cb941f8fd68c46d3669e8e4"
integrity sha512-8itiX7G05Tu3mGDTdNY2fB4KJ8MgZLS54RdG6PkkfwMAavrXu1mV/lls/GABx9O3Rw4PnTtasxrvbMQoBYY92Q==
dependencies:
os-name "^3.0.0"
universal-user-agent@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-3.0.0.tgz#4cc88d68097bffd7ac42e3b7c903e7481424b4b9"
integrity sha512-T3siHThqoj5X0benA5H0qcDnrKGXzU8TKoX15x/tQHw1hQBvIEBHjxQ2klizYsqBOO/Q+WuxoQUihadeeqDnoA==
dependencies:
os-name "^3.0.0"
universalify@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment