mirror of https://github.com/status-im/consul.git
UI: Add EventSource ready for implementing blocking queries (#5070)
- Maintain http headers as JSON-API meta for all API requests (#4946) - Add EventSource ready for implementing blocking queries - EventSource project implementation to enable blocking queries for service and node listings (#5267) - Add setting to enable/disable blocking queries (#5352)
This commit is contained in:
parent
f225da36f1
commit
cb0c5309c9
|
@ -0,0 +1 @@
|
|||
app/utils/dom/event-target/event-target-shim/event.js
|
|
@ -114,6 +114,9 @@ export default Adapter.extend({
|
|||
if (typeof query.separator !== 'undefined') {
|
||||
delete query.separator;
|
||||
}
|
||||
if (typeof query.index !== 'undefined') {
|
||||
delete query.index;
|
||||
}
|
||||
delete _query[DATACENTER_QUERY_PARAM];
|
||||
return query;
|
||||
},
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import Controller from '@ember/controller';
|
||||
import { computed } from '@ember/object';
|
||||
import WithEventSource from 'consul-ui/mixins/with-event-source';
|
||||
import WithHealthFiltering from 'consul-ui/mixins/with-health-filtering';
|
||||
import WithSearching from 'consul-ui/mixins/with-searching';
|
||||
import { get } from '@ember/object';
|
||||
export default Controller.extend(WithSearching, WithHealthFiltering, {
|
||||
export default Controller.extend(WithEventSource, WithSearching, WithHealthFiltering, {
|
||||
init: function() {
|
||||
this.searchParams = {
|
||||
healthyNode: 's',
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import Controller from '@ember/controller';
|
||||
import { get, computed } from '@ember/object';
|
||||
import { htmlSafe } from '@ember/string';
|
||||
import WithEventSource from 'consul-ui/mixins/with-event-source';
|
||||
import WithHealthFiltering from 'consul-ui/mixins/with-health-filtering';
|
||||
import WithSearching from 'consul-ui/mixins/with-searching';
|
||||
const max = function(arr, prop) {
|
||||
|
@ -25,7 +26,7 @@ const width = function(num) {
|
|||
const widthDeclaration = function(num) {
|
||||
return htmlSafe(`width: ${num}px`);
|
||||
};
|
||||
export default Controller.extend(WithSearching, WithHealthFiltering, {
|
||||
export default Controller.extend(WithEventSource, WithSearching, WithHealthFiltering, {
|
||||
init: function() {
|
||||
this.searchParams = {
|
||||
service: 's',
|
||||
|
@ -52,14 +53,14 @@ export default Controller.extend(WithSearching, WithHealthFiltering, {
|
|||
remainingWidth: computed('maxWidth', function() {
|
||||
return htmlSafe(`width: calc(50% - ${Math.round(get(this, 'maxWidth') / 2)}px)`);
|
||||
}),
|
||||
maxPassing: computed('items', function() {
|
||||
return max(get(this, 'items'), 'ChecksPassing');
|
||||
maxPassing: computed('filtered', function() {
|
||||
return max(get(this, 'filtered'), 'ChecksPassing');
|
||||
}),
|
||||
maxWarning: computed('items', function() {
|
||||
return max(get(this, 'items'), 'ChecksWarning');
|
||||
maxWarning: computed('filtered', function() {
|
||||
return max(get(this, 'filtered'), 'ChecksWarning');
|
||||
}),
|
||||
maxCritical: computed('items', function() {
|
||||
return max(get(this, 'items'), 'ChecksCritical');
|
||||
maxCritical: computed('filtered', function() {
|
||||
return max(get(this, 'filtered'), 'ChecksCritical');
|
||||
}),
|
||||
passingWidth: computed('maxPassing', function() {
|
||||
return widthDeclaration(width(get(this, 'maxPassing')));
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
import Controller from '@ember/controller';
|
||||
import { get, set } from '@ember/object';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default Controller.extend({
|
||||
repo: service('settings'),
|
||||
dom: service('dom'),
|
||||
actions: {
|
||||
change: function(e, value, item) {
|
||||
const event = get(this, 'dom').normalizeEvent(e, value);
|
||||
// TODO: Switch to using forms like the rest of the app
|
||||
// setting utils/form/builder for things to be done before we
|
||||
// can do that. For the moment just do things normally its a simple
|
||||
// enough form at the moment
|
||||
|
||||
const target = event.target;
|
||||
const blocking = get(this, 'item.client.blocking');
|
||||
switch (target.name) {
|
||||
case 'client[blocking]':
|
||||
if (typeof blocking === 'undefined') {
|
||||
set(this, 'item.client', {});
|
||||
}
|
||||
set(this, 'item.client.blocking', !blocking);
|
||||
this.send('update', get(this, 'item'));
|
||||
break;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
|
@ -0,0 +1,61 @@
|
|||
import config from '../config/environment';
|
||||
|
||||
const enabled = 'CONSUL_UI_DISABLE_REALTIME';
|
||||
export function initialize(container) {
|
||||
if (config[enabled] || window.localStorage.getItem(enabled) !== null) {
|
||||
return;
|
||||
}
|
||||
['node', 'service']
|
||||
.map(function(item) {
|
||||
// create repositories that return a promise resolving to an EventSource
|
||||
return {
|
||||
service: `repository/${item}/event-source`,
|
||||
extend: 'repository/type/event-source',
|
||||
// Inject our original respository that is used by this class
|
||||
// within the callable of the EventSource
|
||||
services: {
|
||||
content: `repository/${item}`,
|
||||
},
|
||||
};
|
||||
})
|
||||
.concat([
|
||||
// These are the routes where we overwrite the 'default'
|
||||
// repo service. Default repos are repos that return a promise resovlving to
|
||||
// an ember-data record or recordset
|
||||
{
|
||||
route: 'dc/nodes/index',
|
||||
services: {
|
||||
repo: 'repository/node/event-source',
|
||||
},
|
||||
},
|
||||
{
|
||||
route: 'dc/services/index',
|
||||
services: {
|
||||
repo: 'repository/service/event-source',
|
||||
},
|
||||
},
|
||||
])
|
||||
.forEach(function(definition) {
|
||||
if (typeof definition.extend !== 'undefined') {
|
||||
// Create the class instances that we need
|
||||
container.register(
|
||||
`service:${definition.service}`,
|
||||
container.resolveRegistration(`service:${definition.extend}`).extend({})
|
||||
);
|
||||
}
|
||||
Object.keys(definition.services).forEach(function(name) {
|
||||
const servicePath = definition.services[name];
|
||||
// inject its dependencies, this could probably detect the type
|
||||
// but hardcode this for the moment
|
||||
if (typeof definition.route !== 'undefined') {
|
||||
container.inject(`route:${definition.route}`, name, `service:${servicePath}`);
|
||||
} else {
|
||||
container.inject(`service:${definition.service}`, name, `service:${servicePath}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
initialize,
|
||||
};
|
|
@ -0,0 +1,16 @@
|
|||
import Mixin from '@ember/object/mixin';
|
||||
|
||||
export default Mixin.create({
|
||||
reset: function(exiting) {
|
||||
if (exiting) {
|
||||
Object.keys(this).forEach(prop => {
|
||||
if (this[prop] && typeof this[prop].close === 'function') {
|
||||
this[prop].close();
|
||||
// ember doesn't delete on 'resetController' by default
|
||||
delete this[prop];
|
||||
}
|
||||
});
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
});
|
|
@ -15,7 +15,7 @@ const toKeyValue = function(el) {
|
|||
};
|
||||
export default Mixin.create({
|
||||
filters: {},
|
||||
filtered: computed('items', 'filters', function() {
|
||||
filtered: computed('items.[]', 'filters', function() {
|
||||
const filters = get(this, 'filters');
|
||||
return get(this, 'items').filter(item => {
|
||||
return this.filter(item, filters);
|
||||
|
|
|
@ -29,7 +29,7 @@ export default Mixin.create(WithFiltering, {
|
|||
as: 'filter',
|
||||
},
|
||||
},
|
||||
healthFilters: computed('items', function() {
|
||||
healthFilters: computed('items.[]', function() {
|
||||
const items = get(this, 'items');
|
||||
const objs = ['', 'passing', 'warning', 'critical'].map(function(item) {
|
||||
const count = countStatus(items, item);
|
||||
|
|
|
@ -88,9 +88,9 @@ export const routes = {
|
|||
_options: { path: '/' },
|
||||
},
|
||||
// The settings page is global.
|
||||
// settings: {
|
||||
// _options: { path: '/setting' },
|
||||
// },
|
||||
settings: {
|
||||
_options: { path: '/setting' },
|
||||
},
|
||||
notfound: {
|
||||
_options: { path: '/*path' },
|
||||
},
|
||||
|
|
|
@ -3,8 +3,8 @@ import { inject as service } from '@ember/service';
|
|||
import { hash } from 'rsvp';
|
||||
import { get } from '@ember/object';
|
||||
|
||||
import WithBlockingActions from 'consul-ui/mixins/with-blocking-actions';
|
||||
export default Route.extend(WithBlockingActions, {
|
||||
export default Route.extend({
|
||||
client: service('client/http'),
|
||||
repo: service('settings'),
|
||||
dcRepo: service('repository/dc'),
|
||||
model: function(params) {
|
||||
|
@ -24,8 +24,12 @@ export default Route.extend(WithBlockingActions, {
|
|||
this._super(...arguments);
|
||||
controller.setProperties(model);
|
||||
},
|
||||
// overwrite afterUpdate and afterDelete hooks
|
||||
// to avoid the default 'return to listing page'
|
||||
afterUpdate: function() {},
|
||||
afterDelete: function() {},
|
||||
actions: {
|
||||
update: function(item) {
|
||||
if (!get(item, 'client.blocking')) {
|
||||
get(this, 'client').abort();
|
||||
}
|
||||
get(this, 'repo').persist(item);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -63,6 +63,9 @@ export default Service.extend({
|
|||
});
|
||||
}
|
||||
},
|
||||
abort: function(id = null) {
|
||||
get(this, 'connections').purge();
|
||||
},
|
||||
whenAvailable: function(e) {
|
||||
const doc = get(this, 'dom').document();
|
||||
// if we are using a connection limited protocol and the user has hidden the tab (hidden browser/tab switch)
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
import Service from '@ember/service';
|
||||
import { get } from '@ember/object';
|
||||
|
||||
export default Service.extend({
|
||||
shouldProxy: function(content, method) {
|
||||
return false;
|
||||
},
|
||||
init: function() {
|
||||
this._super(...arguments);
|
||||
const content = get(this, 'content');
|
||||
for (let prop in content) {
|
||||
if (typeof content[prop] === 'function') {
|
||||
if (this.shouldProxy(content, prop)) {
|
||||
this[prop] = function() {
|
||||
return this.execute(content, prop).then(method => {
|
||||
return method.apply(this, arguments);
|
||||
});
|
||||
};
|
||||
} else if (typeof this[prop] !== 'function') {
|
||||
this[prop] = function() {
|
||||
return content[prop](...arguments);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
|
@ -0,0 +1,92 @@
|
|||
import { inject as service } from '@ember/service';
|
||||
import { get } from '@ember/object';
|
||||
|
||||
import LazyProxyService from 'consul-ui/services/lazy-proxy';
|
||||
|
||||
import { cache as createCache, BlockingEventSource } from 'consul-ui/utils/dom/event-source';
|
||||
|
||||
const createProxy = function(repo, find, settings, cache, serialize = JSON.stringify) {
|
||||
// proxied find*..(id, dc)
|
||||
const throttle = get(this, 'wait').execute;
|
||||
const client = get(this, 'client');
|
||||
return function() {
|
||||
const key = `${repo.getModelName()}.${find}.${serialize([...arguments])}`;
|
||||
const _args = arguments;
|
||||
const newPromisedEventSource = cache;
|
||||
return newPromisedEventSource(
|
||||
function(configuration) {
|
||||
// take a copy of the original arguments
|
||||
// this means we don't have any configuration object on it
|
||||
let args = [..._args];
|
||||
if (configuration.settings.enabled) {
|
||||
// ...and only add our current cursor/configuration if we are blocking
|
||||
args = args.concat([configuration]);
|
||||
}
|
||||
// save a callback so we can conditionally throttle
|
||||
const cb = () => {
|
||||
// original find... with configuration now added
|
||||
return repo[find](...args)
|
||||
.then(res => {
|
||||
if (!configuration.settings.enabled) {
|
||||
// blocking isn't enabled, immediately close
|
||||
this.close();
|
||||
}
|
||||
return res;
|
||||
})
|
||||
.catch(function(e) {
|
||||
// setup the aborted connection restarting
|
||||
// this should happen here to avoid cache deletion
|
||||
const status = get(e, 'errors.firstObject.status');
|
||||
if (status === '0') {
|
||||
// Any '0' errors (abort) should possibly try again, depending upon the circumstances
|
||||
// whenAvailable returns a Promise that resolves when the client is available
|
||||
// again
|
||||
return client.whenAvailable(e);
|
||||
}
|
||||
throw e;
|
||||
});
|
||||
};
|
||||
// if we have a cursor (which means its at least the second call)
|
||||
// and we have a throttle setting, wait for so many ms
|
||||
if (typeof configuration.cursor !== 'undefined' && configuration.settings.throttle) {
|
||||
return throttle(configuration.settings.throttle).then(cb);
|
||||
}
|
||||
return cb();
|
||||
},
|
||||
{
|
||||
key: key,
|
||||
type: BlockingEventSource,
|
||||
settings: {
|
||||
enabled: settings.blocking,
|
||||
throttle: settings.throttle,
|
||||
},
|
||||
}
|
||||
);
|
||||
};
|
||||
};
|
||||
let cache = null;
|
||||
export default LazyProxyService.extend({
|
||||
store: service('store'),
|
||||
settings: service('settings'),
|
||||
wait: service('timeout'),
|
||||
client: service('client/http'),
|
||||
init: function() {
|
||||
this._super(...arguments);
|
||||
if (cache === null) {
|
||||
cache = createCache({});
|
||||
}
|
||||
},
|
||||
willDestroy: function() {
|
||||
cache = null;
|
||||
},
|
||||
shouldProxy: function(content, method) {
|
||||
return method.indexOf('find') === 0;
|
||||
},
|
||||
execute: function(repo, find) {
|
||||
return get(this, 'settings')
|
||||
.findBySlug('client')
|
||||
.then(settings => {
|
||||
return createProxy.bind(this)(repo, find, settings, cache);
|
||||
});
|
||||
},
|
||||
});
|
|
@ -44,11 +44,9 @@
|
|||
<li data-test-main-nav-docs>
|
||||
<a href="{{ env 'CONSUL_DOCUMENTATION_URL'}}/index.html" rel="help noopener noreferrer" target="_blank">Documentation</a>
|
||||
</li>
|
||||
{{#if false }}
|
||||
<li data-test-main-nav-settings class={{if (is-href 'settings') 'is-active'}}>
|
||||
<a href={{href-to 'settings'}}>Settings</a>
|
||||
</li>
|
||||
{{/if}}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
|
|
@ -1,20 +1,5 @@
|
|||
{{#hashicorp-consul id="wrapper" dcs=dcs dc=dc}}
|
||||
{{#app-view class="settings show"}}
|
||||
{{#block-slot 'notification' as |status type|}}
|
||||
{{#if (eq type 'update')}}
|
||||
{{#if (eq status 'success') }}
|
||||
Your settings were saved.
|
||||
{{else}}
|
||||
There was an error saving your settings.
|
||||
{{/if}}
|
||||
{{ else if (eq type 'delete')}}
|
||||
{{#if (eq status 'success') }}
|
||||
You settings have been reset.
|
||||
{{else}}
|
||||
There was an error resetting your settings.
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/block-slot}}
|
||||
{{#block-slot 'header'}}
|
||||
<h1>
|
||||
Settings
|
||||
|
@ -26,13 +11,13 @@
|
|||
</p>
|
||||
<form>
|
||||
<fieldset>
|
||||
<label class="type-text">
|
||||
<span>ACL Token</span>
|
||||
{{ input type='password' value=item.token name="token" }}
|
||||
<em>The token is sent with requests as the <code>X-Consul-Token</code> HTTP header parameter. This is used to control the ACL for the web UI.</em>
|
||||
</label>
|
||||
<div class="type-toggle">
|
||||
<label>
|
||||
<input type="checkbox" name="client[blocking]" checked={{if item.client.blocking 'checked' }} onchange={{action 'change'}} />
|
||||
<span>Enable Catalog realtime updates (blocking queries)</span>
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
<button type="submit" {{action 'update' item}}>Save</button>
|
||||
</form>
|
||||
{{/block-slot}}
|
||||
{{/app-view}}
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
import { get } from '@ember/object';
|
||||
import { Promise } from 'rsvp';
|
||||
|
||||
// native EventSource retry is ~3s wait
|
||||
export const create5xxBackoff = function(ms = 3000, P = Promise, wait = setTimeout) {
|
||||
// This expects an ember-data like error
|
||||
return function(err) {
|
||||
const status = get(err, 'errors.firstObject.status');
|
||||
if (typeof status !== 'undefined') {
|
||||
switch (true) {
|
||||
// Any '5xx' (not 500) errors should back off and try again
|
||||
case status.indexOf('5') === 0 && status.length === 3 && status !== '500':
|
||||
return new P(function(resolve) {
|
||||
wait(function() {
|
||||
resolve(err);
|
||||
}, ms);
|
||||
});
|
||||
}
|
||||
}
|
||||
// any other errors should throw to be picked up by an error listener/catch
|
||||
throw err;
|
||||
};
|
||||
};
|
||||
const defaultCreateEvent = function(result, configuration) {
|
||||
return {
|
||||
type: 'message',
|
||||
data: result,
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Wraps an EventSource with functionality to add native EventSource-like functionality
|
||||
*
|
||||
* @param {Class} [CallableEventSource] - CallableEventSource Class
|
||||
* @param {Function} [backoff] - Default backoff function for all instances, defaults to create5xxBackoff
|
||||
*/
|
||||
export default function(EventSource, backoff = create5xxBackoff()) {
|
||||
/**
|
||||
* An EventSource implementation to add native EventSource-like functionality with just callbacks (`cursor` and 5xx backoff)
|
||||
*
|
||||
* This includes:
|
||||
* 1. 5xx backoff support (uses a 3 second reconnect like native implementations). You can add to this via `Promise.catch`
|
||||
* 2. A `cursor` configuration value. Current `cursor` is taken from the `meta` property of the event (i.e. `event.data.meta.cursor`)
|
||||
* 3. Event data can be customized by adding a `configuration.createEvent`
|
||||
*
|
||||
* @param {Function} [source] - Promise returning function that resolves your data
|
||||
* @param {Object} [configuration] - Plain configuration object:
|
||||
* `cursor` - Cursor position of the EventSource
|
||||
* `createEvent` - A data filter, giving you the opportunity to filter or replace the event data, such as removing/replacing records
|
||||
*/
|
||||
return class extends EventSource {
|
||||
constructor(source, configuration = {}) {
|
||||
super(configuration => {
|
||||
const { createEvent, ...superConfiguration } = configuration;
|
||||
return source
|
||||
.apply(this, [superConfiguration])
|
||||
.catch(backoff)
|
||||
.then(result => {
|
||||
if (!(result instanceof Error)) {
|
||||
const _createEvent =
|
||||
typeof createEvent === 'function' ? createEvent : defaultCreateEvent;
|
||||
let event = _createEvent(result, configuration);
|
||||
// allow custom types, but make a default of `message`, ideally this would check for CustomEvent
|
||||
// but keep this flexible for the moment
|
||||
if (!event.type) {
|
||||
event = {
|
||||
type: 'message',
|
||||
data: event,
|
||||
};
|
||||
}
|
||||
// meta is also configurable by using createEvent
|
||||
const meta = get(event.data || {}, 'meta');
|
||||
if (meta) {
|
||||
// pick off the `cursor` from the meta and add it to configuration
|
||||
configuration.cursor = meta.cursor;
|
||||
}
|
||||
this.currentEvent = event;
|
||||
this.dispatchEvent(this.currentEvent);
|
||||
this.previousEvent = this.currentEvent;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}, configuration);
|
||||
}
|
||||
// if we are having these props, at least make getters
|
||||
getCurrentEvent() {
|
||||
return this.currentEvent;
|
||||
}
|
||||
getPreviousEvent() {
|
||||
return this.previousEvent;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
export default function(source, DefaultEventSource, P = Promise) {
|
||||
return function(sources) {
|
||||
return function(cb, configuration) {
|
||||
const key = configuration.key;
|
||||
if (typeof sources[key] !== 'undefined' && configuration.settings.enabled) {
|
||||
if (typeof sources[key].configuration === 'undefined') {
|
||||
sources[key].configuration = {};
|
||||
}
|
||||
sources[key].configuration.settings = configuration.settings;
|
||||
return source(sources[key]);
|
||||
} else {
|
||||
const EventSource = configuration.type || DefaultEventSource;
|
||||
const eventSource = (sources[key] = new EventSource(cb, configuration));
|
||||
return source(eventSource)
|
||||
.catch(function(e) {
|
||||
// any errors, delete from the cache for next time
|
||||
delete sources[key];
|
||||
return P.reject(e);
|
||||
})
|
||||
.then(function(eventSource) {
|
||||
// make sure we cancel everything out if there is no cursor
|
||||
if (typeof eventSource.configuration.cursor === 'undefined') {
|
||||
eventSource.close();
|
||||
delete sources[key];
|
||||
}
|
||||
return eventSource;
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
export const defaultRunner = function(target, configuration, isClosed) {
|
||||
if (isClosed(target)) {
|
||||
return;
|
||||
}
|
||||
// TODO Consider wrapping this is a promise for none thenable returns
|
||||
return target.source
|
||||
.bind(target)(configuration)
|
||||
.then(function(res) {
|
||||
return defaultRunner(target, configuration, isClosed);
|
||||
});
|
||||
};
|
||||
const errorEvent = function(e) {
|
||||
return new ErrorEvent('error', {
|
||||
error: e,
|
||||
message: e.message,
|
||||
});
|
||||
};
|
||||
const isClosed = function(target) {
|
||||
switch (target.readyState) {
|
||||
case 2: // CLOSED
|
||||
case 3: // CLOSING
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
export default function(
|
||||
EventTarget,
|
||||
P = Promise,
|
||||
run = defaultRunner,
|
||||
createErrorEvent = errorEvent
|
||||
) {
|
||||
return class extends EventTarget {
|
||||
constructor(source, configuration = {}) {
|
||||
super();
|
||||
this.readyState = 2;
|
||||
this.source =
|
||||
typeof source !== 'function'
|
||||
? function(configuration) {
|
||||
this.close();
|
||||
return P.resolve();
|
||||
}
|
||||
: source;
|
||||
this.readyState = 0; // connecting
|
||||
P.resolve()
|
||||
.then(() => {
|
||||
this.readyState = 1; // open
|
||||
// ...that the connection _was just_ opened
|
||||
this.dispatchEvent({ type: 'open' });
|
||||
return run(this, configuration, isClosed);
|
||||
})
|
||||
.catch(e => {
|
||||
this.dispatchEvent(createErrorEvent(e));
|
||||
// close after the dispatch so we can tell if it was an error whilst closed or not
|
||||
// but make sure its before the promise tick
|
||||
this.readyState = 2; // CLOSE
|
||||
})
|
||||
.then(() => {
|
||||
// This only gets called when the promise chain completely finishes
|
||||
// so only when its completely closed.
|
||||
this.readyState = 2; // CLOSE
|
||||
});
|
||||
}
|
||||
close() {
|
||||
// additional readyState 3 = CLOSING
|
||||
if (this.readyState !== 2) {
|
||||
this.readyState = 3;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
import ObjectProxy from '@ember/object/proxy';
|
||||
import ArrayProxy from '@ember/array/proxy';
|
||||
import { Promise } from 'rsvp';
|
||||
|
||||
import createListeners from 'consul-ui/utils/dom/create-listeners';
|
||||
|
||||
import EventTarget from 'consul-ui/utils/dom/event-target/rsvp';
|
||||
|
||||
import cacheFactory from 'consul-ui/utils/dom/event-source/cache';
|
||||
import proxyFactory from 'consul-ui/utils/dom/event-source/proxy';
|
||||
import firstResolverFactory from 'consul-ui/utils/dom/event-source/resolver';
|
||||
|
||||
import CallableEventSourceFactory from 'consul-ui/utils/dom/event-source/callable';
|
||||
import ReopenableEventSourceFactory from 'consul-ui/utils/dom/event-source/reopenable';
|
||||
import BlockingEventSourceFactory from 'consul-ui/utils/dom/event-source/blocking';
|
||||
import StorageEventSourceFactory from 'consul-ui/utils/dom/event-source/storage';
|
||||
|
||||
// All The EventSource-i
|
||||
export const CallableEventSource = CallableEventSourceFactory(EventTarget, Promise);
|
||||
export const ReopenableEventSource = ReopenableEventSourceFactory(CallableEventSource);
|
||||
export const BlockingEventSource = BlockingEventSourceFactory(ReopenableEventSource);
|
||||
export const StorageEventSource = StorageEventSourceFactory(EventTarget, Promise);
|
||||
|
||||
// various utils
|
||||
export const proxy = proxyFactory(ObjectProxy, ArrayProxy);
|
||||
export const resolve = firstResolverFactory(Promise);
|
||||
|
||||
export const source = function(source) {
|
||||
// create API needed for conventional promise blocked, loading, Routes
|
||||
// i.e. resolve/reject on first response
|
||||
return resolve(source, createListeners()).then(function(data) {
|
||||
// create API needed for conventional DD/computed and Controllers
|
||||
return proxy(data, source, createListeners());
|
||||
});
|
||||
};
|
||||
export const cache = cacheFactory(source, BlockingEventSource, Promise);
|
|
@ -0,0 +1,50 @@
|
|||
import { get, set } from '@ember/object';
|
||||
|
||||
export default function(ObjProxy, ArrProxy) {
|
||||
return function(data, source, listeners) {
|
||||
let Proxy = ObjProxy;
|
||||
if (typeof data !== 'string' && typeof get(data, 'length') !== 'undefined') {
|
||||
data = data.filter(function(item) {
|
||||
return !get(item, 'isDestroyed') && !get(item, 'isDeleted') && get(item, 'isLoaded');
|
||||
});
|
||||
}
|
||||
if (typeof data !== 'string' && typeof get(data, 'length') !== 'undefined') {
|
||||
Proxy = ArrProxy;
|
||||
}
|
||||
const proxy = Proxy.create({
|
||||
content: data,
|
||||
init: function() {
|
||||
this.listeners = listeners;
|
||||
this.listeners.add(source, 'message', e => {
|
||||
set(this, 'content', e.data);
|
||||
});
|
||||
},
|
||||
configuration: source.configuration,
|
||||
addEventListener: function(type, handler) {
|
||||
// Force use of computed for messages
|
||||
if (type !== 'message') {
|
||||
this.listeners.add(source, type, handler);
|
||||
}
|
||||
},
|
||||
getCurrentEvent: function() {
|
||||
return source.getCurrentEvent(...arguments);
|
||||
},
|
||||
removeEventListener: function() {
|
||||
return source.removeEventListener(...arguments);
|
||||
},
|
||||
dispatchEvent: function() {
|
||||
return source.dispatchEvent(...arguments);
|
||||
},
|
||||
close: function() {
|
||||
return source.close(...arguments);
|
||||
},
|
||||
reopen: function() {
|
||||
return source.reopen(...arguments);
|
||||
},
|
||||
willDestroy: function() {
|
||||
this.listeners.remove();
|
||||
},
|
||||
});
|
||||
return proxy;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* Wraps an EventSource so that you can `close` and `reopen`
|
||||
*
|
||||
* @param {Class} eventSource - EventSource class to extend from
|
||||
*/
|
||||
export default function(eventSource = EventSource) {
|
||||
return class extends eventSource {
|
||||
constructor(source, configuration) {
|
||||
super(...arguments);
|
||||
this.configuration = configuration;
|
||||
}
|
||||
reopen() {
|
||||
switch (this.readyState) {
|
||||
case 3: // CLOSING
|
||||
this.readyState = 1;
|
||||
break;
|
||||
case 2: // CLOSED
|
||||
eventSource.apply(this, [this.source, this.configuration]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
export default function(P = Promise) {
|
||||
return function(source, listeners) {
|
||||
let current;
|
||||
if (typeof source.getCurrentEvent === 'function') {
|
||||
current = source.getCurrentEvent();
|
||||
}
|
||||
if (current != null) {
|
||||
// immediately resolve if we have previous cached data
|
||||
return P.resolve(current.data).then(function(cached) {
|
||||
source.reopen();
|
||||
return cached;
|
||||
});
|
||||
}
|
||||
// if we have no previously cached data, listen for the first response
|
||||
return new P(function(resolve, reject) {
|
||||
// close, cleanup and reject if we get an error
|
||||
listeners.add(source, 'error', function(e) {
|
||||
listeners.remove();
|
||||
e.target.close();
|
||||
reject(e.error);
|
||||
});
|
||||
// ...or cleanup and respond with the first lot of data
|
||||
listeners.add(source, 'message', function(e) {
|
||||
listeners.remove();
|
||||
resolve(e.data);
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
export default function(EventTarget, P = Promise) {
|
||||
const handler = function(e) {
|
||||
if (e.key === this.configuration.key) {
|
||||
P.resolve(this.getCurrentEvent()).then(event => {
|
||||
this.configuration.cursor++;
|
||||
this.dispatchEvent(event);
|
||||
});
|
||||
}
|
||||
};
|
||||
return class extends EventTarget {
|
||||
constructor(cb, configuration) {
|
||||
super(...arguments);
|
||||
this.source = cb;
|
||||
this.handler = handler.bind(this);
|
||||
this.configuration = configuration;
|
||||
this.configuration.cursor = 1;
|
||||
this.dispatcher = configuration.dispatcher;
|
||||
this.reopen();
|
||||
}
|
||||
dispatchEvent() {
|
||||
if (this.readyState === 1) {
|
||||
return super.dispatchEvent(...arguments);
|
||||
}
|
||||
}
|
||||
close() {
|
||||
this.dispatcher.removeEventListener('storage', this.handler);
|
||||
this.readyState = 2;
|
||||
}
|
||||
reopen() {
|
||||
this.dispatcher.addEventListener('storage', this.handler);
|
||||
this.readyState = 1;
|
||||
}
|
||||
getCurrentEvent() {
|
||||
return {
|
||||
type: 'message',
|
||||
data: this.source(this.configuration),
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Toru Nagashima
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -0,0 +1,470 @@
|
|||
/**
|
||||
* @typedef {object} PrivateData
|
||||
* @property {EventTarget} eventTarget The event target.
|
||||
* @property {{type:string}} event The original event object.
|
||||
* @property {number} eventPhase The current event phase.
|
||||
* @property {EventTarget|null} currentTarget The current event target.
|
||||
* @property {boolean} canceled The flag to prevent default.
|
||||
* @property {boolean} stopped The flag to stop propagation.
|
||||
* @property {boolean} immediateStopped The flag to stop propagation immediately.
|
||||
* @property {Function|null} passiveListener The listener if the current listener is passive. Otherwise this is null.
|
||||
* @property {number} timeStamp The unix time.
|
||||
* @private
|
||||
*/
|
||||
|
||||
/**
|
||||
* Private data for event wrappers.
|
||||
* @type {WeakMap<Event, PrivateData>}
|
||||
* @private
|
||||
*/
|
||||
const privateData = new WeakMap();
|
||||
|
||||
/**
|
||||
* Cache for wrapper classes.
|
||||
* @type {WeakMap<Object, Function>}
|
||||
* @private
|
||||
*/
|
||||
const wrappers = new WeakMap();
|
||||
|
||||
/**
|
||||
* Get private data.
|
||||
* @param {Event} event The event object to get private data.
|
||||
* @returns {PrivateData} The private data of the event.
|
||||
* @private
|
||||
*/
|
||||
function pd(event) {
|
||||
const retv = privateData.get(event);
|
||||
console.assert(retv != null, "'this' is expected an Event object, but got", event);
|
||||
return retv;
|
||||
}
|
||||
|
||||
/**
|
||||
* https://dom.spec.whatwg.org/#set-the-canceled-flag
|
||||
* @param data {PrivateData} private data.
|
||||
*/
|
||||
function setCancelFlag(data) {
|
||||
if (data.passiveListener != null) {
|
||||
if (typeof console !== 'undefined' && typeof console.error === 'function') {
|
||||
console.error(
|
||||
'Unable to preventDefault inside passive event listener invocation.',
|
||||
data.passiveListener
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!data.event.cancelable) {
|
||||
return;
|
||||
}
|
||||
|
||||
data.canceled = true;
|
||||
if (typeof data.event.preventDefault === 'function') {
|
||||
data.event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://dom.spec.whatwg.org/#interface-event
|
||||
* @private
|
||||
*/
|
||||
/**
|
||||
* The event wrapper.
|
||||
* @constructor
|
||||
* @param {EventTarget} eventTarget The event target of this dispatching.
|
||||
* @param {Event|{type:string}} event The original event to wrap.
|
||||
*/
|
||||
function Event(eventTarget, event) {
|
||||
privateData.set(this, {
|
||||
eventTarget,
|
||||
event,
|
||||
eventPhase: 2,
|
||||
currentTarget: eventTarget,
|
||||
canceled: false,
|
||||
stopped: false,
|
||||
immediateStopped: false,
|
||||
passiveListener: null,
|
||||
timeStamp: event.timeStamp || Date.now(),
|
||||
});
|
||||
|
||||
// https://heycam.github.io/webidl/#Unforgeable
|
||||
Object.defineProperty(this, 'isTrusted', { value: false, enumerable: true });
|
||||
|
||||
// Define accessors
|
||||
const keys = Object.keys(event);
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
const key = keys[i];
|
||||
if (!(key in this)) {
|
||||
Object.defineProperty(this, key, defineRedirectDescriptor(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Should be enumerable, but class methods are not enumerable.
|
||||
Event.prototype = {
|
||||
/**
|
||||
* The type of this event.
|
||||
* @type {string}
|
||||
*/
|
||||
get type() {
|
||||
return pd(this).event.type;
|
||||
},
|
||||
|
||||
/**
|
||||
* The target of this event.
|
||||
* @type {EventTarget}
|
||||
*/
|
||||
get target() {
|
||||
return pd(this).eventTarget;
|
||||
},
|
||||
|
||||
/**
|
||||
* The target of this event.
|
||||
* @type {EventTarget}
|
||||
*/
|
||||
get currentTarget() {
|
||||
return pd(this).currentTarget;
|
||||
},
|
||||
|
||||
/**
|
||||
* @returns {EventTarget[]} The composed path of this event.
|
||||
*/
|
||||
composedPath() {
|
||||
const currentTarget = pd(this).currentTarget;
|
||||
if (currentTarget == null) {
|
||||
return [];
|
||||
}
|
||||
return [currentTarget];
|
||||
},
|
||||
|
||||
/**
|
||||
* Constant of NONE.
|
||||
* @type {number}
|
||||
*/
|
||||
get NONE() {
|
||||
return 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Constant of CAPTURING_PHASE.
|
||||
* @type {number}
|
||||
*/
|
||||
get CAPTURING_PHASE() {
|
||||
return 1;
|
||||
},
|
||||
|
||||
/**
|
||||
* Constant of AT_TARGET.
|
||||
* @type {number}
|
||||
*/
|
||||
get AT_TARGET() {
|
||||
return 2;
|
||||
},
|
||||
|
||||
/**
|
||||
* Constant of BUBBLING_PHASE.
|
||||
* @type {number}
|
||||
*/
|
||||
get BUBBLING_PHASE() {
|
||||
return 3;
|
||||
},
|
||||
|
||||
/**
|
||||
* The target of this event.
|
||||
* @type {number}
|
||||
*/
|
||||
get eventPhase() {
|
||||
return pd(this).eventPhase;
|
||||
},
|
||||
|
||||
/**
|
||||
* Stop event bubbling.
|
||||
* @returns {void}
|
||||
*/
|
||||
stopPropagation() {
|
||||
const data = pd(this);
|
||||
|
||||
data.stopped = true;
|
||||
if (typeof data.event.stopPropagation === 'function') {
|
||||
data.event.stopPropagation();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Stop event bubbling.
|
||||
* @returns {void}
|
||||
*/
|
||||
stopImmediatePropagation() {
|
||||
const data = pd(this);
|
||||
|
||||
data.stopped = true;
|
||||
data.immediateStopped = true;
|
||||
if (typeof data.event.stopImmediatePropagation === 'function') {
|
||||
data.event.stopImmediatePropagation();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The flag to be bubbling.
|
||||
* @type {boolean}
|
||||
*/
|
||||
get bubbles() {
|
||||
return Boolean(pd(this).event.bubbles);
|
||||
},
|
||||
|
||||
/**
|
||||
* The flag to be cancelable.
|
||||
* @type {boolean}
|
||||
*/
|
||||
get cancelable() {
|
||||
return Boolean(pd(this).event.cancelable);
|
||||
},
|
||||
|
||||
/**
|
||||
* Cancel this event.
|
||||
* @returns {void}
|
||||
*/
|
||||
preventDefault() {
|
||||
setCancelFlag(pd(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* The flag to indicate cancellation state.
|
||||
* @type {boolean}
|
||||
*/
|
||||
get defaultPrevented() {
|
||||
return pd(this).canceled;
|
||||
},
|
||||
|
||||
/**
|
||||
* The flag to be composed.
|
||||
* @type {boolean}
|
||||
*/
|
||||
get composed() {
|
||||
return Boolean(pd(this).event.composed);
|
||||
},
|
||||
|
||||
/**
|
||||
* The unix time of this event.
|
||||
* @type {number}
|
||||
*/
|
||||
get timeStamp() {
|
||||
return pd(this).timeStamp;
|
||||
},
|
||||
|
||||
/**
|
||||
* The target of this event.
|
||||
* @type {EventTarget}
|
||||
* @deprecated
|
||||
*/
|
||||
get srcElement() {
|
||||
return pd(this).eventTarget;
|
||||
},
|
||||
|
||||
/**
|
||||
* The flag to stop event bubbling.
|
||||
* @type {boolean}
|
||||
* @deprecated
|
||||
*/
|
||||
get cancelBubble() {
|
||||
return pd(this).stopped;
|
||||
},
|
||||
set cancelBubble(value) {
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
const data = pd(this);
|
||||
|
||||
data.stopped = true;
|
||||
if (typeof data.event.cancelBubble === 'boolean') {
|
||||
data.event.cancelBubble = true;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The flag to indicate cancellation state.
|
||||
* @type {boolean}
|
||||
* @deprecated
|
||||
*/
|
||||
get returnValue() {
|
||||
return !pd(this).canceled;
|
||||
},
|
||||
set returnValue(value) {
|
||||
if (!value) {
|
||||
setCancelFlag(pd(this));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize this event object. But do nothing under event dispatching.
|
||||
* @param {string} type The event type.
|
||||
* @param {boolean} [bubbles=false] The flag to be possible to bubble up.
|
||||
* @param {boolean} [cancelable=false] The flag to be possible to cancel.
|
||||
* @deprecated
|
||||
*/
|
||||
initEvent() {
|
||||
// Do nothing.
|
||||
},
|
||||
};
|
||||
|
||||
// `constructor` is not enumerable.
|
||||
Object.defineProperty(Event.prototype, 'constructor', {
|
||||
value: Event,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
});
|
||||
|
||||
// Ensure `event instanceof window.Event` is `true`.
|
||||
if (typeof window !== 'undefined' && typeof window.Event !== 'undefined') {
|
||||
Object.setPrototypeOf(Event.prototype, window.Event.prototype);
|
||||
|
||||
// Make association for wrappers.
|
||||
wrappers.set(window.Event.prototype, Event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the property descriptor to redirect a given property.
|
||||
* @param {string} key Property name to define property descriptor.
|
||||
* @returns {PropertyDescriptor} The property descriptor to redirect the property.
|
||||
* @private
|
||||
*/
|
||||
function defineRedirectDescriptor(key) {
|
||||
return {
|
||||
get() {
|
||||
return pd(this).event[key];
|
||||
},
|
||||
set(value) {
|
||||
pd(this).event[key] = value;
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the property descriptor to call a given method property.
|
||||
* @param {string} key Property name to define property descriptor.
|
||||
* @returns {PropertyDescriptor} The property descriptor to call the method property.
|
||||
* @private
|
||||
*/
|
||||
function defineCallDescriptor(key) {
|
||||
return {
|
||||
value() {
|
||||
const event = pd(this).event;
|
||||
return event[key].apply(event, arguments);
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Define new wrapper class.
|
||||
* @param {Function} BaseEvent The base wrapper class.
|
||||
* @param {Object} proto The prototype of the original event.
|
||||
* @returns {Function} The defined wrapper class.
|
||||
* @private
|
||||
*/
|
||||
function defineWrapper(BaseEvent, proto) {
|
||||
const keys = Object.keys(proto);
|
||||
if (keys.length === 0) {
|
||||
return BaseEvent;
|
||||
}
|
||||
|
||||
/** CustomEvent */
|
||||
function CustomEvent(eventTarget, event) {
|
||||
BaseEvent.call(this, eventTarget, event);
|
||||
}
|
||||
|
||||
CustomEvent.prototype = Object.create(BaseEvent.prototype, {
|
||||
constructor: { value: CustomEvent, configurable: true, writable: true },
|
||||
});
|
||||
|
||||
// Define accessors.
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
const key = keys[i];
|
||||
if (!(key in BaseEvent.prototype)) {
|
||||
const descriptor = Object.getOwnPropertyDescriptor(proto, key);
|
||||
const isFunc = typeof descriptor.value === 'function';
|
||||
Object.defineProperty(
|
||||
CustomEvent.prototype,
|
||||
key,
|
||||
isFunc ? defineCallDescriptor(key) : defineRedirectDescriptor(key)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return CustomEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapper class of a given prototype.
|
||||
* @param {Object} proto The prototype of the original event to get its wrapper.
|
||||
* @returns {Function} The wrapper class.
|
||||
* @private
|
||||
*/
|
||||
function getWrapper(proto) {
|
||||
if (proto == null || proto === Object.prototype) {
|
||||
return Event;
|
||||
}
|
||||
|
||||
let wrapper = wrappers.get(proto);
|
||||
if (wrapper == null) {
|
||||
wrapper = defineWrapper(getWrapper(Object.getPrototypeOf(proto)), proto);
|
||||
wrappers.set(proto, wrapper);
|
||||
}
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap a given event to management a dispatching.
|
||||
* @param {EventTarget} eventTarget The event target of this dispatching.
|
||||
* @param {Object} event The event to wrap.
|
||||
* @returns {Event} The wrapper instance.
|
||||
* @private
|
||||
*/
|
||||
export function wrapEvent(eventTarget, event) {
|
||||
const Wrapper = getWrapper(Object.getPrototypeOf(event));
|
||||
return new Wrapper(eventTarget, event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the immediateStopped flag of a given event.
|
||||
* @param {Event} event The event to get.
|
||||
* @returns {boolean} The flag to stop propagation immediately.
|
||||
* @private
|
||||
*/
|
||||
export function isStopped(event) {
|
||||
return pd(event).immediateStopped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current event phase of a given event.
|
||||
* @param {Event} event The event to set current target.
|
||||
* @param {number} eventPhase New event phase.
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
export function setEventPhase(event, eventPhase) {
|
||||
pd(event).eventPhase = eventPhase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current target of a given event.
|
||||
* @param {Event} event The event to set current target.
|
||||
* @param {EventTarget|null} currentTarget New current target.
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
export function setCurrentTarget(event, currentTarget) {
|
||||
pd(event).currentTarget = currentTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a passive listener of a given event.
|
||||
* @param {Event} event The event to set current target.
|
||||
* @param {Function|null} passiveListener New passive listener.
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
export function setPassiveListener(event, passiveListener) {
|
||||
pd(event).passiveListener = passiveListener;
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
// Simple RSVP.EventTarget wrapper to make it more like a standard EventTarget
|
||||
import RSVP from 'rsvp';
|
||||
// See https://github.com/mysticatea/event-target-shim/blob/v4.0.2/src/event.mjs
|
||||
// The MIT License (MIT) - Copyright (c) 2015 Toru Nagashima
|
||||
import { setCurrentTarget, wrapEvent } from './event-target-shim/event';
|
||||
|
||||
const EventTarget = function() {};
|
||||
function callbacksFor(object) {
|
||||
let callbacks = object._promiseCallbacks;
|
||||
|
||||
if (!callbacks) {
|
||||
callbacks = object._promiseCallbacks = {};
|
||||
}
|
||||
|
||||
return callbacks;
|
||||
}
|
||||
EventTarget.prototype = Object.assign(
|
||||
Object.create(Object.prototype, {
|
||||
constructor: {
|
||||
value: EventTarget,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
},
|
||||
}),
|
||||
{
|
||||
dispatchEvent: function(obj) {
|
||||
// borrow just what I need from event-target-shim
|
||||
// to make true events even ErrorEvents with targets
|
||||
const wrappedEvent = wrapEvent(this, obj);
|
||||
setCurrentTarget(wrappedEvent, null);
|
||||
// RSVP trigger doesn't bind to `this`
|
||||
// the rest is pretty much the contents of `trigger`
|
||||
// but with a `.bind(this)` to make it compatible
|
||||
// with standard EventTarget
|
||||
// we use `let` and `callbacksFor` above, just to keep things the same as rsvp.js
|
||||
const eventName = obj.type;
|
||||
const options = wrappedEvent;
|
||||
let allCallbacks = callbacksFor(this);
|
||||
|
||||
let callbacks = allCallbacks[eventName];
|
||||
if (callbacks) {
|
||||
// Don't cache the callbacks.length since it may grow
|
||||
let callback;
|
||||
for (let i = 0; i < callbacks.length; i++) {
|
||||
callback = callbacks[i];
|
||||
callback.bind(this)(options);
|
||||
}
|
||||
}
|
||||
},
|
||||
addEventListener: function(event, cb) {
|
||||
this.on(event, cb);
|
||||
},
|
||||
removeEventListener: function(event, cb) {
|
||||
try {
|
||||
this.off(event, cb);
|
||||
} catch (e) {
|
||||
// passthrough
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
RSVP.EventTarget.mixin(EventTarget.prototype);
|
||||
export default EventTarget;
|
|
@ -77,9 +77,13 @@ export default function(changeset = defaultChangeset, getFormNameProperty = pars
|
|||
}
|
||||
const data = this.getData();
|
||||
// ember-data/changeset dance
|
||||
// TODO: This works for ember-data RecordSets and Changesets but not for plain js Objects
|
||||
// see settings
|
||||
const json = typeof data.toJSON === 'function' ? data.toJSON() : get(data, 'data').toJSON();
|
||||
// if the form doesn't include a property then throw so it can be
|
||||
// caught outside, therefore the user can deal with things that aren't in the data
|
||||
// TODO: possibly need to add support for deeper properties using `get` here
|
||||
// for example `client.blocking` instead of just `blocking`
|
||||
if (!Object.keys(json).includes(prop)) {
|
||||
const error = new Error(`${prop} property doesn't exist`);
|
||||
error.target = target;
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
@setupApplicationTest
|
||||
Feature: dc / list-blocking
|
||||
In order to see updates without refreshing the page
|
||||
As a user
|
||||
I want to see changes if I change consul externally
|
||||
Background:
|
||||
Given 1 datacenter model with the value "dc-1"
|
||||
And settings from yaml
|
||||
---
|
||||
consul:client:
|
||||
blocking: 1
|
||||
throttle: 200
|
||||
---
|
||||
Scenario:
|
||||
And 3 [Model] models
|
||||
And a network latency of 100
|
||||
When I visit the [Page] page for yaml
|
||||
---
|
||||
dc: dc-1
|
||||
---
|
||||
Then the url should be /dc-1/[Url]
|
||||
And pause until I see 3 [Model] models
|
||||
And an external edit results in 5 [Model] models
|
||||
And pause until I see 5 [Model] models
|
||||
And an external edit results in 1 [Model] model
|
||||
And pause until I see 1 [Model] model
|
||||
And an external edit results in 0 [Model] models
|
||||
And pause until I see 0 [Model] models
|
||||
Where:
|
||||
--------------------------------------------
|
||||
| Page | Model | Url |
|
||||
| services | service | services |
|
||||
| nodes | node | nodes |
|
||||
--------------------------------------------
|
|
@ -44,7 +44,7 @@ Feature: Page Navigation
|
|||
| Item | Model | URL | Endpoint | Back |
|
||||
| service | services | /dc-1/services/service-0 | /v1/health/service/service-0?dc=dc-1 | /dc-1/services |
|
||||
| node | nodes | /dc-1/nodes/node-0 | /v1/session/node/node-0?dc=dc-1 | /dc-1/nodes |
|
||||
| kv | kvs | /dc-1/kv/necessitatibus-0/edit | /v1/session/info/ee52203d-989f-4f7a-ab5a-2bef004164ca?dc=dc-1 | /dc-1/kv |
|
||||
| kv | kvs | /dc-1/kv/0-key-value/edit | /v1/session/info/ee52203d-989f-4f7a-ab5a-2bef004164ca?dc=dc-1 | /dc-1/kv |
|
||||
# | acl | acls | /dc-1/acls/anonymous | /v1/acl/info/anonymous?dc=dc-1 | /dc-1/acls |
|
||||
| intention | intentions | /dc-1/intentions/ee52203d-989f-4f7a-ab5a-2bef004164ca | /v1/internal/ui/services?dc=dc-1 | /dc-1/intentions |
|
||||
| token | tokens | /dc-1/acls/tokens/ee52203d-989f-4f7a-ab5a-2bef004164ca | /v1/acl/policies?dc=dc-1 | /dc-1/acls/tokens |
|
||||
|
@ -116,7 +116,7 @@ Feature: Page Navigation
|
|||
Where:
|
||||
--------------------------------------------------------------------------------------------------------
|
||||
| Item | Model | URL | Back |
|
||||
| kv | kvs | /dc-1/kv/necessitatibus-0/edit | /dc-1/kv |
|
||||
| kv | kvs | /dc-1/kv/0-key-value/edit | /dc-1/kv |
|
||||
# | acl | acls | /dc-1/acls/anonymous | /dc-1/acls |
|
||||
| intention | intentions | /dc-1/intentions/ee52203d-989f-4f7a-ab5a-2bef004164ca | /dc-1/intentions |
|
||||
--------------------------------------------------------------------------------------------------------
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
import steps from '../steps';
|
||||
|
||||
// step definitions that are shared between features should be moved to the
|
||||
// tests/acceptance/steps/steps.js file
|
||||
|
||||
export default function(assert) {
|
||||
return steps(assert).then('I should find a file', function() {
|
||||
assert.ok(true, this.step);
|
||||
});
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
import domEventSourceCallable from 'consul-ui/utils/dom/event-source/callable';
|
||||
import EventTarget from 'consul-ui/utils/dom/event-target/rsvp';
|
||||
import { Promise } from 'rsvp';
|
||||
|
||||
import { module } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
import test from 'ember-sinon-qunit/test-support/test';
|
||||
|
||||
module('Integration | Utility | dom/event-source/callable', function(hooks) {
|
||||
setupTest(hooks);
|
||||
test('it dispatches messages', function(assert) {
|
||||
assert.expect(1);
|
||||
const EventSource = domEventSourceCallable(EventTarget);
|
||||
const listener = this.stub();
|
||||
const source = new EventSource(
|
||||
function(configuration) {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
this.dispatchEvent({
|
||||
type: 'message',
|
||||
data: null,
|
||||
});
|
||||
resolve();
|
||||
}, configuration.milliseconds);
|
||||
});
|
||||
},
|
||||
{
|
||||
milliseconds: 100,
|
||||
}
|
||||
);
|
||||
source.addEventListener('message', function() {
|
||||
listener();
|
||||
});
|
||||
return new Promise(function(resolve) {
|
||||
setTimeout(function() {
|
||||
source.close();
|
||||
assert.equal(listener.callCount, 5);
|
||||
resolve();
|
||||
}, 550);
|
||||
});
|
||||
});
|
||||
test('it dispatches a single open event and closes when called with no callable', function(assert) {
|
||||
assert.expect(4);
|
||||
const EventSource = domEventSourceCallable(EventTarget);
|
||||
const listener = this.stub();
|
||||
const source = new EventSource();
|
||||
source.addEventListener('open', function(e) {
|
||||
assert.deepEqual(e.target, this);
|
||||
assert.equal(e.target.readyState, 1);
|
||||
listener();
|
||||
});
|
||||
return Promise.resolve().then(function() {
|
||||
assert.ok(listener.calledOnce);
|
||||
assert.equal(source.readyState, 2);
|
||||
});
|
||||
});
|
||||
test('it dispatches a single open event, and calls the specified callable that can dispatch an event', function(assert) {
|
||||
assert.expect(1);
|
||||
const EventSource = domEventSourceCallable(EventTarget);
|
||||
const listener = this.stub();
|
||||
const source = new EventSource(function() {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
this.dispatchEvent({
|
||||
type: 'message',
|
||||
data: {},
|
||||
});
|
||||
this.close();
|
||||
}, 190);
|
||||
});
|
||||
});
|
||||
source.addEventListener('open', function() {
|
||||
// open is called first
|
||||
listener();
|
||||
});
|
||||
return new Promise(function(resolve) {
|
||||
source.addEventListener('message', function() {
|
||||
// message is called second
|
||||
assert.ok(listener.calledOnce);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -65,7 +65,10 @@ export default function(assert) {
|
|||
}, yadda)
|
||||
)
|
||||
// doubles
|
||||
.given(['$number $model model[s]?', '$number $model models'], function(number, model) {
|
||||
.given(['an external edit results in $number $model model[s]?'], function(number, model) {
|
||||
return create(number, model);
|
||||
})
|
||||
.given(['$number $model model[s]?'], function(number, model) {
|
||||
return create(number, model);
|
||||
})
|
||||
.given(['$number $model model[s]? with the value "$value"'], function(number, model, value) {
|
||||
|
@ -77,7 +80,15 @@ export default function(assert) {
|
|||
return create(number, model, data);
|
||||
}
|
||||
)
|
||||
.given(["I'm using a legacy token"], function(number, model, data) {
|
||||
.given(['settings from yaml\n$yaml'], function(data) {
|
||||
return Object.keys(data).forEach(function(key) {
|
||||
window.localStorage[key] = JSON.stringify(data[key]);
|
||||
});
|
||||
})
|
||||
.given('a network latency of $number', function(number) {
|
||||
api.server.setCookie('CONSUL_LATENCY', number);
|
||||
})
|
||||
.given(["I'm using a legacy token"], function() {
|
||||
window.localStorage['consul:token'] = JSON.stringify({ AccessorID: null, SecretID: 'id' });
|
||||
})
|
||||
// TODO: Abstract this away from HTTP
|
||||
|
@ -188,6 +199,26 @@ export default function(assert) {
|
|||
});
|
||||
})
|
||||
// assertions
|
||||
.then('pause until I see $number $model model[s]?', function(num, model) {
|
||||
return new Promise(function(resolve) {
|
||||
let count = 0;
|
||||
const interval = setInterval(function() {
|
||||
if (++count >= 50) {
|
||||
clearInterval(interval);
|
||||
assert.ok(false);
|
||||
resolve();
|
||||
}
|
||||
const len = currentPage[`${pluralize(model)}`].filter(function(item) {
|
||||
return item.isVisible;
|
||||
}).length;
|
||||
if (len === num) {
|
||||
clearInterval(interval);
|
||||
assert.equal(len, num, `Expected ${num} ${model}s, saw ${len}`);
|
||||
resolve();
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
})
|
||||
.then('a $method request is made to "$url" with the body from yaml\n$yaml', function(
|
||||
method,
|
||||
url,
|
||||
|
@ -358,6 +389,9 @@ export default function(assert) {
|
|||
.then('I have settings like yaml\n$yaml', function(data) {
|
||||
// TODO: Inject this
|
||||
const settings = window.localStorage;
|
||||
// TODO: this and the setup should probably use consul:
|
||||
// as we are talking about 'settings' here not localStorage
|
||||
// so the prefix should be hidden
|
||||
Object.keys(data).forEach(function(prop) {
|
||||
const actual = settings.getItem(prop);
|
||||
const expected = data[prop];
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import { moduleFor, test } from 'ember-qunit';
|
||||
|
||||
moduleFor('controller:settings', 'Unit | Controller | settings', {
|
||||
// Specify the other units that are required for this test.
|
||||
needs: ['service:settings', 'service:dom'],
|
||||
});
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function(assert) {
|
||||
let controller = this.subject();
|
||||
assert.ok(controller);
|
||||
});
|
|
@ -3,6 +3,7 @@ import { moduleFor, test } from 'ember-qunit';
|
|||
moduleFor('route:settings', 'Unit | Route | settings', {
|
||||
// Specify the other units that are required for this test.
|
||||
needs: [
|
||||
'service:client/http',
|
||||
'service:repository/dc',
|
||||
'service:settings',
|
||||
'service:logger',
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
import domEventSourceBlocking, {
|
||||
create5xxBackoff,
|
||||
} from 'consul-ui/utils/dom/event-source/blocking';
|
||||
import { module } from 'qunit';
|
||||
import test from 'ember-sinon-qunit/test-support/test';
|
||||
|
||||
module('Unit | Utility | dom/event-source/blocking');
|
||||
|
||||
const createEventSource = function() {
|
||||
return class {
|
||||
constructor(cb) {
|
||||
this.readyState = 1;
|
||||
this.source = cb;
|
||||
this.source.apply(this, arguments);
|
||||
}
|
||||
addEventListener() {}
|
||||
removeEventListener() {}
|
||||
dispatchEvent() {}
|
||||
close() {}
|
||||
};
|
||||
};
|
||||
const createPromise = function(resolve = function() {}) {
|
||||
class PromiseMock {
|
||||
constructor(cb = function() {}) {
|
||||
cb(resolve);
|
||||
}
|
||||
then(cb) {
|
||||
setTimeout(() => cb.bind(this)(), 0);
|
||||
return this;
|
||||
}
|
||||
catch(cb) {
|
||||
cb({ message: 'error' });
|
||||
return this;
|
||||
}
|
||||
}
|
||||
PromiseMock.resolve = function() {
|
||||
return new PromiseMock();
|
||||
};
|
||||
return PromiseMock;
|
||||
};
|
||||
test('it creates an BlockingEventSource class implementing EventSource', function(assert) {
|
||||
const EventSource = createEventSource();
|
||||
const BlockingEventSource = domEventSourceBlocking(EventSource, function() {});
|
||||
assert.ok(BlockingEventSource instanceof Function);
|
||||
const source = new BlockingEventSource(function() {
|
||||
return createPromise().resolve();
|
||||
});
|
||||
assert.ok(source instanceof EventSource);
|
||||
});
|
||||
test("the 5xx backoff continues to throw when it's not a 5xx", function(assert) {
|
||||
const backoff = create5xxBackoff();
|
||||
[
|
||||
undefined,
|
||||
null,
|
||||
new Error(),
|
||||
{ errors: [] },
|
||||
{ errors: [{ status: '0' }] },
|
||||
{ errors: [{ status: 501 }] },
|
||||
{ errors: [{ status: '401' }] },
|
||||
{ errors: [{ status: '500' }] },
|
||||
{ errors: [{ status: '5' }] },
|
||||
{ errors: [{ status: '50' }] },
|
||||
{ errors: [{ status: '5000' }] },
|
||||
{ errors: [{ status: '5050' }] },
|
||||
].forEach(function(item) {
|
||||
assert.throws(function() {
|
||||
backoff(item);
|
||||
});
|
||||
});
|
||||
});
|
||||
test('the 5xx backoff returns a resolve promise on a 5xx (apart from 500)', function(assert) {
|
||||
[
|
||||
{ errors: [{ status: '501' }] },
|
||||
{ errors: [{ status: '503' }] },
|
||||
{ errors: [{ status: '504' }] },
|
||||
{ errors: [{ status: '524' }] },
|
||||
].forEach(item => {
|
||||
const timeout = this.stub().callsArg(0);
|
||||
const resolve = this.stub().withArgs(item);
|
||||
const Promise = createPromise(resolve);
|
||||
const backoff = create5xxBackoff(undefined, Promise, timeout);
|
||||
const promise = backoff(item);
|
||||
assert.ok(promise instanceof Promise, 'a promise was returned');
|
||||
assert.ok(resolve.calledOnce, 'the promise was resolved with the correct arguments');
|
||||
assert.ok(timeout.calledOnce, 'timeout was called once');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,145 @@
|
|||
import domEventSourceCache from 'consul-ui/utils/dom/event-source/cache';
|
||||
import { module } from 'qunit';
|
||||
import test from 'ember-sinon-qunit/test-support/test';
|
||||
|
||||
module('Unit | Utility | dom/event-source/cache');
|
||||
|
||||
const createEventSource = function() {
|
||||
return class {
|
||||
constructor(cb) {
|
||||
this.source = cb;
|
||||
this.source.apply(this, arguments);
|
||||
}
|
||||
addEventListener() {}
|
||||
removeEventListener() {}
|
||||
dispatchEvent() {}
|
||||
close() {}
|
||||
};
|
||||
};
|
||||
const createPromise = function(
|
||||
resolve = result => result,
|
||||
reject = (result = { message: 'error' }) => result
|
||||
) {
|
||||
class PromiseMock {
|
||||
constructor(cb = function() {}) {
|
||||
cb(resolve);
|
||||
}
|
||||
then(cb) {
|
||||
setTimeout(() => cb.bind(this)(resolve()), 0);
|
||||
return this;
|
||||
}
|
||||
catch(cb) {
|
||||
setTimeout(() => cb.bind(this)(reject()), 0);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
PromiseMock.resolve = function(result) {
|
||||
return new PromiseMock(function(resolve) {
|
||||
resolve(result);
|
||||
});
|
||||
};
|
||||
PromiseMock.reject = function() {
|
||||
return new PromiseMock();
|
||||
};
|
||||
return PromiseMock;
|
||||
};
|
||||
test('it returns a function', function(assert) {
|
||||
const EventSource = createEventSource();
|
||||
const Promise = createPromise();
|
||||
|
||||
const getCache = domEventSourceCache(function() {}, EventSource, Promise);
|
||||
assert.ok(typeof getCache === 'function');
|
||||
});
|
||||
test('getCache returns a function', function(assert) {
|
||||
const EventSource = createEventSource();
|
||||
const Promise = createPromise();
|
||||
|
||||
const getCache = domEventSourceCache(function() {}, EventSource, Promise);
|
||||
const obj = {};
|
||||
const cache = getCache(obj);
|
||||
assert.ok(typeof cache === 'function');
|
||||
});
|
||||
test('cache creates the default EventSource and keeps it open when there is a cursor', function(assert) {
|
||||
const EventSource = createEventSource();
|
||||
const stub = {
|
||||
configuration: { cursor: 1 },
|
||||
};
|
||||
const Promise = createPromise(function() {
|
||||
return stub;
|
||||
});
|
||||
const source = this.stub().returns(Promise.resolve());
|
||||
const cb = this.stub();
|
||||
const getCache = domEventSourceCache(source, EventSource, Promise);
|
||||
const obj = {};
|
||||
const cache = getCache(obj);
|
||||
const promisedEventSource = cache(cb, {
|
||||
key: 'key',
|
||||
settings: {
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
assert.ok(source.calledOnce, 'promisifying source called once');
|
||||
assert.ok(promisedEventSource instanceof Promise, 'source returns a Promise');
|
||||
const retrievedEventSource = cache(cb, {
|
||||
key: 'key',
|
||||
settings: {
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
assert.deepEqual(promisedEventSource, retrievedEventSource);
|
||||
assert.ok(source.calledTwice, 'promisifying source called once');
|
||||
assert.ok(retrievedEventSource instanceof Promise, 'source returns a Promise');
|
||||
});
|
||||
test('cache creates the default EventSource and keeps it open when there is a cursor', function(assert) {
|
||||
const EventSource = createEventSource();
|
||||
const stub = {
|
||||
close: this.stub(),
|
||||
configuration: { cursor: 1 },
|
||||
};
|
||||
const Promise = createPromise(function() {
|
||||
return stub;
|
||||
});
|
||||
const source = this.stub().returns(Promise.resolve());
|
||||
const cb = this.stub();
|
||||
const getCache = domEventSourceCache(source, EventSource, Promise);
|
||||
const obj = {};
|
||||
const cache = getCache(obj);
|
||||
const promisedEventSource = cache(cb, {
|
||||
key: 0,
|
||||
settings: {
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
assert.ok(source.calledOnce, 'promisifying source called once');
|
||||
assert.ok(cb.calledOnce, 'callable event source callable called once');
|
||||
assert.ok(promisedEventSource instanceof Promise, 'source returns a Promise');
|
||||
// >>
|
||||
return promisedEventSource.then(function() {
|
||||
assert.notOk(stub.close.called, "close wasn't called");
|
||||
});
|
||||
});
|
||||
test("cache creates the default EventSource and closes it when there isn't a cursor", function(assert) {
|
||||
const EventSource = createEventSource();
|
||||
const stub = {
|
||||
close: this.stub(),
|
||||
configuration: {},
|
||||
};
|
||||
const Promise = createPromise(function() {
|
||||
return stub;
|
||||
});
|
||||
const source = this.stub().returns(Promise.resolve());
|
||||
const cb = this.stub();
|
||||
const getCache = domEventSourceCache(source, EventSource, Promise);
|
||||
const obj = {};
|
||||
const cache = getCache(obj);
|
||||
const promisedEventSource = cache(cb, {
|
||||
key: 0,
|
||||
});
|
||||
assert.ok(source.calledOnce, 'promisifying source called once');
|
||||
assert.ok(cb.calledOnce, 'callable event source callable called once');
|
||||
assert.ok(promisedEventSource instanceof Promise, 'source returns a Promise');
|
||||
// >>
|
||||
return promisedEventSource.then(function() {
|
||||
assert.ok(stub.close.calledOnce, 'close was called');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,65 @@
|
|||
import domEventSourceCallable, { defaultRunner } from 'consul-ui/utils/dom/event-source/callable';
|
||||
import { module } from 'qunit';
|
||||
import test from 'ember-sinon-qunit/test-support/test';
|
||||
|
||||
module('Unit | Utility | dom/event-source/callable');
|
||||
|
||||
const createEventTarget = function() {
|
||||
return class {
|
||||
addEventListener() {}
|
||||
removeEventListener() {}
|
||||
dispatchEvent() {}
|
||||
};
|
||||
};
|
||||
const createPromise = function() {
|
||||
class PromiseMock {
|
||||
then(cb) {
|
||||
cb();
|
||||
return this;
|
||||
}
|
||||
catch(cb) {
|
||||
cb({ message: 'error' });
|
||||
return this;
|
||||
}
|
||||
}
|
||||
PromiseMock.resolve = function() {
|
||||
return new PromiseMock();
|
||||
};
|
||||
return PromiseMock;
|
||||
};
|
||||
test('it creates an EventSource class implementing EventTarget', function(assert) {
|
||||
const EventTarget = createEventTarget();
|
||||
const EventSource = domEventSourceCallable(EventTarget, createPromise());
|
||||
assert.ok(EventSource instanceof Function);
|
||||
const source = new EventSource();
|
||||
assert.ok(source instanceof EventTarget);
|
||||
});
|
||||
test('the default runner loops and can be closed', function(assert) {
|
||||
assert.expect(12); // 10 not closed, 1 to close and the final call count
|
||||
let count = 0;
|
||||
const isClosed = function() {
|
||||
count++;
|
||||
assert.ok(true);
|
||||
return count === 11;
|
||||
};
|
||||
const configuration = {};
|
||||
const then = this.stub().callsArg(0);
|
||||
const target = {
|
||||
source: function(configuration) {
|
||||
return {
|
||||
then: then,
|
||||
};
|
||||
},
|
||||
};
|
||||
defaultRunner(target, configuration, isClosed);
|
||||
assert.ok(then.callCount == 10);
|
||||
});
|
||||
test('it calls the defaultRunner', function(assert) {
|
||||
const Promise = createPromise();
|
||||
const EventTarget = createEventTarget();
|
||||
const run = this.stub();
|
||||
const EventSource = domEventSourceCallable(EventTarget, Promise, run);
|
||||
const source = new EventSource();
|
||||
assert.ok(run.calledOnce);
|
||||
assert.equal(source.readyState, 2);
|
||||
});
|
|
@ -0,0 +1,28 @@
|
|||
import {
|
||||
source,
|
||||
proxy,
|
||||
cache,
|
||||
resolve,
|
||||
CallableEventSource,
|
||||
ReopenableEventSource,
|
||||
BlockingEventSource,
|
||||
StorageEventSource,
|
||||
} from 'consul-ui/utils/dom/event-source/index';
|
||||
import { module, test } from 'qunit';
|
||||
|
||||
module('Unit | Utility | dom/event source/index');
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it works', function(assert) {
|
||||
// All The EventSource
|
||||
assert.ok(typeof CallableEventSource === 'function');
|
||||
assert.ok(typeof ReopenableEventSource === 'function');
|
||||
assert.ok(typeof BlockingEventSource === 'function');
|
||||
assert.ok(typeof StorageEventSource === 'function');
|
||||
|
||||
// Utils
|
||||
assert.ok(typeof source === 'function');
|
||||
assert.ok(typeof proxy === 'function');
|
||||
assert.ok(typeof cache === 'function');
|
||||
assert.ok(typeof resolve === 'function');
|
||||
});
|
|
@ -0,0 +1,10 @@
|
|||
import domEventSourceProxy from 'consul-ui/utils/dom/event-source/proxy';
|
||||
import { module, test } from 'qunit';
|
||||
|
||||
module('Unit | Utility | dom/event source/proxy');
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it works', function(assert) {
|
||||
let result = domEventSourceProxy();
|
||||
assert.ok(result);
|
||||
});
|
|
@ -0,0 +1,46 @@
|
|||
import domEventSourceReopenable from 'consul-ui/utils/dom/event-source/reopenable';
|
||||
import { module } from 'qunit';
|
||||
import test from 'ember-sinon-qunit/test-support/test';
|
||||
|
||||
module('Unit | Utility | dom/event-source/reopenable');
|
||||
|
||||
const createEventSource = function() {
|
||||
return class {
|
||||
constructor(cb) {
|
||||
this.readyState = 1;
|
||||
this.source = cb;
|
||||
this.source.apply(this, arguments);
|
||||
}
|
||||
addEventListener() {}
|
||||
removeEventListener() {}
|
||||
dispatchEvent() {}
|
||||
close() {}
|
||||
};
|
||||
};
|
||||
test('it creates an Reopenable class implementing EventSource', function(assert) {
|
||||
const EventSource = createEventSource();
|
||||
const ReopenableEventSource = domEventSourceReopenable(EventSource);
|
||||
assert.ok(ReopenableEventSource instanceof Function);
|
||||
const source = new ReopenableEventSource(function() {});
|
||||
assert.ok(source instanceof EventSource);
|
||||
});
|
||||
test('it reopens the event source when reopen is called', function(assert) {
|
||||
const callable = this.stub();
|
||||
const EventSource = createEventSource();
|
||||
const ReopenableEventSource = domEventSourceReopenable(EventSource);
|
||||
const source = new ReopenableEventSource(callable);
|
||||
assert.equal(source.readyState, 1);
|
||||
// first automatic EventSource `open`
|
||||
assert.ok(callable.calledOnce);
|
||||
source.readyState = 3;
|
||||
source.reopen();
|
||||
// still only called once as it hasn't completely closed yet
|
||||
// therefore is just opened by resetting the readyState
|
||||
assert.ok(callable.calledOnce);
|
||||
assert.equal(source.readyState, 1);
|
||||
// properly close the source
|
||||
source.readyState = 2;
|
||||
source.reopen();
|
||||
// this time it is reopened via a recall of the callable
|
||||
assert.ok(callable.calledTwice);
|
||||
});
|
|
@ -0,0 +1,10 @@
|
|||
import domEventSourceResolver from 'consul-ui/utils/dom/event-source/resolver';
|
||||
import { module, test } from 'qunit';
|
||||
|
||||
module('Unit | Utility | dom/event source/resolver');
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it works', function(assert) {
|
||||
let result = domEventSourceResolver();
|
||||
assert.ok(result);
|
||||
});
|
|
@ -0,0 +1,10 @@
|
|||
import domEventSourceStorage from 'consul-ui/utils/dom/event-source/storage';
|
||||
import { module, test } from 'qunit';
|
||||
|
||||
module('Unit | Utility | dom/event source/storage');
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it works', function(assert) {
|
||||
let result = domEventSourceStorage(function EventTarget() {});
|
||||
assert.ok(result);
|
||||
});
|
|
@ -0,0 +1,13 @@
|
|||
import domEventTargetRsvp from 'consul-ui/utils/dom/event-target/rsvp';
|
||||
import { module, test } from 'qunit';
|
||||
|
||||
module('Unit | Utility | dom/event-target/rsvp');
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it has EventTarget methods', function(assert) {
|
||||
const result = domEventTargetRsvp;
|
||||
assert.equal(typeof result, 'function');
|
||||
['addEventListener', 'removeEventListener', 'dispatchEvent'].forEach(function(item) {
|
||||
assert.equal(typeof result.prototype[item], 'function');
|
||||
});
|
||||
});
|
102
ui-v2/yarn.lock
102
ui-v2/yarn.lock
|
@ -543,6 +543,7 @@
|
|||
"@gardenhq/component-factory@^1.4.0":
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@gardenhq/component-factory/-/component-factory-1.4.0.tgz#f5da8ddf2050fde9c69f4426d61fe55de043e78d"
|
||||
integrity sha1-9dqN3yBQ/enGn0Qm1h/lXeBD540=
|
||||
dependencies:
|
||||
"@gardenhq/domino" "^1.0.0"
|
||||
"@gardenhq/tick-control" "^2.0.0"
|
||||
|
@ -552,6 +553,7 @@
|
|||
"@gardenhq/domino@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@gardenhq/domino/-/domino-1.0.0.tgz#832c493f3f05697b7df4ccce00c4cf620dc60923"
|
||||
integrity sha1-gyxJPz8FaXt99MzOAMTPYg3GCSM=
|
||||
optionalDependencies:
|
||||
min-document "^2.19.0"
|
||||
unfetch "^2.1.2"
|
||||
|
@ -560,6 +562,7 @@
|
|||
"@gardenhq/o@^8.0.1":
|
||||
version "8.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@gardenhq/o/-/o-8.0.1.tgz#d6772cec7e4295a951165284cf43fbd0a373b779"
|
||||
integrity sha1-1ncs7H5ClalRFlKEz0P70KNzt3k=
|
||||
dependencies:
|
||||
"@gardenhq/component-factory" "^1.4.0"
|
||||
"@gardenhq/tick-control" "^2.0.0"
|
||||
|
@ -577,10 +580,12 @@
|
|||
"@gardenhq/tick-control@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@gardenhq/tick-control/-/tick-control-2.0.0.tgz#f84fe38ca7a09b7b2b52f42945c50429ba639897"
|
||||
integrity sha1-+E/jjKegm3srUvQpRcUEKbpjmJc=
|
||||
|
||||
"@gardenhq/willow@^6.2.0":
|
||||
version "6.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@gardenhq/willow/-/willow-6.2.0.tgz#3e4bc220a89099732746ead3385cc097bfb70186"
|
||||
integrity sha1-PkvCIKiQmXMnRurTOFzAl7+3AYY=
|
||||
|
||||
"@glimmer/di@^0.2.0":
|
||||
version "0.2.1"
|
||||
|
@ -593,8 +598,9 @@
|
|||
"@glimmer/di" "^0.2.0"
|
||||
|
||||
"@hashicorp/api-double@^1.3.0":
|
||||
version "1.4.4"
|
||||
resolved "https://registry.yarnpkg.com/@hashicorp/api-double/-/api-double-1.4.4.tgz#db5521230b0031bfc3dc3cc5b775f17413a4fe91"
|
||||
version "1.4.5"
|
||||
resolved "https://registry.yarnpkg.com/@hashicorp/api-double/-/api-double-1.4.5.tgz#839ba882fad76eb17fd2eb3a8899bf5dd5a162a8"
|
||||
integrity sha512-X8xRtZGXu4JAlh/deaaPW15L8gJIqwNpVEM2OKLkQu1AWHXSh3NF8Vhd5U81061+Dha8Ohl8aEE7LZ8f1tPvzg==
|
||||
dependencies:
|
||||
"@gardenhq/o" "^8.0.1"
|
||||
"@gardenhq/tick-control" "^2.0.0"
|
||||
|
@ -606,12 +612,14 @@
|
|||
js-yaml "^3.10.0"
|
||||
|
||||
"@hashicorp/consul-api-double@^2.0.1":
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@hashicorp/consul-api-double/-/consul-api-double-2.0.1.tgz#eaf2e3f230fbdd876c90b931fd4bb4d94aac10e2"
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@hashicorp/consul-api-double/-/consul-api-double-2.1.0.tgz#511e6a48842ad31133e2070f3b2307568539b10e"
|
||||
integrity sha512-cyW7TiKQylrWzVUORT1e6m4SU8tQ1V5BYEKW2th7QwHP8OFazn/+om9hud/9X5YtjEuSPIQCmFIvhEVwZgLVpQ==
|
||||
|
||||
"@hashicorp/ember-cli-api-double@^1.3.0":
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@hashicorp/ember-cli-api-double/-/ember-cli-api-double-1.7.0.tgz#4fdab6152157dd82b999de030c593c87e0cdb8b7"
|
||||
integrity sha512-ojPcUPyId+3hTbwAtBGYbP5TfCGVAH8Ky6kH+BzlisIO/8XKURo9BSYnFtmYWLgXQVLOIE3iuoia5kOjGS/w2A==
|
||||
dependencies:
|
||||
"@hashicorp/api-double" "^1.3.0"
|
||||
array-range "^1.0.1"
|
||||
|
@ -769,6 +777,7 @@
|
|||
"@xg-wang/whatwg-fetch@^3.0.0":
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@xg-wang/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#f7b222c012a238e7d6e89ed3d72a1e0edb58453d"
|
||||
integrity sha512-ULtqA6L75RLzTNW68IiOja0XYv4Ebc3OGMzfia1xxSEMpD0mk/pMvkQX0vbCFyQmKc5xGp80Ms2WiSlXLh8hbA==
|
||||
|
||||
"@xtuc/ieee754@^1.2.0":
|
||||
version "1.2.0"
|
||||
|
@ -995,6 +1004,7 @@ are-we-there-yet@~1.1.2:
|
|||
argparse@^1.0.7:
|
||||
version "1.0.10"
|
||||
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
|
||||
integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
|
||||
dependencies:
|
||||
sprintf-js "~1.0.2"
|
||||
|
||||
|
@ -1031,6 +1041,7 @@ array-find-index@^1.0.1:
|
|||
array-flatten@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
|
||||
integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=
|
||||
|
||||
array-map@~0.0.0:
|
||||
version "0.0.0"
|
||||
|
@ -1039,6 +1050,7 @@ array-map@~0.0.0:
|
|||
array-range@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/array-range/-/array-range-1.0.1.tgz#f56e46591843611c6a56f77ef02eda7c50089bfc"
|
||||
integrity sha1-9W5GWRhDYRxqVvd+8C7afFAIm/w=
|
||||
|
||||
array-reduce@~0.0.0:
|
||||
version "0.0.0"
|
||||
|
@ -1745,6 +1757,7 @@ babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0:
|
|||
babel-standalone@^6.24.2:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-standalone/-/babel-standalone-6.26.0.tgz#15fb3d35f2c456695815ebf1ed96fe7f015b6886"
|
||||
integrity sha1-Ffs9NfLEVmlYFevx7Zb+fwFbaIY=
|
||||
|
||||
babel-template@^6.24.1, babel-template@^6.26.0:
|
||||
version "6.26.0"
|
||||
|
@ -1903,6 +1916,7 @@ body-parser@1.18.2:
|
|||
body-parser@1.18.3, body-parser@^1.18.3:
|
||||
version "1.18.3"
|
||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.3.tgz#5b292198ffdd553b3a0f20ded0592b956955c8b4"
|
||||
integrity sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=
|
||||
dependencies:
|
||||
bytes "3.0.0"
|
||||
content-type "~1.0.4"
|
||||
|
@ -2640,6 +2654,7 @@ bytes@1:
|
|||
bytes@3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
|
||||
integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=
|
||||
|
||||
cacache@^10.0.4:
|
||||
version "10.0.4"
|
||||
|
@ -2895,6 +2910,7 @@ class-utils@^0.3.5:
|
|||
classtrophobic-es5@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/classtrophobic-es5/-/classtrophobic-es5-0.2.1.tgz#9bbfa62a9928abf26f385440032fb49da1cda88f"
|
||||
integrity sha1-m7+mKpkoq/JvOFRAAy+0naHNqI8=
|
||||
|
||||
clean-base-url@^1.0.0:
|
||||
version "1.0.0"
|
||||
|
@ -3091,6 +3107,7 @@ commander@^2.6.0:
|
|||
commander@~2.13.0:
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c"
|
||||
integrity sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==
|
||||
|
||||
commander@~2.17.1:
|
||||
version "2.17.1"
|
||||
|
@ -3206,10 +3223,12 @@ constants-browserify@^1.0.0, constants-browserify@~1.0.0:
|
|||
content-disposition@0.5.2:
|
||||
version "0.5.2"
|
||||
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4"
|
||||
integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ=
|
||||
|
||||
content-type@~1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
|
||||
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
|
||||
|
||||
continuable-cache@^0.3.1:
|
||||
version "0.3.1"
|
||||
|
@ -3232,6 +3251,7 @@ convert-source-map@~1.1.0:
|
|||
cookie-parser@^1.4.3:
|
||||
version "1.4.3"
|
||||
resolved "https://registry.yarnpkg.com/cookie-parser/-/cookie-parser-1.4.3.tgz#0fe31fa19d000b95f4aadf1f53fdc2b8a203baa5"
|
||||
integrity sha1-D+MfoZ0AC5X0qt8fU/3CuKIDuqU=
|
||||
dependencies:
|
||||
cookie "0.3.1"
|
||||
cookie-signature "1.0.6"
|
||||
|
@ -3239,10 +3259,12 @@ cookie-parser@^1.4.3:
|
|||
cookie-signature@1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
|
||||
integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw=
|
||||
|
||||
cookie@0.3.1:
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb"
|
||||
integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=
|
||||
|
||||
copy-concurrently@^1.0.0:
|
||||
version "1.0.5"
|
||||
|
@ -3601,6 +3623,7 @@ des.js@^1.0.0:
|
|||
destroy@~1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
|
||||
integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
|
||||
|
||||
detect-file@^0.1.0:
|
||||
version "0.1.0"
|
||||
|
@ -3657,6 +3680,7 @@ dom-serializer@0:
|
|||
dom-walk@^0.1.0:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018"
|
||||
integrity sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=
|
||||
|
||||
domain-browser@^1.1.1:
|
||||
version "1.2.0"
|
||||
|
@ -3716,6 +3740,7 @@ editions@^1.1.1:
|
|||
ee-first@1.1.1:
|
||||
version "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.30, electron-to-chromium@^1.3.47:
|
||||
version "1.3.62"
|
||||
|
@ -4701,6 +4726,7 @@ emojis-list@^2.0.0:
|
|||
encodeurl@~1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
||||
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
|
||||
|
||||
encoding@^0.1.11:
|
||||
version "0.1.12"
|
||||
|
@ -4866,6 +4892,7 @@ es6-weak-map@^2.0.1:
|
|||
escape-html@~1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
|
||||
integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=
|
||||
|
||||
escape-string-regexp@^1.0.0, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
|
||||
version "1.0.5"
|
||||
|
@ -4958,6 +4985,7 @@ espree@^3.5.4:
|
|||
esprima@^4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
|
||||
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
|
||||
|
||||
esprima@~3.0.0:
|
||||
version "3.0.0"
|
||||
|
@ -4994,6 +5022,7 @@ esutils@^2.0.2:
|
|||
etag@~1.8.1:
|
||||
version "1.8.1"
|
||||
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
|
||||
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
|
||||
|
||||
event-emitter@~0.3.5:
|
||||
version "0.3.5"
|
||||
|
@ -5189,6 +5218,7 @@ express@^4.10.7, express@^4.12.3:
|
|||
express@^4.16.2:
|
||||
version "4.16.4"
|
||||
resolved "https://registry.yarnpkg.com/express/-/express-4.16.4.tgz#fddef61926109e24c515ea97fd2f1bdbf62df12e"
|
||||
integrity sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==
|
||||
dependencies:
|
||||
accepts "~1.3.5"
|
||||
array-flatten "1.1.1"
|
||||
|
@ -5292,10 +5322,12 @@ eyes@0.1.x:
|
|||
fake-xml-http-request@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fake-xml-http-request/-/fake-xml-http-request-2.0.0.tgz#41a92f0ca539477700cb1dafd2df251d55dac8ff"
|
||||
integrity sha512-UjNnynb6eLAB0lyh2PlTEkjRJORnNsVF1hbzU+PQv89/cyBV9GDRCy7JAcLQgeCLYT+3kaumWWZKEJvbaK74eQ==
|
||||
|
||||
faker@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/faker/-/faker-4.1.0.tgz#1e45bbbecc6774b3c195fad2835109c6d748cc3f"
|
||||
integrity sha1-HkW7vsxndLPBlfrSg1EJxtdIzD8=
|
||||
|
||||
fast-deep-equal@^1.0.0:
|
||||
version "1.1.0"
|
||||
|
@ -5372,7 +5404,8 @@ file-entry-cache@^2.0.0:
|
|||
|
||||
file-saver@^1.3.3:
|
||||
version "1.3.8"
|
||||
resolved "http://registry.npmjs.org/file-saver/-/file-saver-1.3.8.tgz#e68a30c7cb044e2fb362b428469feb291c2e09d8"
|
||||
resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-1.3.8.tgz#e68a30c7cb044e2fb362b428469feb291c2e09d8"
|
||||
integrity sha512-spKHSBQIxxS81N/O21WmuXA2F6wppUCsutpzenOeZzOCCJ5gEfcbqJP983IrpLXzYmXnMUa6J03SubcNPdKrlg==
|
||||
|
||||
filename-regex@^2.0.0:
|
||||
version "2.0.1"
|
||||
|
@ -5410,7 +5443,8 @@ fill-range@^4.0.0:
|
|||
|
||||
finalhandler@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105"
|
||||
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105"
|
||||
integrity sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==
|
||||
dependencies:
|
||||
debug "2.6.9"
|
||||
encodeurl "~1.0.2"
|
||||
|
@ -5552,6 +5586,7 @@ formatio@1.2.0:
|
|||
forwarded@~0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
|
||||
integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=
|
||||
|
||||
fragment-cache@^0.2.1:
|
||||
version "0.2.1"
|
||||
|
@ -5562,6 +5597,7 @@ fragment-cache@^0.2.1:
|
|||
fresh@0.5.2:
|
||||
version "0.5.2"
|
||||
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
|
||||
integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
|
||||
|
||||
from2@^2.1.0:
|
||||
version "2.3.0"
|
||||
|
@ -6200,7 +6236,8 @@ http-errors@1.6.2:
|
|||
|
||||
http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3:
|
||||
version "1.6.3"
|
||||
resolved "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d"
|
||||
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d"
|
||||
integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=
|
||||
dependencies:
|
||||
depd "~1.1.2"
|
||||
inherits "2.0.3"
|
||||
|
@ -6260,6 +6297,7 @@ husky@^1.1.0:
|
|||
hyperhtml@^0.15.5:
|
||||
version "0.15.10"
|
||||
resolved "https://registry.yarnpkg.com/hyperhtml/-/hyperhtml-0.15.10.tgz#5e5f42393d4fc30cd803063fb88a5c9d97625e1c"
|
||||
integrity sha512-D3dkc5nac47dzGXhLfGTearEoUXLk8ijSrj+5ngEH1Od+6EZ9Cwjspj/MWWx74DWpvCH+glO7M+B7WqCYSzkTg==
|
||||
|
||||
iconv-lite@0.4.19:
|
||||
version "0.4.19"
|
||||
|
@ -6268,6 +6306,7 @@ iconv-lite@0.4.19:
|
|||
iconv-lite@0.4.23:
|
||||
version "0.4.23"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63"
|
||||
integrity sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==
|
||||
dependencies:
|
||||
safer-buffer ">= 2.1.2 < 3"
|
||||
|
||||
|
@ -6431,6 +6470,7 @@ invert-kv@^1.0.0:
|
|||
ipaddr.js@1.8.0:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.8.0.tgz#eaa33d6ddd7ace8f7f6fe0c9ca0440e706738b1e"
|
||||
integrity sha1-6qM9bd16zo9/b+DJygRA5wZzix4=
|
||||
|
||||
is-accessor-descriptor@^0.1.6:
|
||||
version "0.1.6"
|
||||
|
@ -6631,6 +6671,7 @@ is-path-inside@^1.0.0:
|
|||
is-plain-obj@^1.1:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
|
||||
integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4=
|
||||
|
||||
is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4:
|
||||
version "2.0.4"
|
||||
|
@ -6862,7 +6903,15 @@ js-yaml@0.3.x:
|
|||
version "0.3.7"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-0.3.7.tgz#d739d8ee86461e54b354d6a7d7d1f2ad9a167f62"
|
||||
|
||||
js-yaml@^3.10.0, js-yaml@^3.11.0, js-yaml@^3.12.0, js-yaml@^3.8.4:
|
||||
js-yaml@^3.10.0, js-yaml@^3.11.0, js-yaml@^3.8.4:
|
||||
version "3.12.1"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.1.tgz#295c8632a18a23e054cf5c9d3cecafe678167600"
|
||||
integrity sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==
|
||||
dependencies:
|
||||
argparse "^1.0.7"
|
||||
esprima "^4.0.0"
|
||||
|
||||
js-yaml@^3.12.0:
|
||||
version "3.12.0"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1"
|
||||
dependencies:
|
||||
|
@ -7701,7 +7750,8 @@ mdurl@^1.0.1:
|
|||
|
||||
media-typer@0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
|
||||
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
|
||||
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
|
||||
|
||||
mem@^1.1.0:
|
||||
version "1.1.0"
|
||||
|
@ -7740,10 +7790,12 @@ meow@^3.4.0, meow@^3.7.0:
|
|||
merge-descriptors@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
|
||||
integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=
|
||||
|
||||
merge-options@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/merge-options/-/merge-options-1.0.1.tgz#2a64b24457becd4e4dc608283247e94ce589aa32"
|
||||
integrity sha512-iuPV41VWKWBIOpBsjoxjDZw8/GbSfZ2mk7N1453bwMrfzdrIk7EzBd+8UVR6rkw67th7xnk9Dytl3J+lHPdxvg==
|
||||
dependencies:
|
||||
is-plain-obj "^1.1"
|
||||
|
||||
|
@ -7772,6 +7824,7 @@ merge@^1.1.3:
|
|||
methods@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
|
||||
integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
|
||||
|
||||
micromatch@^2.1.5, micromatch@^2.3.11, micromatch@^2.3.7:
|
||||
version "2.3.11"
|
||||
|
@ -7827,6 +7880,7 @@ mime-db@~1.36.0:
|
|||
mime-db@~1.37.0:
|
||||
version "1.37.0"
|
||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.37.0.tgz#0b6a0ce6fdbe9576e25f1f2d2fde8830dc0ad0d8"
|
||||
integrity sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==
|
||||
|
||||
mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.7:
|
||||
version "2.1.20"
|
||||
|
@ -7843,12 +7897,14 @@ mime-types@^2.1.18:
|
|||
mime-types@~2.1.18:
|
||||
version "2.1.21"
|
||||
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.21.tgz#28995aa1ecb770742fe6ae7e58f9181c744b3f96"
|
||||
integrity sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==
|
||||
dependencies:
|
||||
mime-db "~1.37.0"
|
||||
|
||||
mime@1.4.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6"
|
||||
integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==
|
||||
|
||||
mimic-fn@^1.0.0:
|
||||
version "1.2.0"
|
||||
|
@ -7857,6 +7913,7 @@ mimic-fn@^1.0.0:
|
|||
min-document@^2.19.0:
|
||||
version "2.19.0"
|
||||
resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685"
|
||||
integrity sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=
|
||||
dependencies:
|
||||
dom-walk "^0.1.0"
|
||||
|
||||
|
@ -7972,6 +8029,7 @@ morgan@^1.8.1:
|
|||
mousetrap@^1.6.1:
|
||||
version "1.6.2"
|
||||
resolved "https://registry.yarnpkg.com/mousetrap/-/mousetrap-1.6.2.tgz#caadd9cf886db0986fb2fee59a82f6bd37527587"
|
||||
integrity sha512-jDjhi7wlHwdO6q6DS7YRmSHcuI+RVxadBkLt3KHrhd3C2b+w5pKefg3oj5beTcHZyVFA9Aksf+yEE1y5jxUjVA==
|
||||
|
||||
mout@^1.0.0:
|
||||
version "1.1.0"
|
||||
|
@ -7991,6 +8049,7 @@ move-concurrently@^1.0.1:
|
|||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
|
||||
|
||||
ms@^2.1.1:
|
||||
version "2.1.1"
|
||||
|
@ -8051,7 +8110,8 @@ natural-compare@^1.4.0:
|
|||
|
||||
ncp@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "http://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3"
|
||||
resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3"
|
||||
integrity sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=
|
||||
|
||||
needle@^2.2.1:
|
||||
version "2.2.4"
|
||||
|
@ -8064,6 +8124,7 @@ needle@^2.2.1:
|
|||
negotiator@0.6.1:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9"
|
||||
integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=
|
||||
|
||||
neo-async@^2.5.0:
|
||||
version "2.5.2"
|
||||
|
@ -8409,6 +8470,7 @@ object.values@^1.0.4:
|
|||
on-finished@~2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
|
||||
integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=
|
||||
dependencies:
|
||||
ee-first "1.1.1"
|
||||
|
||||
|
@ -8607,6 +8669,7 @@ parseuri@0.0.5:
|
|||
parseurl@~1.3.2:
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3"
|
||||
integrity sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=
|
||||
|
||||
pascalcase@^0.1.1:
|
||||
version "0.1.1"
|
||||
|
@ -8663,6 +8726,7 @@ path-posix@^1.0.0:
|
|||
path-to-regexp@0.1.7:
|
||||
version "0.1.7"
|
||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
|
||||
integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
|
||||
|
||||
path-to-regexp@^1.7.0:
|
||||
version "1.7.0"
|
||||
|
@ -8783,6 +8847,7 @@ preserve@^0.2.0:
|
|||
pretender@^2.0.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/pretender/-/pretender-2.1.1.tgz#5085f0a1272c31d5b57c488386f69e6ca207cb35"
|
||||
integrity sha512-IkidsJzaroAanw3I43tKCFm2xCpurkQr9aPXv5/jpN+LfCwDaeI8rngVWtQZTx4qqbhc5zJspnLHJ4N/25KvDQ==
|
||||
dependencies:
|
||||
"@xg-wang/whatwg-fetch" "^3.0.0"
|
||||
fake-xml-http-request "^2.0.0"
|
||||
|
@ -8974,6 +9039,7 @@ randomfill@^1.0.3:
|
|||
range-parser@~1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e"
|
||||
integrity sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=
|
||||
|
||||
raw-body@2.3.2:
|
||||
version "2.3.2"
|
||||
|
@ -8987,6 +9053,7 @@ raw-body@2.3.2:
|
|||
raw-body@2.3.3:
|
||||
version "2.3.3"
|
||||
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.3.tgz#1b324ece6b5706e153855bc1148c65bb7f6ea0c3"
|
||||
integrity sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==
|
||||
dependencies:
|
||||
bytes "3.0.0"
|
||||
http-errors "1.6.3"
|
||||
|
@ -9115,6 +9182,7 @@ recast@^0.11.3:
|
|||
recursive-readdir-sync@^1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/recursive-readdir-sync/-/recursive-readdir-sync-1.0.6.tgz#1dbf6d32f3c5bb8d3cde97a6c588d547a9e13d56"
|
||||
integrity sha1-Hb9tMvPFu4083pemxYjVR6nhPVY=
|
||||
|
||||
redent@^1.0.0:
|
||||
version "1.0.0"
|
||||
|
@ -9436,6 +9504,7 @@ rollup-plugin-commonjs@^9.1.0:
|
|||
rollup-plugin-memory@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-memory/-/rollup-plugin-memory-2.0.0.tgz#0a8ac6b57fa0e714f89a15c3ac82bc93f89c47c5"
|
||||
integrity sha1-CorGtX+g5xT4mhXDrIK8k/icR8U=
|
||||
|
||||
rollup-plugin-node-resolve@^3.3.0:
|
||||
version "3.4.0"
|
||||
|
@ -9468,6 +9537,7 @@ rollup@^0.59.0:
|
|||
route-recognizer@^0.3.3:
|
||||
version "0.3.4"
|
||||
resolved "https://registry.yarnpkg.com/route-recognizer/-/route-recognizer-0.3.4.tgz#39ab1ffbce1c59e6d2bdca416f0932611e4f3ca3"
|
||||
integrity sha512-2+MhsfPhvauN1O8KaXpXAOfR/fwe8dnUXVM+xw7yt40lJRfPVQxV6yryZm0cgRvAj5fMF/mdRZbL2ptwbs5i2g==
|
||||
|
||||
rsvp@^3.0.14, rsvp@^3.0.16, rsvp@^3.0.17, rsvp@^3.0.18, rsvp@^3.0.21, rsvp@^3.0.6, rsvp@^3.1.0, rsvp@^3.2.1, rsvp@^3.3.3, rsvp@^3.5.0:
|
||||
version "3.6.2"
|
||||
|
@ -9550,6 +9620,7 @@ safe-regex@^1.1.0:
|
|||
"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||
|
||||
samsam@1.3.0, samsam@1.x, samsam@^1.1.3:
|
||||
version "1.3.0"
|
||||
|
@ -9619,6 +9690,7 @@ semver@~5.3.0:
|
|||
send@0.16.2:
|
||||
version "0.16.2"
|
||||
resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1"
|
||||
integrity sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==
|
||||
dependencies:
|
||||
debug "2.6.9"
|
||||
depd "~1.1.2"
|
||||
|
@ -9641,6 +9713,7 @@ serialize-javascript@^1.4.0:
|
|||
serve-static@1.13.2:
|
||||
version "1.13.2"
|
||||
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1"
|
||||
integrity sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==
|
||||
dependencies:
|
||||
encodeurl "~1.0.2"
|
||||
escape-html "~1.0.3"
|
||||
|
@ -9684,6 +9757,7 @@ setprototypeof@1.0.3:
|
|||
setprototypeof@1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656"
|
||||
integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==
|
||||
|
||||
sha.js@^2.4.0, sha.js@^2.4.8, sha.js@~2.4.4:
|
||||
version "2.4.11"
|
||||
|
@ -9894,6 +9968,7 @@ source-map@0.4.x, source-map@^0.4.2, source-map@^0.4.4:
|
|||
source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.0, source-map@~0.5.1, source-map@~0.5.3:
|
||||
version "0.5.7"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
|
||||
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
|
||||
|
||||
source-map@^0.6.1, source-map@~0.6.1:
|
||||
version "0.6.1"
|
||||
|
@ -9960,6 +10035,7 @@ sprintf-js@^1.0.3:
|
|||
sprintf-js@~1.0.2:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
|
||||
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
|
||||
|
||||
sri-toolbox@^0.2.0:
|
||||
version "0.2.0"
|
||||
|
@ -10012,6 +10088,7 @@ static-extend@^0.1.1:
|
|||
statuses@~1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087"
|
||||
integrity sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==
|
||||
|
||||
stdout-stream@^1.4.0:
|
||||
version "1.4.1"
|
||||
|
@ -10608,6 +10685,7 @@ underscore@~1.6.0:
|
|||
unfetch@^2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-2.1.2.tgz#684fee4d8acdb135bdb26c0364c642fc326ca95b"
|
||||
integrity sha1-aE/uTYrNsTW9smwDZMZC/DJsqVs=
|
||||
|
||||
unicode-canonical-property-names-ecmascript@^1.0.4:
|
||||
version "1.0.4"
|
||||
|
@ -10662,6 +10740,7 @@ universalify@^0.1.0:
|
|||
unpipe@1.0.0, unpipe@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
|
||||
integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=
|
||||
|
||||
unquote@~1.1.1:
|
||||
version "1.1.1"
|
||||
|
@ -10758,6 +10837,7 @@ util@^0.10.3:
|
|||
utils-merge@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
||||
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
|
||||
|
||||
uuid@^3.0.0, uuid@^3.1.0, uuid@^3.3.2:
|
||||
version "3.3.2"
|
||||
|
@ -10779,6 +10859,7 @@ validate-npm-package-name@^3.0.0:
|
|||
vary@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
|
||||
integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
|
||||
|
||||
verror@1.10.0:
|
||||
version "1.10.0"
|
||||
|
@ -11004,6 +11085,7 @@ xdg-basedir@^3.0.0:
|
|||
xhr2@^0.1.4:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/xhr2/-/xhr2-0.1.4.tgz#7f87658847716db5026323812f818cadab387a5f"
|
||||
integrity sha1-f4dliEdxbbUCYyOBL4GMras4el8=
|
||||
|
||||
xmldom@^0.1.19:
|
||||
version "0.1.27"
|
||||
|
|
Loading…
Reference in New Issue