diff --git a/ui/app/components/flex-masonry.js b/ui/app/components/flex-masonry.js
new file mode 100644
index 00000000000..5d29c54faf2
--- /dev/null
+++ b/ui/app/components/flex-masonry.js
@@ -0,0 +1,66 @@
+import Component from '@glimmer/component';
+import { tracked } from '@glimmer/tracking';
+import { run } from '@ember/runloop';
+import { action } from '@ember/object';
+import { minIndex, max } from 'd3-array';
+
+export default class FlexMasonry extends Component {
+ @tracked element = null;
+
+ @action
+ captureElement(element) {
+ this.element = element;
+ }
+
+ @action
+ reflow() {
+ run.next(() => {
+ // There's nothing to do if this is a single column layout
+ if (!this.element || this.args.columns === 1 || !this.args.columns) return;
+
+ const columns = new Array(this.args.columns).fill(null).map(() => ({
+ height: 0,
+ elements: [],
+ }));
+
+ const items = this.element.querySelectorAll('.flex-masonry-item');
+
+ // First pass: assign each element to a column based on the running heights of each column
+ for (let item of items) {
+ const styles = window.getComputedStyle(item);
+ const marginTop = parseFloat(styles.marginTop);
+ const marginBottom = parseFloat(styles.marginBottom);
+ const height = item.clientHeight;
+
+ // Pick the shortest column accounting for margins
+ const column = columns[minIndex(columns, c => c.height)];
+
+ // Add the new element's height to the column height
+ column.height += marginTop + height + marginBottom;
+ column.elements.push(item);
+ }
+
+ // Second pass: assign an order to each element based on their column and position in the column
+ columns
+ .mapBy('elements')
+ .flat()
+ .forEach((dc, index) => {
+ dc.style.order = index;
+ });
+
+ // Guarantee column wrapping as predicted (if the first item of a column is shorter than the difference
+ // beteen the height of the column and the previous column, then flexbox will naturally place the first
+ // item at the end of the previous column).
+ columns.forEach((column, index) => {
+ const nextHeight = index < columns.length - 1 ? columns[index + 1].height : 0;
+ const item = column.elements.lastObject;
+ if (item) {
+ item.style.flexBasis = item.clientHeight + Math.max(0, nextHeight - column.height) + 'px';
+ }
+ });
+
+ // Set the max height of the container to the height of the tallest column
+ this.element.style.maxHeight = max(columns.mapBy('height')) + 1 + 'px';
+ });
+ }
+}
diff --git a/ui/app/components/topo-viz.js b/ui/app/components/topo-viz.js
new file mode 100644
index 00000000000..ee3616102d0
--- /dev/null
+++ b/ui/app/components/topo-viz.js
@@ -0,0 +1,271 @@
+import Component from '@glimmer/component';
+import { tracked } from '@glimmer/tracking';
+import { action, set } from '@ember/object';
+import { run } from '@ember/runloop';
+import { scaleLinear } from 'd3-scale';
+import { extent, deviation, mean } from 'd3-array';
+import { line, curveBasis } from 'd3-shape';
+
+export default class TopoViz extends Component {
+ @tracked element = null;
+ @tracked topology = { datacenters: [] };
+
+ @tracked activeNode = null;
+ @tracked activeAllocation = null;
+ @tracked activeEdges = [];
+ @tracked edgeOffset = { x: 0, y: 0 };
+
+ get isSingleColumn() {
+ if (this.topology.datacenters.length <= 1) return true;
+
+ // Compute the coefficient of variance to determine if it would be
+ // better to stack datacenters or place them in columns
+ const nodeCounts = this.topology.datacenters.map(datacenter => datacenter.nodes.length);
+ const variationCoefficient = deviation(nodeCounts) / mean(nodeCounts);
+
+ // The point at which the varation is too extreme for a two column layout
+ const threshold = 0.5;
+ if (variationCoefficient > threshold) return true;
+ return false;
+ }
+
+ get datacenterIsSingleColumn() {
+ // If there are enough nodes, use two columns of nodes within
+ // a single column layout of datacenters to increase density.
+ return !this.isSingleColumn || (this.isSingleColumn && this.args.nodes.length <= 20);
+ }
+
+ // Once a cluster is large enough, the exact details of a node are
+ // typically irrelevant and a waste of space.
+ get isDense() {
+ return this.args.nodes.length > 50;
+ }
+
+ dataForNode(node) {
+ return {
+ node,
+ datacenter: node.datacenter,
+ memory: node.resources.memory,
+ cpu: node.resources.cpu,
+ allocations: [],
+ isSelected: false,
+ };
+ }
+
+ dataForAllocation(allocation, node) {
+ const jobId = allocation.belongsTo('job').id();
+ return {
+ allocation,
+ node,
+ jobId,
+ groupKey: JSON.stringify([jobId, allocation.taskGroupName]),
+ memory: allocation.allocatedResources.memory,
+ cpu: allocation.allocatedResources.cpu,
+ memoryPercent: allocation.allocatedResources.memory / node.memory,
+ cpuPercent: allocation.allocatedResources.cpu / node.cpu,
+ isSelected: false,
+ };
+ }
+
+ @action
+ buildTopology() {
+ const nodes = this.args.nodes;
+ const allocations = this.args.allocations;
+
+ // Wrap nodes in a topo viz specific data structure and build an index to speed up allocation assignment
+ const nodeContainers = [];
+ const nodeIndex = {};
+ nodes.forEach(node => {
+ const container = this.dataForNode(node);
+ nodeContainers.push(container);
+ nodeIndex[node.id] = container;
+ });
+
+ // Wrap allocations in a topo viz specific data structure, assign allocations to nodes, and build an allocation
+ // index keyed off of job and task group
+ const allocationIndex = {};
+ allocations.forEach(allocation => {
+ const nodeId = allocation.belongsTo('node').id();
+ const nodeContainer = nodeIndex[nodeId];
+ if (!nodeContainer)
+ throw new Error(`Node ${nodeId} for alloc ${allocation.id} not in index.`);
+
+ const allocationContainer = this.dataForAllocation(allocation, nodeContainer);
+ nodeContainer.allocations.push(allocationContainer);
+
+ const key = allocationContainer.groupKey;
+ if (!allocationIndex[key]) allocationIndex[key] = [];
+ allocationIndex[key].push(allocationContainer);
+ });
+
+ // Group nodes into datacenters
+ const datacentersMap = nodeContainers.reduce((datacenters, nodeContainer) => {
+ if (!datacenters[nodeContainer.datacenter]) datacenters[nodeContainer.datacenter] = [];
+ datacenters[nodeContainer.datacenter].push(nodeContainer);
+ return datacenters;
+ }, {});
+
+ // Turn hash of datacenters into a sorted array
+ const datacenters = Object.keys(datacentersMap)
+ .map(key => ({ name: key, nodes: datacentersMap[key] }))
+ .sortBy('name');
+
+ const topology = {
+ datacenters,
+ allocationIndex,
+ selectedKey: null,
+ heightScale: scaleLinear()
+ .range([15, 40])
+ .domain(extent(nodeContainers.mapBy('memory'))),
+ };
+ this.topology = topology;
+ }
+
+ @action
+ captureElement(element) {
+ this.element = element;
+ }
+
+ @action
+ showNodeDetails(node) {
+ if (this.activeNode) {
+ set(this.activeNode, 'isSelected', false);
+ }
+
+ this.activeNode = this.activeNode === node ? null : node;
+
+ if (this.activeNode) {
+ set(this.activeNode, 'isSelected', true);
+ }
+
+ if (this.args.onNodeSelect) this.args.onNodeSelect(this.activeNode);
+ }
+
+ @action
+ associateAllocations(allocation) {
+ if (this.activeAllocation === allocation) {
+ this.activeAllocation = null;
+ this.activeEdges = [];
+
+ if (this.topology.selectedKey) {
+ const selectedAllocations = this.topology.allocationIndex[this.topology.selectedKey];
+ if (selectedAllocations) {
+ selectedAllocations.forEach(allocation => {
+ set(allocation, 'isSelected', false);
+ });
+ }
+ set(this.topology, 'selectedKey', null);
+ }
+ } else {
+ if (this.activeNode) {
+ set(this.activeNode, 'isSelected', false);
+ }
+ this.activeNode = null;
+ this.activeAllocation = allocation;
+ const selectedAllocations = this.topology.allocationIndex[this.topology.selectedKey];
+ if (selectedAllocations) {
+ selectedAllocations.forEach(allocation => {
+ set(allocation, 'isSelected', false);
+ });
+ }
+
+ set(this.topology, 'selectedKey', allocation.groupKey);
+ const newAllocations = this.topology.allocationIndex[this.topology.selectedKey];
+ if (newAllocations) {
+ newAllocations.forEach(allocation => {
+ set(allocation, 'isSelected', true);
+ });
+ }
+
+ this.computedActiveEdges();
+ }
+ if (this.args.onAllocationSelect)
+ this.args.onAllocationSelect(this.activeAllocation && this.activeAllocation.allocation);
+ if (this.args.onNodeSelect) this.args.onNodeSelect(this.activeNode);
+ }
+
+ @action
+ computedActiveEdges() {
+ // Wait a render cycle
+ run.next(() => {
+ const path = line().curve(curveBasis);
+ // 1. Get the active element
+ const allocation = this.activeAllocation.allocation;
+ const activeEl = this.element.querySelector(`[data-allocation-id="${allocation.id}"]`);
+ const activePoint = centerOfBBox(activeEl.getBoundingClientRect());
+
+ // 2. Collect the mem and cpu pairs for all selected allocs
+ const selectedMem = Array.from(this.element.querySelectorAll('.memory .bar.is-selected'));
+ const selectedPairs = selectedMem.map(mem => {
+ const id = mem.closest('[data-allocation-id]').dataset.allocationId;
+ const cpu = mem
+ .closest('.topo-viz-node')
+ .querySelector(`.cpu .bar[data-allocation-id="${id}"]`);
+ return [mem, cpu];
+ });
+ const selectedPoints = selectedPairs.map(pair => {
+ return pair.map(el => centerOfBBox(el.getBoundingClientRect()));
+ });
+
+ // 3. For each pair, compute the midpoint of the truncated triangle of points [Mem, Cpu, Active]
+ selectedPoints.forEach(points => {
+ const d1 = pointBetween(points[0], activePoint, 100, 0.5);
+ const d2 = pointBetween(points[1], activePoint, 100, 0.5);
+ points.push(midpoint(d1, d2));
+ });
+
+ // 4. Generate curves for each active->mem and active->cpu pair going through the bisector
+ const curves = [];
+ // Steps are used to restrict the range of curves. The closer control points are placed, the less
+ // curvature the curve generator will generate.
+ const stepsMain = [0, 0.8, 1.0];
+ // The second prong the fork does not need to retrace the entire path from the activePoint
+ const stepsSecondary = [0.8, 1.0];
+ selectedPoints.forEach(points => {
+ curves.push(
+ curveFromPoints(...pointsAlongPath(activePoint, points[2], stepsMain), points[0]),
+ curveFromPoints(...pointsAlongPath(activePoint, points[2], stepsSecondary), points[1])
+ );
+ });
+
+ this.activeEdges = curves.map(curve => path(curve));
+ this.edgeOffset = { x: window.visualViewport.pageLeft, y: window.visualViewport.pageTop };
+ });
+ }
+}
+
+function centerOfBBox(bbox) {
+ return {
+ x: bbox.x + bbox.width / 2,
+ y: bbox.y + bbox.height / 2,
+ };
+}
+
+function dist(p1, p2) {
+ return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
+}
+
+// Return the point between p1 and p2 at len (or pct if len > dist(p1, p2))
+function pointBetween(p1, p2, len, pct) {
+ const d = dist(p1, p2);
+ const ratio = d < len ? pct : len / d;
+ return pointBetweenPct(p1, p2, ratio);
+}
+
+function pointBetweenPct(p1, p2, pct) {
+ const dx = p2.x - p1.x;
+ const dy = p2.y - p1.y;
+ return { x: p1.x + dx * pct, y: p1.y + dy * pct };
+}
+
+function pointsAlongPath(p1, p2, pcts) {
+ return pcts.map(pct => pointBetweenPct(p1, p2, pct));
+}
+
+function midpoint(p1, p2) {
+ return pointBetweenPct(p1, p2, 0.5);
+}
+
+function curveFromPoints(...points) {
+ return points.map(p => [p.x, p.y]);
+}
diff --git a/ui/app/components/topo-viz/datacenter.js b/ui/app/components/topo-viz/datacenter.js
new file mode 100644
index 00000000000..0750fc1eb08
--- /dev/null
+++ b/ui/app/components/topo-viz/datacenter.js
@@ -0,0 +1,32 @@
+import Component from '@glimmer/component';
+
+export default class TopoVizDatacenter extends Component {
+ get scheduledAllocations() {
+ return this.args.datacenter.nodes.reduce(
+ (all, node) => all.concat(node.allocations.filterBy('allocation.isScheduled')),
+ []
+ );
+ }
+
+ get aggregatedAllocationResources() {
+ return this.scheduledAllocations.reduce(
+ (totals, allocation) => {
+ totals.cpu += allocation.cpu;
+ totals.memory += allocation.memory;
+ return totals;
+ },
+ { cpu: 0, memory: 0 }
+ );
+ }
+
+ get aggregatedNodeResources() {
+ return this.args.datacenter.nodes.reduce(
+ (totals, node) => {
+ totals.cpu += node.cpu;
+ totals.memory += node.memory;
+ return totals;
+ },
+ { cpu: 0, memory: 0 }
+ );
+ }
+}
diff --git a/ui/app/components/topo-viz/node.js b/ui/app/components/topo-viz/node.js
new file mode 100644
index 00000000000..3bef48ee049
--- /dev/null
+++ b/ui/app/components/topo-viz/node.js
@@ -0,0 +1,182 @@
+import Component from '@glimmer/component';
+import { tracked } from '@glimmer/tracking';
+import { action } from '@ember/object';
+import { guidFor } from '@ember/object/internals';
+
+export default class TopoVizNode extends Component {
+ @tracked data = { cpu: [], memory: [] };
+ @tracked dimensionsWidth = 0;
+ @tracked padding = 5;
+ @tracked activeAllocation = null;
+
+ get height() {
+ return this.args.heightScale ? this.args.heightScale(this.args.node.memory) : 15;
+ }
+
+ get labelHeight() {
+ return this.height / 2;
+ }
+
+ get paddingLeft() {
+ const labelWidth = 20;
+ return this.padding + labelWidth;
+ }
+
+ // Since strokes are placed centered on the perimeter of fills, The width of the stroke needs to be removed from
+ // the height of the fill to match unstroked height and avoid clipping.
+ get selectedHeight() {
+ return this.height - 1;
+ }
+
+ // Since strokes are placed centered on the perimeter of fills, half the width of the stroke needs to be added to
+ // the yOffset to match heights with unstroked shapes.
+ get selectedYOffset() {
+ return this.height + 2.5;
+ }
+
+ get yOffset() {
+ return this.height + 2;
+ }
+
+ get maskHeight() {
+ return this.height + this.yOffset;
+ }
+
+ get totalHeight() {
+ return this.maskHeight + this.padding * 2;
+ }
+
+ get maskId() {
+ return `topo-viz-node-mask-${guidFor(this)}`;
+ }
+
+ get count() {
+ return this.args.node.allocations.length;
+ }
+
+ get allocations() {
+ // Sort by the delta between memory and cpu percent. This creates the least amount of
+ // drift between the positional alignment of an alloc's cpu and memory representations.
+ return this.args.node.allocations.filterBy('allocation.isScheduled').sort((a, b) => {
+ const deltaA = Math.abs(a.memoryPercent - a.cpuPercent);
+ const deltaB = Math.abs(b.memoryPercent - b.cpuPercent);
+ return deltaA - deltaB;
+ });
+ }
+
+ @action
+ async reloadNode() {
+ if (this.args.node.isPartial) {
+ await this.args.node.reload();
+ this.data = this.computeData(this.dimensionsWidth);
+ }
+ }
+
+ @action
+ render(svg) {
+ this.dimensionsWidth = svg.clientWidth - this.padding - this.paddingLeft;
+ this.data = this.computeData(this.dimensionsWidth);
+ }
+
+ @action
+ updateRender(svg) {
+ // Only update all data when the width changes
+ const newWidth = svg.clientWidth - this.padding - this.paddingLeft;
+ if (newWidth !== this.dimensionsWidth) {
+ this.dimensionsWidth = newWidth;
+ this.data = this.computeData(this.dimensionsWidth);
+ }
+ }
+
+ @action
+ highlightAllocation(allocation) {
+ this.activeAllocation = allocation;
+ }
+
+ @action
+ clearHighlight() {
+ this.activeAllocation = null;
+ }
+
+ @action
+ selectNode() {
+ if (this.args.isDense && this.args.onNodeSelect) {
+ this.args.onNodeSelect(this.args.node.isSelected ? null : this.args.node);
+ }
+ }
+
+ @action
+ selectAllocation(allocation) {
+ if (this.args.onAllocationSelect) this.args.onAllocationSelect(allocation);
+ }
+
+ containsActiveTaskGroup() {
+ return this.args.node.allocations.some(
+ allocation =>
+ allocation.taskGroupName === this.args.activeTaskGroup &&
+ allocation.belongsTo('job').id() === this.args.activeJobId
+ );
+ }
+
+ computeData(width) {
+ const allocations = this.allocations;
+ let cpuOffset = 0;
+ let memoryOffset = 0;
+
+ const cpu = [];
+ const memory = [];
+ for (const allocation of allocations) {
+ const { cpuPercent, memoryPercent, isSelected } = allocation;
+ const isFirst = allocation === allocations[0];
+
+ let cpuWidth = cpuPercent * width - 1;
+ let memoryWidth = memoryPercent * width - 1;
+ if (isFirst) {
+ cpuWidth += 0.5;
+ memoryWidth += 0.5;
+ }
+ if (isSelected) {
+ cpuWidth--;
+ memoryWidth--;
+ }
+
+ cpu.push({
+ allocation,
+ offset: cpuOffset * 100,
+ percent: cpuPercent * 100,
+ width: cpuWidth,
+ x: cpuOffset * width + (isFirst ? 0 : 0.5) + (isSelected ? 0.5 : 0),
+ className: allocation.allocation.clientStatus,
+ });
+ memory.push({
+ allocation,
+ offset: memoryOffset * 100,
+ percent: memoryPercent * 100,
+ width: memoryWidth,
+ x: memoryOffset * width + (isFirst ? 0 : 0.5) + (isSelected ? 0.5 : 0),
+ className: allocation.allocation.clientStatus,
+ });
+
+ cpuOffset += cpuPercent;
+ memoryOffset += memoryPercent;
+ }
+
+ const cpuRemainder = {
+ x: cpuOffset * width + 0.5,
+ width: width - cpuOffset * width,
+ };
+ const memoryRemainder = {
+ x: memoryOffset * width + 0.5,
+ width: width - memoryOffset * width,
+ };
+
+ return {
+ cpu,
+ memory,
+ cpuRemainder,
+ memoryRemainder,
+ cpuLabel: { x: -this.paddingLeft / 2, y: this.height / 2 + this.yOffset },
+ memoryLabel: { x: -this.paddingLeft / 2, y: this.height / 2 },
+ };
+ }
+}
diff --git a/ui/app/controllers/topology.js b/ui/app/controllers/topology.js
new file mode 100644
index 00000000000..b32ce4bcaa7
--- /dev/null
+++ b/ui/app/controllers/topology.js
@@ -0,0 +1,114 @@
+import Controller from '@ember/controller';
+import { computed, action } from '@ember/object';
+import classic from 'ember-classic-decorator';
+import { reduceToLargestUnit } from 'nomad-ui/helpers/format-bytes';
+
+const sumAggregator = (sum, value) => sum + (value || 0);
+
+@classic
+export default class TopologyControllers extends Controller {
+ @computed('model.nodes.@each.datacenter')
+ get datacenters() {
+ return Array.from(new Set(this.model.nodes.mapBy('datacenter'))).compact();
+ }
+
+ @computed('model.allocations.@each.isScheduled')
+ get scheduledAllocations() {
+ return this.model.allocations.filterBy('isScheduled');
+ }
+
+ @computed('model.nodes.@each.resources')
+ get totalMemory() {
+ const mibs = this.model.nodes.mapBy('resources.memory').reduce(sumAggregator, 0);
+ return mibs * 1024 * 1024;
+ }
+
+ @computed('model.nodes.@each.resources')
+ get totalCPU() {
+ return this.model.nodes.mapBy('resources.cpu').reduce((sum, cpu) => sum + (cpu || 0), 0);
+ }
+
+ @computed('totalMemory')
+ get totalMemoryFormatted() {
+ return reduceToLargestUnit(this.totalMemory)[0].toFixed(2);
+ }
+
+ @computed('totalCPU')
+ get totalMemoryUnits() {
+ return reduceToLargestUnit(this.totalMemory)[1];
+ }
+
+ @computed('model.allocations.@each.allocatedResources')
+ get totalReservedMemory() {
+ const mibs = this.model.allocations.mapBy('allocatedResources.memory').reduce(sumAggregator, 0);
+ return mibs * 1024 * 1024;
+ }
+
+ @computed('model.allocations.@each.allocatedResources')
+ get totalReservedCPU() {
+ return this.model.allocations.mapBy('allocatedResources.cpu').reduce(sumAggregator, 0);
+ }
+
+ @computed('totalMemory', 'totalReservedMemory')
+ get reservedMemoryPercent() {
+ if (!this.totalReservedMemory || !this.totalMemory) return 0;
+ return this.totalReservedMemory / this.totalMemory;
+ }
+
+ @computed('totalCPU', 'totalReservedCPU')
+ get reservedCPUPercent() {
+ if (!this.totalReservedCPU || !this.totalCPU) return 0;
+ return this.totalReservedCPU / this.totalCPU;
+ }
+
+ @computed('activeAllocation', 'model.allocations.@each.{taskGroupName,job}')
+ get siblingAllocations() {
+ if (!this.activeAllocation) return [];
+ const taskGroup = this.activeAllocation.taskGroupName;
+ const jobId = this.activeAllocation.belongsTo('job').id();
+
+ return this.model.allocations.filter(allocation => {
+ return allocation.taskGroupName === taskGroup && allocation.belongsTo('job').id() === jobId;
+ });
+ }
+
+ @computed('activeNode')
+ get nodeUtilization() {
+ const node = this.activeNode;
+ const [formattedMemory, memoryUnits] = reduceToLargestUnit(node.memory * 1024 * 1024);
+ const totalReservedMemory = node.allocations.mapBy('memory').reduce(sumAggregator, 0);
+ const totalReservedCPU = node.allocations.mapBy('cpu').reduce(sumAggregator, 0);
+
+ return {
+ totalMemoryFormatted: formattedMemory.toFixed(2),
+ totalMemoryUnits: memoryUnits,
+
+ totalMemory: node.memory * 1024 * 1024,
+ totalReservedMemory: totalReservedMemory * 1024 * 1024,
+ reservedMemoryPercent: totalReservedMemory / node.memory,
+
+ totalCPU: node.cpu,
+ totalReservedCPU,
+ reservedCPUPercent: totalReservedCPU / node.cpu,
+ };
+ }
+
+ @computed('siblingAllocations.@each.node')
+ get uniqueActiveAllocationNodes() {
+ return this.siblingAllocations.mapBy('node').uniq();
+ }
+
+ @action
+ async setAllocation(allocation) {
+ if (allocation) {
+ await allocation.reload();
+ await allocation.job.reload();
+ }
+ this.set('activeAllocation', allocation);
+ }
+
+ @action
+ setNode(node) {
+ this.set('activeNode', node);
+ }
+}
diff --git a/ui/app/helpers/format-bytes.js b/ui/app/helpers/format-bytes.js
index b2c69ed065d..ba204fbfcbe 100644
--- a/ui/app/helpers/format-bytes.js
+++ b/ui/app/helpers/format-bytes.js
@@ -1,6 +1,6 @@
import Helper from '@ember/component/helper';
-const UNITS = ['Bytes', 'KiB', 'MiB'];
+const UNITS = ['Bytes', 'KiB', 'MiB', 'GiB'];
/**
* Bytes Formatter
@@ -10,7 +10,7 @@ const UNITS = ['Bytes', 'KiB', 'MiB'];
* Outputs the bytes reduced to the largest supported unit size for which
* bytes is larger than one.
*/
-export function formatBytes([bytes]) {
+export function reduceToLargestUnit(bytes) {
bytes || (bytes = 0);
let unitIndex = 0;
while (bytes >= 1024 && unitIndex < UNITS.length - 1) {
@@ -18,7 +18,12 @@ export function formatBytes([bytes]) {
unitIndex++;
}
- return `${Math.floor(bytes)} ${UNITS[unitIndex]}`;
+ return [bytes, UNITS[unitIndex]];
+}
+
+export function formatBytes([bytes]) {
+ const [number, unit] = reduceToLargestUnit(bytes);
+ return `${Math.floor(number)} ${unit}`;
}
export default Helper.helper(formatBytes);
diff --git a/ui/app/models/allocation.js b/ui/app/models/allocation.js
index a9a60d351b0..3aa09fa1c7c 100644
--- a/ui/app/models/allocation.js
+++ b/ui/app/models/allocation.js
@@ -47,6 +47,11 @@ export default class Allocation extends Model {
@equal('clientStatus', 'running') isRunning;
@attr('boolean') isMigrating;
+ @computed('clientStatus')
+ get isScheduled() {
+ return ['pending', 'running', 'failed'].includes(this.clientStatus);
+ }
+
// An allocation model created from any allocation list response will be lacking
// many properties (some of which can always be null). This is an indicator that
// the allocation needs to be reloaded to get the complete allocation state.
diff --git a/ui/app/modifiers/window-resize.js b/ui/app/modifiers/window-resize.js
new file mode 100644
index 00000000000..f70b20976d2
--- /dev/null
+++ b/ui/app/modifiers/window-resize.js
@@ -0,0 +1,10 @@
+import { modifier } from 'ember-modifier';
+
+export default modifier(function windowResize(element, [handler]) {
+ const boundHandler = ev => handler(element, ev);
+ window.addEventListener('resize', boundHandler);
+
+ return () => {
+ window.removeEventListener('resize', boundHandler);
+ };
+});
diff --git a/ui/app/router.js b/ui/app/router.js
index 12c3c3047dc..8a6cf648cd7 100644
--- a/ui/app/router.js
+++ b/ui/app/router.js
@@ -37,6 +37,8 @@ Router.map(function() {
});
});
+ this.route('topology');
+
this.route('csi', function() {
this.route('volumes', function() {
this.route('volume', { path: '/:volume_name' });
diff --git a/ui/app/routes/topology.js b/ui/app/routes/topology.js
new file mode 100644
index 00000000000..84875ceae29
--- /dev/null
+++ b/ui/app/routes/topology.js
@@ -0,0 +1,27 @@
+import { inject as service } from '@ember/service';
+import Route from '@ember/routing/route';
+import WithForbiddenState from 'nomad-ui/mixins/with-forbidden-state';
+import notifyForbidden from 'nomad-ui/utils/notify-forbidden';
+import classic from 'ember-classic-decorator';
+import RSVP from 'rsvp';
+
+@classic
+export default class TopologyRoute extends Route.extend(WithForbiddenState) {
+ @service store;
+ @service system;
+
+ breadcrumbs = [
+ {
+ label: 'Topology',
+ args: ['topology'],
+ },
+ ];
+
+ model() {
+ return RSVP.hash({
+ jobs: this.store.findAll('job'),
+ allocations: this.store.query('allocation', { resources: true }),
+ nodes: this.store.query('node', { resources: true }),
+ }).catch(notifyForbidden(this));
+ }
+}
diff --git a/ui/app/serializers/allocation.js b/ui/app/serializers/allocation.js
index 15cccb6e377..1946bb98111 100644
--- a/ui/app/serializers/allocation.js
+++ b/ui/app/serializers/allocation.js
@@ -9,6 +9,21 @@ const taskGroupFromJob = (job, taskGroupName) => {
return taskGroup ? taskGroup : null;
};
+const merge = tasks => {
+ const mergedResources = {
+ Cpu: { CpuShares: 0 },
+ Memory: { MemoryMB: 0 },
+ Disk: { DiskMB: 0 },
+ };
+
+ return tasks.reduce((resources, task) => {
+ resources.Cpu.CpuShares += (task.Cpu && task.Cpu.CpuShares) || 0;
+ resources.Memory.MemoryMB += (task.Memory && task.Memory.MemoryMB) || 0;
+ resources.Disk.DiskMB += (task.Disk && task.Disk.DiskMB) || 0;
+ return resources;
+ }, mergedResources);
+};
+
@classic
export default class AllocationSerializer extends ApplicationSerializer {
@service system;
@@ -30,7 +45,7 @@ export default class AllocationSerializer extends ApplicationSerializer {
const state = states[key] || {};
const summary = { Name: key };
Object.keys(state).forEach(stateKey => (summary[stateKey] = state[stateKey]));
- summary.Resources = hash.TaskResources && hash.TaskResources[key];
+ summary.Resources = hash.AllocatedResources && hash.AllocatedResources.Tasks[key];
return summary;
});
@@ -57,8 +72,13 @@ export default class AllocationSerializer extends ApplicationSerializer {
hash.PreemptedByAllocationID = hash.PreemptedByAllocation || null;
hash.WasPreempted = !!hash.PreemptedByAllocationID;
- // When present, the resources are nested under AllocatedResources.Shared
- hash.AllocatedResources = hash.AllocatedResources && hash.AllocatedResources.Shared;
+ const shared = hash.AllocatedResources && hash.AllocatedResources.Shared;
+ hash.AllocatedResources =
+ hash.AllocatedResources && merge(Object.values(hash.AllocatedResources.Tasks));
+ if (shared) {
+ hash.AllocatedResources.Ports = shared.Ports;
+ hash.AllocatedResources.Networks = shared.Networks;
+ }
// The Job definition for an allocation is only included in findRecord responses.
hash.AllocationTaskGroup = !hash.Job ? null : taskGroupFromJob(hash.Job, hash.TaskGroup);
diff --git a/ui/app/serializers/node.js b/ui/app/serializers/node.js
index 6a07ba0ac43..a689c40b267 100644
--- a/ui/app/serializers/node.js
+++ b/ui/app/serializers/node.js
@@ -7,6 +7,8 @@ export default class NodeSerializer extends ApplicationSerializer {
attrs = {
isDraining: 'Drain',
httpAddr: 'HTTPAddr',
+ resources: 'NodeResources',
+ reserved: 'ReservedResources',
};
mapToArray = ['Drivers', 'HostVolumes'];
diff --git a/ui/app/serializers/resources.js b/ui/app/serializers/resources.js
index cd642d64319..c82e009962d 100644
--- a/ui/app/serializers/resources.js
+++ b/ui/app/serializers/resources.js
@@ -1,12 +1,21 @@
import ApplicationSerializer from './application';
export default class ResourcesSerializer extends ApplicationSerializer {
- attrs = {
- cpu: 'CPU',
- memory: 'MemoryMB',
- disk: 'DiskMB',
- iops: 'IOPS',
- };
-
- arrayNullOverrides = ['Ports'];
+ arrayNullOverrides = ['Ports', 'Networks'];
+
+ normalize(typeHash, hash) {
+ hash.Cpu = hash.Cpu && hash.Cpu.CpuShares;
+ hash.Memory = hash.Memory && hash.Memory.MemoryMB;
+ hash.Disk = hash.Disk && hash.Disk.DiskMB;
+
+ // Networks for ReservedResources is different than for Resources.
+ // This smooths over the differences, but doesn't actually support
+ // anything in the ReservedResources.Networks object, since we don't
+ // use any of it in the UI.
+ if (!(hash.Networks instanceof Array)) {
+ hash.Networks = [];
+ }
+
+ return super.normalize(...arguments);
+ }
}
diff --git a/ui/app/styles/charts.scss b/ui/app/styles/charts.scss
index bdb259dd269..3d494e09151 100644
--- a/ui/app/styles/charts.scss
+++ b/ui/app/styles/charts.scss
@@ -3,7 +3,9 @@
@import './charts/line-chart';
@import './charts/tooltip';
@import './charts/colors';
-@import './charts/chart-annotation.scss';
+@import './charts/chart-annotation';
+@import './charts/topo-viz';
+@import './charts/topo-viz-node';
.inline-chart {
height: 1.5rem;
diff --git a/ui/app/styles/charts/colors.scss b/ui/app/styles/charts/colors.scss
index 6eedeeaecf4..dce0a3ddf5a 100644
--- a/ui/app/styles/charts/colors.scss
+++ b/ui/app/styles/charts/colors.scss
@@ -47,6 +47,10 @@ $lost: $dark;
vertical-align: middle;
border-radius: $radius;
+ &.is-wide {
+ width: 2rem;
+ }
+
$color-sequence: $orange, $yellow, $green, $turquoise, $blue, $purple, $red;
@for $i from 1 through length($color-sequence) {
&.swatch-#{$i - 1} {
diff --git a/ui/app/styles/charts/topo-viz-node.scss b/ui/app/styles/charts/topo-viz-node.scss
new file mode 100644
index 00000000000..1da8b49400d
--- /dev/null
+++ b/ui/app/styles/charts/topo-viz-node.scss
@@ -0,0 +1,94 @@
+.topo-viz-node {
+ display: block;
+
+ .label {
+ font-weight: $weight-normal;
+ }
+
+ .chart {
+ display: inline-block;
+ height: 100%;
+ width: 100%;
+ overflow: visible;
+
+ .node-background {
+ fill: $white-ter;
+ stroke-width: 1;
+ stroke: $grey-lighter;
+
+ &.is-interactive:hover {
+ fill: $white;
+ stroke: $grey-light;
+ }
+
+ &.is-selected,
+ &.is-selected:hover {
+ fill: $white;
+ stroke: $grey;
+ }
+ }
+
+ .dimension-background {
+ fill: lighten($grey-lighter, 5%);
+ }
+
+ .dimensions.is-active {
+ .bar {
+ opacity: 0.2;
+
+ &.is-active {
+ opacity: 1;
+ }
+ }
+ }
+
+ .bar {
+ cursor: pointer;
+
+ &.is-selected {
+ stroke-width: 1px;
+ stroke: $blue;
+ fill: $blue-light;
+ }
+ }
+
+ .label {
+ text-anchor: middle;
+ alignment-baseline: central;
+ font-weight: $weight-normal;
+ fill: $grey;
+ pointer-events: none;
+ }
+ }
+
+ .empty-text {
+ fill: $red;
+ transform: translate(50%, 50%);
+
+ text {
+ text-anchor: middle;
+ alignment-baseline: central;
+ }
+ }
+
+ & + .topo-viz-node {
+ margin-top: 1em;
+ }
+
+ &.is-empty {
+ .node-background {
+ stroke: $red;
+ stroke-width: 2;
+ fill: $white;
+ }
+
+ .dimension-background {
+ fill: none;
+ }
+ }
+}
+
+.flex-masonry-columns-2 > .flex-masonry-item > .topo-viz-node .chart,
+.flex-masonry-columns-2 > .flex-masonry-item > .topo-viz-node .label {
+ width: calc(100% - 0.75em);
+}
diff --git a/ui/app/styles/charts/topo-viz.scss b/ui/app/styles/charts/topo-viz.scss
new file mode 100644
index 00000000000..bfbccb29dbb
--- /dev/null
+++ b/ui/app/styles/charts/topo-viz.scss
@@ -0,0 +1,35 @@
+.topo-viz {
+ .topo-viz-datacenters {
+ display: flex;
+ flex-direction: column;
+ flex-wrap: wrap;
+ align-content: space-between;
+ margin-top: -0.75em;
+
+ .topo-viz-datacenter {
+ margin-top: 0.75em;
+ margin-bottom: 0.75em;
+ width: calc(50% - 0.75em);
+ }
+ }
+
+ &.is-single-column .topo-viz-datacenter {
+ width: 100%;
+ }
+
+ .topo-viz-edges {
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ left: 0;
+ top: 0;
+ pointer-events: none;
+ overflow: visible;
+
+ .edge {
+ stroke-width: 2;
+ stroke: $blue;
+ fill: none;
+ }
+ }
+}
diff --git a/ui/app/styles/components.scss b/ui/app/styles/components.scss
index 1f1058f3692..a032c11e6d5 100644
--- a/ui/app/styles/components.scss
+++ b/ui/app/styles/components.scss
@@ -4,6 +4,7 @@
@import './components/codemirror';
@import './components/copy-button';
@import './components/cli-window';
+@import './components/dashboard-metric';
@import './components/dropdown';
@import './components/ember-power-select';
@import './components/empty-message';
@@ -11,6 +12,7 @@
@import './components/event';
@import './components/exec-button';
@import './components/exec-window';
+@import './components/flex-masonry';
@import './components/fs-explorer';
@import './components/global-search-container';
@import './components/global-search-dropdown';
@@ -20,6 +22,7 @@
@import './components/inline-definitions';
@import './components/job-diff';
@import './components/json-viewer';
+@import './components/legend';
@import './components/lifecycle-chart';
@import './components/loading-spinner';
@import './components/metrics';
diff --git a/ui/app/styles/components/dashboard-metric.scss b/ui/app/styles/components/dashboard-metric.scss
new file mode 100644
index 00000000000..5b04440a109
--- /dev/null
+++ b/ui/app/styles/components/dashboard-metric.scss
@@ -0,0 +1,50 @@
+.dashboard-metric {
+ &:not(:last-child) {
+ margin-bottom: 1.5em;
+ }
+
+ &.column:not(:last-child) {
+ margin-bottom: 0;
+ }
+
+ .metric {
+ text-align: left;
+ font-weight: $weight-bold;
+ font-size: $size-3;
+
+ .metric-units {
+ font-size: $size-4;
+ }
+
+ .metric-label {
+ font-size: $body-size;
+ font-weight: $weight-normal;
+ }
+ }
+
+ .graphic {
+ padding-bottom: 0;
+ margin-bottom: 0;
+
+ > .column {
+ padding: 0.5rem 0.75rem;
+ }
+ }
+
+ .annotation {
+ margin-top: -0.75rem;
+ }
+
+ &.with-divider {
+ border-top: 1px solid $grey-blue;
+ padding-top: 1.5em;
+ }
+
+ .pair {
+ font-size: $size-5;
+ }
+
+ .is-faded {
+ color: darken($grey-blue, 20%);
+ }
+}
diff --git a/ui/app/styles/components/flex-masonry.scss b/ui/app/styles/components/flex-masonry.scss
new file mode 100644
index 00000000000..74385567235
--- /dev/null
+++ b/ui/app/styles/components/flex-masonry.scss
@@ -0,0 +1,37 @@
+.flex-masonry {
+ display: flex;
+ flex-direction: column;
+ flex-wrap: wrap;
+ align-content: space-between;
+ margin-top: -0.75em;
+
+ &.flex-masonry-columns-1 > .flex-masonry-item {
+ width: 100%;
+ }
+ &.flex-masonry-columns-2 > .flex-masonry-item {
+ width: 50%;
+ }
+ &.flex-masonry-columns-3 > .flex-masonry-item {
+ width: 33%;
+ }
+ &.flex-masonry-columns-4 > .flex-masonry-item {
+ width: 25%;
+ }
+
+ &.with-spacing {
+ > .flex-masonry-item {
+ margin-top: 0.75em;
+ margin-bottom: 0.75em;
+ }
+
+ &.flex-masonry-columns-2 > .flex-masonry-item {
+ width: calc(50% - 0.75em);
+ }
+ &.flex-masonry-columns-3 > .flex-masonry-item {
+ width: calc(33% - 0.75em);
+ }
+ &.flex-masonry-columns-4 > .flex-masonry-item {
+ width: calc(25% - 0.75em);
+ }
+ }
+}
diff --git a/ui/app/styles/components/legend.scss b/ui/app/styles/components/legend.scss
new file mode 100644
index 00000000000..2e86faf97d1
--- /dev/null
+++ b/ui/app/styles/components/legend.scss
@@ -0,0 +1,33 @@
+.legend {
+ margin-bottom: 1em;
+
+ .legend-label {
+ font-weight: $weight-bold;
+ margin-bottom: 0.3em;
+ }
+
+ .legend-terms {
+ dt,
+ dd {
+ display: inline;
+ }
+
+ dt {
+ font-weight: $weight-bold;
+ }
+
+ dd {
+ margin-left: 0.5em;
+ }
+
+ .legend-term {
+ display: inline-block;
+ whitespace: nowrap;
+ margin-right: 1.5em;
+
+ &:last-child {
+ margin-right: 0;
+ }
+ }
+ }
+}
diff --git a/ui/app/styles/components/primary-metric.scss b/ui/app/styles/components/primary-metric.scss
index e48b52d16e3..ba3777767b0 100644
--- a/ui/app/styles/components/primary-metric.scss
+++ b/ui/app/styles/components/primary-metric.scss
@@ -13,6 +13,10 @@
height: 150px;
}
+ &.is-short .primary-graphic {
+ height: 100px;
+ }
+
.secondary-graphic {
padding: 0.75em;
padding-bottom: 0;
diff --git a/ui/app/styles/core/columns.scss b/ui/app/styles/core/columns.scss
index d912a37c31c..f4cdd5e62cc 100644
--- a/ui/app/styles/core/columns.scss
+++ b/ui/app/styles/core/columns.scss
@@ -23,4 +23,8 @@
margin-left: auto;
margin-right: auto;
}
+
+ &.is-flush {
+ margin-bottom: 0;
+ }
}
diff --git a/ui/app/styles/core/typography.scss b/ui/app/styles/core/typography.scss
index 546320e6730..9294c5f1337 100644
--- a/ui/app/styles/core/typography.scss
+++ b/ui/app/styles/core/typography.scss
@@ -23,3 +23,7 @@ code {
.is-interactive {
cursor: pointer;
}
+
+.is-faded {
+ color: darken($grey-blue, 20%);
+}
diff --git a/ui/app/styles/core/variables.scss b/ui/app/styles/core/variables.scss
index cee22b48fe4..24832cf1a19 100644
--- a/ui/app/styles/core/variables.scss
+++ b/ui/app/styles/core/variables.scss
@@ -4,6 +4,7 @@ $blue: $vagrant-blue;
$purple: $terraform-purple;
$red: #c84034;
$grey-blue: #bbc4d1;
+$blue-light: #c0d5ff;
$primary: $nomad-green;
$warning: $orange;
diff --git a/ui/app/templates/components/flex-masonry.hbs b/ui/app/templates/components/flex-masonry.hbs
new file mode 100644
index 00000000000..81777bf7bdc
--- /dev/null
+++ b/ui/app/templates/components/flex-masonry.hbs
@@ -0,0 +1,13 @@
+
+ {{#each @items as |item|}}
+
+ {{yield item (action this.reflow)}}
+
+ {{/each}}
+
diff --git a/ui/app/templates/components/gutter-menu.hbs b/ui/app/templates/components/gutter-menu.hbs
index eb788f0b960..9d0608d3532 100644
--- a/ui/app/templates/components/gutter-menu.hbs
+++ b/ui/app/templates/components/gutter-menu.hbs
@@ -81,6 +81,7 @@
diff --git a/ui/app/templates/components/topo-viz.hbs b/ui/app/templates/components/topo-viz.hbs
new file mode 100644
index 00000000000..63d123417d8
--- /dev/null
+++ b/ui/app/templates/components/topo-viz.hbs
@@ -0,0 +1,24 @@
+
+
+
+
+
+ {{#if this.activeAllocation}}
+
+ {{/if}}
+
diff --git a/ui/app/templates/components/topo-viz/datacenter.hbs b/ui/app/templates/components/topo-viz/datacenter.hbs
new file mode 100644
index 00000000000..0a50a1f5a8d
--- /dev/null
+++ b/ui/app/templates/components/topo-viz/datacenter.hbs
@@ -0,0 +1,19 @@
+
+
+ {{@datacenter.name}}
+ {{this.scheduledAllocations.length}} Allocs
+ {{@datacenter.nodes.length}} Nodes
+ {{this.aggregatedAllocationResources.memory}}/{{this.aggregatedNodeResources.memory}} MiB,
+ {{this.aggregatedAllocationResources.cpu}}/{{this.aggregatedNodeResources.cpu}} Mhz
+
+
+
+
+
+
+
diff --git a/ui/app/templates/components/topo-viz/node.hbs b/ui/app/templates/components/topo-viz/node.hbs
new file mode 100644
index 00000000000..093eaaa1a3c
--- /dev/null
+++ b/ui/app/templates/components/topo-viz/node.hbs
@@ -0,0 +1,116 @@
+
+ {{#unless @isDense}}
+
+ {{#if @node.node.isDraining}}
+ {{x-icon "clock-outline" class="is-info"}}
+ {{else if (not @node.node.isEligible)}}
+ {{x-icon "lock-closed" class="is-warning"}}
+ {{/if}}
+ {{@node.node.name}}
+ {{this.count}} Allocs
+ {{@node.memory}} MiB, {{@node.cpu}} Mhz
+
+ {{/unless}}
+
+
+
diff --git a/ui/app/templates/topology.hbs b/ui/app/templates/topology.hbs
new file mode 100644
index 00000000000..19c1fd515dc
--- /dev/null
+++ b/ui/app/templates/topology.hbs
@@ -0,0 +1,204 @@
+{{title "Cluster Topology"}}
+
+
+
+
+
+
Legend
+
+
+
Metrics
+
+ - M:
- Memory
+ - C:
- CPU
+
+
+
+
Allocation Status
+
+ - Running
+ - Failed
+ - Starting
+
+
+
+
+
+
+ {{#if this.activeNode}}Client{{else if this.activeAllocation}}Allocation{{else}}Cluster{{/if}} Details
+
+
+ {{#if this.activeNode}}
+ {{#let this.activeNode.node as |node|}}
+
+
{{this.activeNode.allocations.length}} Allocations
+
+
+
+ Client:
+
+ {{node.shortId}}
+
+
+
Name: {{node.name}}
+
Address: {{node.httpAddr}}
+
Status: {{node.status}}
+
+
+
+ Draining? {{if node.isDraining "Yes" "No"}}
+
+
+ Eligible? {{if node.isEligible "Yes" "No"}}
+
+
+
+
+ {{this.nodeUtilization.totalMemoryFormatted}}
+ {{this.nodeUtilization.totalMemoryUnits}}
+ of memory
+
+
+
+
+
+
+
+
+ {{format-percentage this.nodeUtilization.reservedMemoryPercent total=1}}
+
+
+
+ {{format-bytes this.nodeUtilization.totalReservedMemory}} / {{format-bytes this.nodeUtilization.totalMemory}} reserved
+
+
+
+
{{this.nodeUtilization.totalCPU}} Mhz of CPU
+
+
+
+
+
+
+
+ {{format-percentage this.nodeUtilization.reservedCPUPercent total=1}}
+
+
+
+ {{this.nodeUtilization.totalReservedCPU}} Mhz / {{this.nodeUtilization.totalCPU}} Mhz reserved
+
+
+ {{/let}}
+ {{else if this.activeAllocation}}
+
+
+ Allocation:
+ {{this.activeAllocation.shortId}}
+
+
Sibling Allocations: {{this.siblingAllocations.length}}
+
Unique Client Placements: {{this.uniqueActiveAllocationNodes.length}}
+
+
+
+ Job:
+
+ {{this.activeAllocation.job.name}}
+ / {{this.activeAllocation.taskGroupName}}
+
+
Type: {{this.activeAllocation.job.type}}
+
Priority: {{this.activeAllocation.job.priority}}
+
+
+
+ Client:
+
+ {{this.activeAllocation.node.shortId}}
+
+
+
Name: {{this.activeAllocation.node.name}}
+
Address: {{this.activeAllocation.node.httpAddr}}
+
+
+
+ {{else}}
+
+
+
{{this.model.nodes.length}} Clients
+
+
+
{{this.scheduledAllocations.length}} Allocations
+
+
+
+
{{this.totalMemoryFormatted}} {{this.totalMemoryUnits}} of memory
+
+
+
+
+
+
+
+ {{format-percentage this.reservedMemoryPercent total=1}}
+
+
+
+ {{format-bytes this.totalReservedMemory}} / {{format-bytes this.totalMemory}} reserved
+
+
+
+
{{this.totalCPU}} Mhz of CPU
+
+
+
+
+
+
+
+ {{format-percentage this.reservedCPUPercent total=1}}
+
+
+
+ {{this.totalReservedCPU}} Mhz / {{this.totalCPU}} Mhz reserved
+
+
+ {{/if}}
+
+
+
+
+
+
+
+
+
diff --git a/ui/config/environment.js b/ui/config/environment.js
index 9d758e0a62c..e459f26241f 100644
--- a/ui/config/environment.js
+++ b/ui/config/environment.js
@@ -25,8 +25,8 @@ module.exports = function(environment) {
APP: {
blockingQueries: true,
- mirageScenario: 'smallCluster',
- mirageWithNamespaces: true,
+ mirageScenario: 'topoMedium',
+ mirageWithNamespaces: false,
mirageWithTokens: true,
mirageWithRegions: true,
},
diff --git a/ui/mirage/common.js b/ui/mirage/common.js
index acfd6069d1d..cac53cdb0ac 100644
--- a/ui/mirage/common.js
+++ b/ui/mirage/common.js
@@ -5,10 +5,8 @@ import { provide } from './utils';
const CPU_RESERVATIONS = [250, 500, 1000, 2000, 2500, 4000];
const MEMORY_RESERVATIONS = [256, 512, 1024, 2048, 4096, 8192];
const DISK_RESERVATIONS = [200, 500, 1000, 2000, 5000, 10000, 100000];
-const IOPS_RESERVATIONS = [100000, 250000, 500000, 1000000, 10000000, 20000000];
// There is also a good chance that certain resource restrictions are unbounded
-IOPS_RESERVATIONS.push(...Array(1000).fill(0));
DISK_RESERVATIONS.push(...Array(500).fill(0));
const NETWORK_MODES = ['bridge', 'host'];
@@ -27,10 +25,15 @@ export const STORAGE_PROVIDERS = ['ebs', 'zfs', 'nfs', 'cow', 'moo'];
export function generateResources(options = {}) {
return {
- CPU: options.CPU || faker.helpers.randomize(CPU_RESERVATIONS),
- MemoryMB: options.MemoryMB || faker.helpers.randomize(MEMORY_RESERVATIONS),
- DiskMB: options.DiskMB || faker.helpers.randomize(DISK_RESERVATIONS),
- IOPS: options.IOPS || faker.helpers.randomize(IOPS_RESERVATIONS),
+ Cpu: {
+ CpuShares: options.CPU || faker.helpers.randomize(CPU_RESERVATIONS),
+ },
+ Memory: {
+ MemoryMB: options.MemoryMB || faker.helpers.randomize(MEMORY_RESERVATIONS),
+ },
+ Disk: {
+ DiskMB: options.DiskMB || faker.helpers.randomize(DISK_RESERVATIONS),
+ },
Networks: generateNetworks(options.networks),
Ports: generatePorts(options.networks),
};
diff --git a/ui/mirage/factories/allocation.js b/ui/mirage/factories/allocation.js
index c571271d039..d433340bc58 100644
--- a/ui/mirage/factories/allocation.js
+++ b/ui/mirage/factories/allocation.js
@@ -42,15 +42,16 @@ export default Factory.extend({
const taskGroup = server.db.taskGroups.findBy({ name: allocation.taskGroup });
const resources = taskGroup.taskIds.map(id => {
const task = server.db.tasks.find(id);
- return server.create(
- 'task-resource',
- {
- allocation,
- name: task.name,
- resources: task.Resources,
- },
- 'withReservedPorts'
- );
+ return server.create('task-resource', {
+ allocation,
+ name: task.name,
+ resources: generateResources({
+ CPU: task.resources.CPU,
+ MemoryMB: task.resources.MemoryMB,
+ DiskMB: task.resources.DiskMB,
+ networks: { minPorts: 1 },
+ }),
+ });
});
allocation.update({ taskResourceIds: resources.mapBy('id') });
@@ -62,29 +63,22 @@ export default Factory.extend({
const taskGroup = server.db.taskGroups.findBy({ name: allocation.taskGroup });
const resources = taskGroup.taskIds.map(id => {
const task = server.db.tasks.find(id);
- return server.create(
- 'task-resource',
- {
- allocation,
- name: task.name,
- resources: task.Resources,
- },
- 'withoutReservedPorts'
- );
+ return server.create('task-resource', {
+ allocation,
+ name: task.name,
+ resources: generateResources({
+ CPU: task.resources.CPU,
+ MemoryMB: task.resources.MemoryMB,
+ DiskMB: task.resources.DiskMB,
+ networks: { minPorts: 0, maxPorts: 0 },
+ }),
+ });
});
allocation.update({ taskResourceIds: resources.mapBy('id') });
},
}),
- withAllocatedResources: trait({
- allocatedResources: () => {
- return {
- Shared: generateResources({ networks: { minPorts: 2 } }),
- };
- },
- }),
-
rescheduleAttempts: 0,
rescheduleSuccess: false,
@@ -200,13 +194,13 @@ export default Factory.extend({
return server.create('task-resource', {
allocation,
name: task.name,
- resources: task.Resources,
+ resources: task.originalResources,
});
});
allocation.update({
taskStateIds: allocation.clientStatus === 'pending' ? [] : states.mapBy('id'),
- taskResourceIds: allocation.clientStatus === 'pending' ? [] : resources.mapBy('id'),
+ taskResourceIds: resources.mapBy('id'),
});
// Each allocation has a corresponding allocation stats running on some client.
diff --git a/ui/mirage/factories/node.js b/ui/mirage/factories/node.js
index 03d6427d631..151536e1812 100644
--- a/ui/mirage/factories/node.js
+++ b/ui/mirage/factories/node.js
@@ -74,7 +74,7 @@ export default Factory.extend({
hostVolumes: makeHostVolumes,
- resources: generateResources,
+ nodeResources: generateResources,
attributes() {
// TODO add variability to these
diff --git a/ui/mirage/factories/task-group.js b/ui/mirage/factories/task-group.js
index 8e3cbc9ac8b..73ecf36f6f1 100644
--- a/ui/mirage/factories/task-group.js
+++ b/ui/mirage/factories/task-group.js
@@ -79,7 +79,7 @@ export default Factory.extend({
const maybeResources = {};
if (resources) {
- maybeResources.Resources = generateResources(resources[idx]);
+ maybeResources.originalResources = generateResources(resources[idx]);
}
return server.create('task', {
taskGroup: group,
diff --git a/ui/mirage/factories/task-resource.js b/ui/mirage/factories/task-resource.js
index 782988bcda6..708cc761a31 100644
--- a/ui/mirage/factories/task-resource.js
+++ b/ui/mirage/factories/task-resource.js
@@ -5,12 +5,4 @@ export default Factory.extend({
name: () => '!!!this should be set by the allocation that owns this task state!!!',
resources: generateResources,
-
- withReservedPorts: trait({
- resources: () => generateResources({ networks: { minPorts: 1 } }),
- }),
-
- withoutReservedPorts: trait({
- resources: () => generateResources({ networks: { minPorts: 0, maxPorts: 0 } }),
- }),
});
diff --git a/ui/mirage/factories/task.js b/ui/mirage/factories/task.js
index 6b6759c0795..ab3ee6359cd 100644
--- a/ui/mirage/factories/task.js
+++ b/ui/mirage/factories/task.js
@@ -16,7 +16,17 @@ export default Factory.extend({
name: id => `task-${faker.hacker.noun().dasherize()}-${id}`,
driver: () => faker.helpers.randomize(DRIVERS),
- Resources: generateResources,
+ originalResources: generateResources,
+ resources: function() {
+ // Generate resources the usual way, but transform to the old
+ // shape because that's what the job spec uses.
+ const resources = this.originalResources;
+ return {
+ CPU: resources.Cpu.CpuShares,
+ MemoryMB: resources.Memory.MemoryMB,
+ DiskMB: resources.Disk.DiskMB,
+ };
+ },
Lifecycle: i => {
const cycle = i % 5;
diff --git a/ui/mirage/scenarios/default.js b/ui/mirage/scenarios/default.js
index fadd4f7343a..c30dc446ad6 100644
--- a/ui/mirage/scenarios/default.js
+++ b/ui/mirage/scenarios/default.js
@@ -1,4 +1,5 @@
import config from 'nomad-ui/config/environment';
+import * as topoScenarios from './topo';
import { pickOne } from '../utils';
const withNamespaces = getConfigValue('mirageWithNamespaces', false);
@@ -14,6 +15,7 @@ const allScenarios = {
allNodeTypes,
everyFeature,
emptyCluster,
+ ...topoScenarios,
};
const scenario = getConfigValue('mirageScenario', 'emptyCluster');
diff --git a/ui/mirage/scenarios/topo.js b/ui/mirage/scenarios/topo.js
new file mode 100644
index 00000000000..d38d28df523
--- /dev/null
+++ b/ui/mirage/scenarios/topo.js
@@ -0,0 +1,109 @@
+import faker from 'nomad-ui/mirage/faker';
+import { generateNetworks, generatePorts } from '../common';
+
+const genResources = (CPU, Memory) => ({
+ Cpu: { CpuShares: CPU },
+ Memory: { MemoryMB: Memory },
+ Disk: { DiskMB: 10000 },
+ Networks: generateNetworks(),
+ Ports: generatePorts(),
+});
+
+export function topoSmall(server) {
+ server.createList('agent', 3);
+ server.createList('node', 12, {
+ datacenter: 'dc1',
+ status: 'ready',
+ nodeResources: genResources(3000, 5192),
+ });
+
+ const jobResources = [
+ ['M: 2560, C: 150'],
+ ['M: 128, C: 400'],
+ ['M: 512, C: 100'],
+ ['M: 256, C: 150'],
+ ['M: 200, C: 50'],
+ ['M: 64, C: 100'],
+ ['M: 128, C: 150'],
+ ['M: 1024, C: 500'],
+ ['M: 100, C: 300', 'M: 200, C: 150'],
+ ['M: 512, C: 250', 'M: 600, C: 200'],
+ ];
+
+ jobResources.forEach(spec => {
+ server.create('job', {
+ status: 'running',
+ datacenters: ['dc1'],
+ type: 'service',
+ createAllocations: false,
+ resourceSpec: spec,
+ });
+ });
+
+ server.createList('allocation', 25, {
+ forceRunningClientStatus: true,
+ });
+}
+
+export function topoMedium(server) {
+ server.createList('agent', 3);
+ server.createList('node', 10, {
+ datacenter: 'us-west-1',
+ status: 'ready',
+ nodeResources: genResources(3000, 5192),
+ });
+ server.createList('node', 12, {
+ datacenter: 'us-east-1',
+ status: 'ready',
+ nodeResources: genResources(3000, 5192),
+ });
+ server.createList('node', 11, {
+ datacenter: 'eu-west-1',
+ status: 'ready',
+ nodeResources: genResources(3000, 5192),
+ });
+
+ server.createList('node', 8, {
+ datacenter: 'us-west-1',
+ status: 'ready',
+ nodeResources: genResources(8000, 12192),
+ });
+ server.createList('node', 9, {
+ datacenter: 'us-east-1',
+ status: 'ready',
+ nodeResources: genResources(8000, 12192),
+ });
+
+ const jobResources = [
+ ['M: 2560, C: 150'],
+ ['M: 128, C: 400'],
+ ['M: 512, C: 100'],
+ ['M: 256, C: 150'],
+ ['M: 200, C: 50'],
+ ['M: 64, C: 100'],
+ ['M: 128, C: 150'],
+ ['M: 1024, C: 500'],
+
+ ['M: 1200, C: 50'],
+ ['M: 1400, C: 200'],
+ ['M: 50, C: 150'],
+ ['M: 5000, C: 1800'],
+
+ ['M: 100, C: 300', 'M: 200, C: 150'],
+ ['M: 512, C: 250', 'M: 600, C: 200'],
+ ];
+
+ jobResources.forEach(spec => {
+ server.create('job', {
+ status: 'running',
+ datacenters: ['dc1'],
+ type: 'service',
+ createAllocations: false,
+ resourceSpec: spec,
+ });
+ });
+
+ server.createList('allocation', 100, {
+ forceRunningClientStatus: true,
+ });
+}
diff --git a/ui/mirage/serializers/allocation.js b/ui/mirage/serializers/allocation.js
index eeaaf28f6f4..d2b58d4bb48 100644
--- a/ui/mirage/serializers/allocation.js
+++ b/ui/mirage/serializers/allocation.js
@@ -18,14 +18,14 @@ export default ApplicationSerializer.extend({
function serializeAllocation(allocation) {
allocation.TaskStates = allocation.TaskStates.reduce(arrToObj('Name'), {});
- allocation.Resources = allocation.TaskResources.mapBy('Resources').reduce(
- (hash, resources) => {
- ['CPU', 'DiskMB', 'IOPS', 'MemoryMB'].forEach(key => (hash[key] += resources[key]));
- hash.Networks = resources.Networks;
- hash.Ports = resources.Ports;
- return hash;
- },
- { CPU: 0, DiskMB: 0, IOPS: 0, MemoryMB: 0 }
- );
- allocation.TaskResources = allocation.TaskResources.reduce(arrToObj('Name', 'Resources'), {});
+ const { Ports, Networks } = allocation.TaskResources[0]
+ ? allocation.TaskResources[0].Resources
+ : {};
+ allocation.AllocatedResources = {
+ Shared: { Ports, Networks },
+ Tasks: allocation.TaskResources.map(({ Name, Resources }) => ({ Name, ...Resources })).reduce(
+ arrToObj('Name'),
+ {}
+ ),
+ };
}
diff --git a/ui/package.json b/ui/package.json
index 35a3ca20a72..cd1b4b29215 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -43,7 +43,7 @@
"broccoli-asset-rev": "^3.0.0",
"bulma": "0.6.1",
"core-js": "^2.4.1",
- "d3-array": "^1.2.0",
+ "d3-array": "^2.1.0",
"d3-axis": "^1.0.0",
"d3-format": "^1.3.0",
"d3-scale": "^1.0.0",
@@ -84,6 +84,7 @@
"ember-inline-svg": "^0.3.0",
"ember-load-initializers": "^2.1.1",
"ember-maybe-import-regenerator": "^0.1.6",
+ "ember-modifier": "^2.1.0",
"ember-moment": "^7.8.1",
"ember-overridable-computed": "^1.0.0",
"ember-page-title": "^5.0.2",
diff --git a/ui/tests/acceptance/allocation-detail-test.js b/ui/tests/acceptance/allocation-detail-test.js
index d4788d88e1d..83fa2389809 100644
--- a/ui/tests/acceptance/allocation-detail-test.js
+++ b/ui/tests/acceptance/allocation-detail-test.js
@@ -26,7 +26,7 @@ module('Acceptance | allocation detail', function(hooks) {
withGroupServices: true,
createAllocations: false,
});
- allocation = server.create('allocation', 'withTaskWithPorts', 'withAllocatedResources', {
+ allocation = server.create('allocation', 'withTaskWithPorts', {
clientStatus: 'running',
});
@@ -87,7 +87,7 @@ module('Acceptance | allocation detail', function(hooks) {
createAllocations: false,
});
- const allocation = server.create('allocation', 'withTaskWithPorts', 'withAllocatedResources', {
+ const allocation = server.create('allocation', 'withTaskWithPorts', {
clientStatus: 'running',
jobId: job.id,
});
@@ -188,7 +188,7 @@ module('Acceptance | allocation detail', function(hooks) {
createAllocations: false,
});
- allocation = server.create('allocation', 'withTaskWithPorts', 'withAllocatedResources', {
+ allocation = server.create('allocation', 'withTaskWithPorts', {
clientStatus: 'running',
jobId: job.id,
});
@@ -216,7 +216,7 @@ module('Acceptance | allocation detail', function(hooks) {
});
test('ports are listed', async function(assert) {
- const allServerPorts = allocation.allocatedResources.Shared.Ports;
+ const allServerPorts = allocation.taskResources.models[0].resources.Ports;
allServerPorts.sortBy('Label').forEach((serverPort, index) => {
const renderedPort = Allocation.ports[index];
diff --git a/ui/tests/acceptance/client-detail-test.js b/ui/tests/acceptance/client-detail-test.js
index 9e95141f169..1ce46d80de4 100644
--- a/ui/tests/acceptance/client-detail-test.js
+++ b/ui/tests/acceptance/client-detail-test.js
@@ -134,8 +134,8 @@ module('Acceptance | client detail', function(hooks) {
});
const tasks = taskGroup.taskIds.map(id => server.db.tasks.find(id));
- const cpuUsed = tasks.reduce((sum, task) => sum + task.Resources.CPU, 0);
- const memoryUsed = tasks.reduce((sum, task) => sum + task.Resources.MemoryMB, 0);
+ const cpuUsed = tasks.reduce((sum, task) => sum + task.resources.CPU, 0);
+ const memoryUsed = tasks.reduce((sum, task) => sum + task.resources.MemoryMB, 0);
await ClientDetail.visit({ id: node.id });
diff --git a/ui/tests/acceptance/plugin-detail-test.js b/ui/tests/acceptance/plugin-detail-test.js
index 0f3db7cccf0..13eb29b0410 100644
--- a/ui/tests/acceptance/plugin-detail-test.js
+++ b/ui/tests/acceptance/plugin-detail-test.js
@@ -94,8 +94,8 @@ module('Acceptance | plugin detail', function(hooks) {
});
const tasks = taskGroup.taskIds.map(id => server.db.tasks.find(id));
- const cpuUsed = tasks.reduce((sum, task) => sum + task.Resources.CPU, 0);
- const memoryUsed = tasks.reduce((sum, task) => sum + task.Resources.MemoryMB, 0);
+ const cpuUsed = tasks.reduce((sum, task) => sum + task.resources.CPU, 0);
+ const memoryUsed = tasks.reduce((sum, task) => sum + task.resources.MemoryMB, 0);
await PluginDetail.visit({ id: plugin.id });
diff --git a/ui/tests/acceptance/task-group-detail-test.js b/ui/tests/acceptance/task-group-detail-test.js
index a38508f203e..af4f240e1ff 100644
--- a/ui/tests/acceptance/task-group-detail-test.js
+++ b/ui/tests/acceptance/task-group-detail-test.js
@@ -74,8 +74,8 @@ module('Acceptance | task group detail', function(hooks) {
});
test('/jobs/:id/:task-group should list high-level metrics for the allocation', async function(assert) {
- const totalCPU = tasks.mapBy('Resources.CPU').reduce(sum, 0);
- const totalMemory = tasks.mapBy('Resources.MemoryMB').reduce(sum, 0);
+ const totalCPU = tasks.mapBy('resources.CPU').reduce(sum, 0);
+ const totalMemory = tasks.mapBy('resources.MemoryMB').reduce(sum, 0);
const totalDisk = taskGroup.ephemeralDisk.SizeMB;
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
@@ -199,8 +199,8 @@ module('Acceptance | task group detail', function(hooks) {
const allocStats = server.db.clientAllocationStats.find(allocation.id);
const tasks = taskGroup.taskIds.map(id => server.db.tasks.find(id));
- const cpuUsed = tasks.reduce((sum, task) => sum + task.Resources.CPU, 0);
- const memoryUsed = tasks.reduce((sum, task) => sum + task.Resources.MemoryMB, 0);
+ const cpuUsed = tasks.reduce((sum, task) => sum + task.resources.CPU, 0);
+ const memoryUsed = tasks.reduce((sum, task) => sum + task.resources.MemoryMB, 0);
assert.equal(
allocationRow.cpu,
diff --git a/ui/tests/acceptance/topology-test.js b/ui/tests/acceptance/topology-test.js
new file mode 100644
index 00000000000..79ca675a8c7
--- /dev/null
+++ b/ui/tests/acceptance/topology-test.js
@@ -0,0 +1,53 @@
+import { module, test } from 'qunit';
+import { setupApplicationTest } from 'ember-qunit';
+import { setupMirage } from 'ember-cli-mirage/test-support';
+import a11yAudit from 'nomad-ui/tests/helpers/a11y-audit';
+import Topology from 'nomad-ui/tests/pages/topology';
+
+// TODO: Once we settle on the contents of the info panel, the contents
+// should also get acceptance tests.
+module('Acceptance | topology', function(hooks) {
+ setupApplicationTest(hooks);
+ setupMirage(hooks);
+
+ hooks.beforeEach(function() {
+ server.create('job', { createAllocations: false });
+ });
+
+ test('it passes an accessibility audit', async function(assert) {
+ server.createList('node', 3);
+ server.createList('allocation', 5);
+
+ await Topology.visit();
+ await a11yAudit(assert);
+ });
+
+ test('by default the info panel shows cluster aggregate stats', async function(assert) {
+ server.createList('node', 3);
+ server.createList('allocation', 5);
+
+ await Topology.visit();
+ assert.equal(Topology.infoPanelTitle, 'Cluster Details');
+ });
+
+ test('when an allocation is selected, the info panel shows information on the allocation', async function(assert) {
+ server.createList('node', 1);
+ server.createList('allocation', 5);
+
+ await Topology.visit();
+
+ await Topology.viz.datacenters[0].nodes[0].memoryRects[0].select();
+ assert.equal(Topology.infoPanelTitle, 'Allocation Details');
+ });
+
+ test('when a node is selected, the info panel shows information on the node', async function(assert) {
+ // A high node count is required for node selection
+ server.createList('node', 51);
+ server.createList('allocation', 5);
+
+ await Topology.visit();
+
+ await Topology.viz.datacenters[0].nodes[0].selectNode();
+ assert.equal(Topology.infoPanelTitle, 'Client Details');
+ });
+});
diff --git a/ui/tests/acceptance/volume-detail-test.js b/ui/tests/acceptance/volume-detail-test.js
index b951d54d077..c277dc4bba9 100644
--- a/ui/tests/acceptance/volume-detail-test.js
+++ b/ui/tests/acceptance/volume-detail-test.js
@@ -106,8 +106,8 @@ module('Acceptance | volume detail', function(hooks) {
});
const tasks = taskGroup.taskIds.map(id => server.db.tasks.find(id));
- const cpuUsed = tasks.reduce((sum, task) => sum + task.Resources.CPU, 0);
- const memoryUsed = tasks.reduce((sum, task) => sum + task.Resources.MemoryMB, 0);
+ const cpuUsed = tasks.reduce((sum, task) => sum + task.resources.CPU, 0);
+ const memoryUsed = tasks.reduce((sum, task) => sum + task.resources.MemoryMB, 0);
await VolumeDetail.visit({ id: volume.id });
diff --git a/ui/tests/helpers/glimmer-factory.js b/ui/tests/helpers/glimmer-factory.js
new file mode 100644
index 00000000000..c9cd865d318
--- /dev/null
+++ b/ui/tests/helpers/glimmer-factory.js
@@ -0,0 +1,32 @@
+// Used in glimmer component unit tests. Glimmer components should typically
+// be tested with integration tests, but occasionally individual methods or
+// properties have logic that isn't coupled to rendering or the DOM and can
+// be better tested in a unit fashion.
+//
+// Use like
+//
+// setupGlimmerComponentFactory(hooks, 'my-component')
+//
+// test('testing my component', function(assert) {
+// const component = this.createComponent({ hello: 'world' });
+// assert.equal(component.args.hello, 'world');
+// });
+export default function setupGlimmerComponentFactory(hooks, componentKey) {
+ hooks.beforeEach(function() {
+ this.createComponent = glimmerComponentInstantiator(this.owner, componentKey);
+ });
+
+ hooks.afterEach(function() {
+ delete this.createComponent;
+ });
+}
+
+// Look up the component class in the glimmer component manager and return a
+// function to construct components as if they were functions.
+function glimmerComponentInstantiator(owner, componentKey) {
+ return args => {
+ const componentManager = owner.lookup('component-manager:glimmer');
+ const componentClass = owner.factoryFor(`component:${componentKey}`).class;
+ return componentManager.createComponent(componentClass, { named: args });
+ };
+}
diff --git a/ui/tests/integration/components/flex-masonry-test.js b/ui/tests/integration/components/flex-masonry-test.js
new file mode 100644
index 00000000000..62bded80f83
--- /dev/null
+++ b/ui/tests/integration/components/flex-masonry-test.js
@@ -0,0 +1,168 @@
+import { htmlSafe } from '@ember/template';
+import { click, find, findAll, settled } from '@ember/test-helpers';
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
+import { componentA11yAudit } from 'nomad-ui/tests/helpers/a11y-audit';
+
+// Used to prevent XSS warnings in console
+const h = height => htmlSafe(`height:${height}px`);
+
+module('Integration | Component | FlexMasonry', function(hooks) {
+ setupRenderingTest(hooks);
+
+ test('presents as a single div when @items is empty', async function(assert) {
+ this.setProperties({
+ items: [],
+ });
+
+ await this.render(hbs`
+
+
+ `);
+
+ const div = find('[data-test-flex-masonry]');
+ assert.ok(div);
+ assert.equal(div.tagName.toLowerCase(), 'div');
+ assert.equal(div.children.length, 0);
+
+ await componentA11yAudit(this.element, assert);
+ });
+
+ test('each item in @items gets wrapped in a flex-masonry-item wrapper', async function(assert) {
+ this.setProperties({
+ items: ['one', 'two', 'three'],
+ columns: 2,
+ });
+
+ await this.render(hbs`
+
+ {{item}}
+
+ `);
+
+ assert.equal(findAll('[data-test-flex-masonry-item]').length, this.items.length);
+ });
+
+ test('the @withSpacing arg adds the with-spacing class', async function(assert) {
+ await this.render(hbs`
+
+
+ `);
+
+ assert.ok(find('[data-test-flex-masonry]').classList.contains('with-spacing'));
+ });
+
+ test('individual items along with the reflow action are yielded', async function(assert) {
+ this.setProperties({
+ items: ['one', 'two'],
+ columns: 2,
+ height: h(50),
+ });
+
+ await this.render(hbs`
+
+ {{item}}
+
+ `);
+
+ const div = find('[data-test-flex-masonry]');
+ assert.equal(div.style.maxHeight, '51px');
+ assert.ok(div.textContent.includes('one'));
+ assert.ok(div.textContent.includes('two'));
+
+ this.set('height', h(500));
+ await settled();
+ assert.equal(div.style.maxHeight, '51px');
+
+ // The height of the div changes when reflow is called
+ await click('[data-test-flex-masonry-item]:first-child div');
+ await settled();
+ assert.equal(div.style.maxHeight, '501px');
+ });
+
+ test('items are rendered to the DOM in the order they were passed into the component', async function(assert) {
+ this.setProperties({
+ items: [
+ { text: 'One', height: h(20) },
+ { text: 'Two', height: h(100) },
+ { text: 'Three', height: h(20) },
+ { text: 'Four', height: h(20) },
+ ],
+ columns: 2,
+ });
+
+ await this.render(hbs`
+
+ {{item.text}}
+
+ `);
+
+ findAll('[data-test-flex-masonry-item]').forEach((el, index) => {
+ assert.equal(el.textContent.trim(), this.items[index].text);
+ });
+ });
+
+ test('each item gets an order property', async function(assert) {
+ this.setProperties({
+ items: [
+ { text: 'One', height: h(20), expectedOrder: 0 },
+ { text: 'Two', height: h(100), expectedOrder: 3 },
+ { text: 'Three', height: h(20), expectedOrder: 1 },
+ { text: 'Four', height: h(20), expectedOrder: 2 },
+ ],
+ columns: 2,
+ });
+
+ await this.render(hbs`
+
+ {{item.text}}
+
+ `);
+
+ findAll('[data-test-flex-masonry-item]').forEach((el, index) => {
+ assert.equal(el.style.order, this.items[index].expectedOrder);
+ });
+ });
+
+ test('the last item in each column gets a specific flex-basis value', async function(assert) {
+ this.setProperties({
+ items: [
+ { text: 'One', height: h(20) },
+ { text: 'Two', height: h(100), flexBasis: '100px' },
+ { text: 'Three', height: h(20) },
+ { text: 'Four', height: h(100), flexBasis: '100px' },
+ { text: 'Five', height: h(20), flexBasis: '80px' },
+ { text: 'Six', height: h(20), flexBasis: '80px' },
+ ],
+ columns: 4,
+ });
+
+ await this.render(hbs`
+
+ {{item.text}}
+
+ `);
+
+ findAll('[data-test-flex-masonry-item]').forEach((el, index) => {
+ if (el.style.flexBasis) {
+ assert.equal(el.style.flexBasis, this.items[index].flexBasis);
+ }
+ });
+ });
+});
diff --git a/ui/tests/integration/components/topo-viz-test.js b/ui/tests/integration/components/topo-viz-test.js
new file mode 100644
index 00000000000..5d4ae961f9f
--- /dev/null
+++ b/ui/tests/integration/components/topo-viz-test.js
@@ -0,0 +1,144 @@
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
+import { componentA11yAudit } from 'nomad-ui/tests/helpers/a11y-audit';
+import { create } from 'ember-cli-page-object';
+import sinon from 'sinon';
+import faker from 'nomad-ui/mirage/faker';
+import topoVizPageObject from 'nomad-ui/tests/pages/components/topo-viz';
+
+const TopoViz = create(topoVizPageObject());
+
+const alloc = (nodeId, jobId, taskGroupName, memory, cpu, props = {}) => ({
+ id: faker.random.uuid(),
+ taskGroupName,
+ isScheduled: true,
+ allocatedResources: {
+ cpu,
+ memory,
+ },
+ belongsTo: type => ({
+ id: () => (type === 'job' ? jobId : nodeId),
+ }),
+ ...props,
+});
+
+const node = (datacenter, id, memory, cpu) => ({
+ datacenter,
+ id,
+ resources: { memory, cpu },
+});
+
+module('Integration | Component | TopoViz', function(hooks) {
+ setupRenderingTest(hooks);
+
+ const commonTemplate = hbs`
+
+ `;
+
+ test('presents as a FlexMasonry of datacenters', async function(assert) {
+ this.setProperties({
+ nodes: [node('dc1', 'node0', 1000, 500), node('dc2', 'node1', 1000, 500)],
+
+ allocations: [
+ alloc('node0', 'job1', 'group', 100, 100),
+ alloc('node0', 'job1', 'group', 100, 100),
+ alloc('node1', 'job1', 'group', 100, 100),
+ ],
+ });
+
+ await this.render(commonTemplate);
+
+ assert.equal(TopoViz.datacenters.length, 2);
+ assert.equal(TopoViz.datacenters[0].nodes.length, 1);
+ assert.equal(TopoViz.datacenters[1].nodes.length, 1);
+ assert.equal(TopoViz.datacenters[0].nodes[0].memoryRects.length, 2);
+ assert.equal(TopoViz.datacenters[1].nodes[0].memoryRects.length, 1);
+
+ await componentA11yAudit(this.element, assert);
+ });
+
+ test('clicking on a node in a deeply nested TopoViz::Node will toggle node selection and call @onNodeSelect', async function(assert) {
+ this.setProperties({
+ // TopoViz must be dense for node selection to be a feature
+ nodes: Array(55)
+ .fill(null)
+ .map((_, index) => node('dc1', `node${index}`, 1000, 500)),
+ allocations: [],
+ onNodeSelect: sinon.spy(),
+ });
+
+ await this.render(commonTemplate);
+
+ await TopoViz.datacenters[0].nodes[0].selectNode();
+ assert.ok(this.onNodeSelect.calledOnce);
+ assert.equal(this.onNodeSelect.getCall(0).args[0].node, this.nodes[0]);
+
+ await TopoViz.datacenters[0].nodes[0].selectNode();
+ assert.ok(this.onNodeSelect.calledTwice);
+ assert.equal(this.onNodeSelect.getCall(1).args[0], null);
+ });
+
+ test('clicking on an allocation in a deeply nested TopoViz::Node will update the topology object with selections and call @onAllocationSelect and @onNodeSelect', async function(assert) {
+ this.setProperties({
+ nodes: [node('dc1', 'node0', 1000, 500)],
+ allocations: [alloc('node0', 'job1', 'group', 100, 100)],
+ onNodeSelect: sinon.spy(),
+ onAllocationSelect: sinon.spy(),
+ });
+
+ await this.render(commonTemplate);
+
+ await TopoViz.datacenters[0].nodes[0].memoryRects[0].select();
+ assert.ok(this.onAllocationSelect.calledOnce);
+ assert.equal(this.onAllocationSelect.getCall(0).args[0], this.allocations[0]);
+ assert.ok(this.onNodeSelect.calledOnce);
+
+ await TopoViz.datacenters[0].nodes[0].memoryRects[0].select();
+ assert.ok(this.onAllocationSelect.calledTwice);
+ assert.equal(this.onAllocationSelect.getCall(1).args[0], null);
+ assert.ok(this.onNodeSelect.calledTwice);
+ assert.ok(this.onNodeSelect.alwaysCalledWith(null));
+ });
+
+ test('clicking on an allocation in a deeply nested TopoViz::Node will associate sibling allocations with curves', async function(assert) {
+ this.setProperties({
+ nodes: [
+ node('dc1', 'node0', 1000, 500),
+ node('dc1', 'node1', 1000, 500),
+ node('dc2', 'node2', 1000, 500),
+ ],
+ allocations: [
+ alloc('node0', 'job1', 'group', 100, 100),
+ alloc('node0', 'job1', 'group', 100, 100),
+ alloc('node1', 'job1', 'group', 100, 100),
+ alloc('node2', 'job1', 'group', 100, 100),
+ alloc('node0', 'job1', 'groupTwo', 100, 100),
+ alloc('node1', 'job2', 'group', 100, 100),
+ alloc('node2', 'job2', 'groupTwo', 100, 100),
+ ],
+ onNodeSelect: sinon.spy(),
+ onAllocationSelect: sinon.spy(),
+ });
+
+ const selectedAllocations = this.allocations.filter(
+ alloc => alloc.belongsTo('job').id() === 'job1' && alloc.taskGroupName === 'group'
+ );
+
+ await this.render(commonTemplate);
+
+ assert.notOk(TopoViz.allocationAssociationsArePresent);
+
+ await TopoViz.datacenters[0].nodes[0].memoryRects[0].select();
+
+ assert.ok(TopoViz.allocationAssociationsArePresent);
+ assert.equal(TopoViz.allocationAssociations.length, selectedAllocations.length * 2);
+
+ await TopoViz.datacenters[0].nodes[0].memoryRects[0].select();
+ assert.notOk(TopoViz.allocationAssociationsArePresent);
+ });
+});
diff --git a/ui/tests/integration/components/topo-viz/datacenter-test.js b/ui/tests/integration/components/topo-viz/datacenter-test.js
new file mode 100644
index 00000000000..3480aae8814
--- /dev/null
+++ b/ui/tests/integration/components/topo-viz/datacenter-test.js
@@ -0,0 +1,160 @@
+import { find } from '@ember/test-helpers';
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
+import { componentA11yAudit } from 'nomad-ui/tests/helpers/a11y-audit';
+import { create } from 'ember-cli-page-object';
+import sinon from 'sinon';
+import faker from 'nomad-ui/mirage/faker';
+import topoVizDatacenterPageObject from 'nomad-ui/tests/pages/components/topo-viz/datacenter';
+
+const TopoVizDatacenter = create(topoVizDatacenterPageObject());
+
+const nodeGen = (name, datacenter, memory, cpu, allocations = []) => ({
+ datacenter,
+ memory,
+ cpu,
+ node: { name },
+ allocations: allocations.map(alloc => ({
+ memory: alloc.memory,
+ cpu: alloc.cpu,
+ memoryPercent: alloc.memory / memory,
+ cpuPercent: alloc.cpu / cpu,
+ allocation: {
+ id: faker.random.uuid(),
+ isScheduled: true,
+ },
+ })),
+});
+
+// Used in Array#reduce to sum by a property common to an array of objects
+const sumBy = prop => (sum, obj) => (sum += obj[prop]);
+
+module('Integration | Component | TopoViz::Datacenter', function(hooks) {
+ setupRenderingTest(hooks);
+
+ const commonProps = props => ({
+ isSingleColumn: true,
+ isDense: false,
+ heightScale: () => 50,
+ onAllocationSelect: sinon.spy(),
+ onNodeSelect: sinon.spy(),
+ ...props,
+ });
+
+ const commonTemplate = hbs`
+
+ `;
+
+ test('presents as a div with a label and a FlexMasonry with a collection of nodes', async function(assert) {
+ this.setProperties(
+ commonProps({
+ datacenter: {
+ name: 'dc1',
+ nodes: [nodeGen('node-1', 'dc1', 1000, 500)],
+ },
+ })
+ );
+
+ await this.render(commonTemplate);
+
+ assert.ok(TopoVizDatacenter.isPresent);
+ assert.equal(TopoVizDatacenter.nodes.length, this.datacenter.nodes.length);
+
+ await componentA11yAudit(this.element, assert);
+ });
+
+ test('datacenter stats are an aggregate of node stats', async function(assert) {
+ this.setProperties(
+ commonProps({
+ datacenter: {
+ name: 'dc1',
+ nodes: [
+ nodeGen('node-1', 'dc1', 1000, 500, [
+ { memory: 100, cpu: 300 },
+ { memory: 200, cpu: 50 },
+ ]),
+ nodeGen('node-2', 'dc1', 1500, 100, [
+ { memory: 50, cpu: 80 },
+ { memory: 100, cpu: 20 },
+ ]),
+ nodeGen('node-3', 'dc1', 2000, 300),
+ nodeGen('node-4', 'dc1', 3000, 200),
+ ],
+ },
+ })
+ );
+
+ await this.render(commonTemplate);
+
+ const allocs = this.datacenter.nodes.reduce(
+ (allocs, node) => allocs.concat(node.allocations),
+ []
+ );
+ const memoryReserved = allocs.reduce(sumBy('memory'), 0);
+ const cpuReserved = allocs.reduce(sumBy('cpu'), 0);
+ const memoryTotal = this.datacenter.nodes.reduce(sumBy('memory'), 0);
+ const cpuTotal = this.datacenter.nodes.reduce(sumBy('cpu'), 0);
+
+ assert.ok(TopoVizDatacenter.label.includes(this.datacenter.name));
+ assert.ok(TopoVizDatacenter.label.includes(`${this.datacenter.nodes.length} Nodes`));
+ assert.ok(TopoVizDatacenter.label.includes(`${allocs.length} Allocs`));
+ assert.ok(TopoVizDatacenter.label.includes(`${memoryReserved}/${memoryTotal} MiB`));
+ assert.ok(TopoVizDatacenter.label.includes(`${cpuReserved}/${cpuTotal} Mhz`));
+ });
+
+ test('when @isSingleColumn is true, the FlexMasonry layout gets one column, otherwise it gets two', async function(assert) {
+ this.setProperties(
+ commonProps({
+ isSingleColumn: true,
+ datacenter: {
+ name: 'dc1',
+ nodes: [nodeGen('node-1', 'dc1', 1000, 500), nodeGen('node-2', 'dc1', 1000, 500)],
+ },
+ })
+ );
+
+ await this.render(commonTemplate);
+
+ assert.ok(find('[data-test-flex-masonry].flex-masonry-columns-1'));
+
+ this.set('isSingleColumn', false);
+ assert.ok(find('[data-test-flex-masonry].flex-masonry-columns-2'));
+ });
+
+ test('args get passed down to the TopViz::Node children', async function(assert) {
+ const heightSpy = sinon.spy();
+ this.setProperties(
+ commonProps({
+ isDense: true,
+ heightScale: (...args) => {
+ heightSpy(...args);
+ return 50;
+ },
+ datacenter: {
+ name: 'dc1',
+ nodes: [nodeGen('node-1', 'dc1', 1000, 500, [{ memory: 100, cpu: 300 }])],
+ },
+ })
+ );
+
+ await this.render(commonTemplate);
+
+ TopoVizDatacenter.nodes[0].as(async TopoVizNode => {
+ assert.notOk(TopoVizNode.labelIsPresent);
+ assert.ok(heightSpy.calledWith(this.datacenter.nodes[0].memory));
+
+ await TopoVizNode.selectNode();
+ assert.ok(this.onNodeSelect.calledWith(this.datacenter.nodes[0]));
+
+ await TopoVizNode.memoryRects[0].select();
+ assert.ok(this.onAllocationSelect.calledWith(this.datacenter.nodes[0].allocations[0]));
+ });
+ });
+});
diff --git a/ui/tests/integration/components/topo-viz/node-test.js b/ui/tests/integration/components/topo-viz/node-test.js
new file mode 100644
index 00000000000..14f1f903a0f
--- /dev/null
+++ b/ui/tests/integration/components/topo-viz/node-test.js
@@ -0,0 +1,339 @@
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
+import { componentA11yAudit } from 'nomad-ui/tests/helpers/a11y-audit';
+import { create } from 'ember-cli-page-object';
+import sinon from 'sinon';
+import faker from 'nomad-ui/mirage/faker';
+import topoVisNodePageObject from 'nomad-ui/tests/pages/components/topo-viz/node';
+
+const TopoVizNode = create(topoVisNodePageObject());
+
+const nodeGen = (name, datacenter, memory, cpu, flags = {}) => ({
+ datacenter,
+ memory,
+ cpu,
+ isSelected: !!flags.isSelected,
+ node: {
+ name,
+ isEligible: flags.isEligible || flags.isEligible == null,
+ isDraining: !!flags.isDraining,
+ },
+});
+
+const allocGen = (node, memory, cpu, isSelected) => ({
+ memory,
+ cpu,
+ isSelected,
+ memoryPercent: memory / node.memory,
+ cpuPercent: cpu / node.cpu,
+ allocation: {
+ id: faker.random.uuid(),
+ isScheduled: true,
+ },
+});
+
+const props = overrides => ({
+ isDense: false,
+ heightScale: () => 50,
+ onAllocationSelect: sinon.spy(),
+ onNodeSelect: sinon.spy(),
+ ...overrides,
+});
+
+module('Integration | Component | TopoViz::Node', function(hooks) {
+ setupRenderingTest(hooks);
+
+ const commonTemplate = hbs`
+
+ `;
+
+ test('presents as a div with a label and an svg with CPU and memory rows', async function(assert) {
+ const node = nodeGen('Node One', 'dc1', 1000, 1000);
+ this.setProperties(
+ props({
+ node: {
+ ...node,
+ allocations: [allocGen(node, 100, 100), allocGen(node, 250, 250)],
+ },
+ })
+ );
+
+ await this.render(commonTemplate);
+
+ assert.ok(TopoVizNode.isPresent);
+ assert.ok(TopoVizNode.memoryRects.length);
+ assert.ok(TopoVizNode.cpuRects.length);
+
+ await componentA11yAudit(this.element, assert);
+ });
+
+ test('the label contains aggregate information about the node', async function(assert) {
+ const node = nodeGen('Node One', 'dc1', 1000, 1000);
+ this.setProperties(
+ props({
+ node: {
+ ...node,
+ allocations: [allocGen(node, 100, 100), allocGen(node, 250, 250)],
+ },
+ })
+ );
+
+ await this.render(commonTemplate);
+
+ assert.ok(TopoVizNode.label.includes(node.node.name));
+ assert.ok(TopoVizNode.label.includes(`${this.node.allocations.length} Allocs`));
+ assert.ok(TopoVizNode.label.includes(`${this.node.memory} MiB`));
+ assert.ok(TopoVizNode.label.includes(`${this.node.cpu} Mhz`));
+ });
+
+ test('the status icon indicates when the node is draining', async function(assert) {
+ const node = nodeGen('Node One', 'dc1', 1000, 1000, { isDraining: true });
+ this.setProperties(
+ props({
+ node: {
+ ...node,
+ allocations: [],
+ },
+ })
+ );
+
+ await this.render(commonTemplate);
+
+ assert.ok(TopoVizNode.statusIcon.includes('icon-is-clock-outline'));
+ assert.equal(TopoVizNode.statusIconLabel, 'Client is draining');
+ });
+
+ test('the status icon indicates when the node is ineligible for scheduling', async function(assert) {
+ const node = nodeGen('Node One', 'dc1', 1000, 1000, { isEligible: false });
+ this.setProperties(
+ props({
+ node: {
+ ...node,
+ allocations: [],
+ },
+ })
+ );
+
+ await this.render(commonTemplate);
+
+ assert.ok(TopoVizNode.statusIcon.includes('icon-is-lock-closed'));
+ assert.equal(TopoVizNode.statusIconLabel, 'Client is ineligible');
+ });
+
+ test('when isDense is false, clicking the node does nothing', async function(assert) {
+ const node = nodeGen('Node One', 'dc1', 1000, 1000);
+ this.setProperties(
+ props({
+ isDense: false,
+ node: {
+ ...node,
+ allocations: [],
+ },
+ })
+ );
+
+ await this.render(commonTemplate);
+ await TopoVizNode.selectNode();
+
+ assert.notOk(TopoVizNode.nodeIsInteractive);
+ assert.notOk(this.onNodeSelect.called);
+ });
+
+ test('when isDense is true, clicking the node calls onNodeSelect', async function(assert) {
+ const node = nodeGen('Node One', 'dc1', 1000, 1000);
+ this.setProperties(
+ props({
+ isDense: true,
+ node: {
+ ...node,
+ allocations: [],
+ },
+ })
+ );
+
+ await this.render(commonTemplate);
+ await TopoVizNode.selectNode();
+
+ assert.ok(TopoVizNode.nodeIsInteractive);
+ assert.ok(this.onNodeSelect.called);
+ assert.ok(this.onNodeSelect.calledWith(this.node));
+ });
+
+ test('the node gets the is-selected class when the node is selected', async function(assert) {
+ const node = nodeGen('Node One', 'dc1', 1000, 1000, { isSelected: true });
+ this.setProperties(
+ props({
+ isDense: true,
+ node: {
+ ...node,
+ allocations: [],
+ },
+ })
+ );
+
+ await this.render(commonTemplate);
+
+ assert.ok(TopoVizNode.nodeIsSelected);
+ });
+
+ test('the node gets its height form the @heightScale arg', async function(assert) {
+ const node = nodeGen('Node One', 'dc1', 1000, 1000);
+ const height = 50;
+ const heightSpy = sinon.spy();
+ this.setProperties(
+ props({
+ heightScale: (...args) => {
+ heightSpy(...args);
+ return height;
+ },
+ node: {
+ ...node,
+ allocations: [allocGen(node, 100, 100), allocGen(node, 250, 250)],
+ },
+ })
+ );
+
+ await this.render(commonTemplate);
+
+ assert.ok(heightSpy.called);
+ assert.ok(heightSpy.calledWith(this.node.memory));
+ assert.equal(TopoVizNode.memoryRects[0].height, `${height}px`);
+ });
+
+ test('each allocation gets a memory rect and a cpu rect', async function(assert) {
+ const node = nodeGen('Node One', 'dc1', 1000, 1000);
+ this.setProperties(
+ props({
+ node: {
+ ...node,
+ allocations: [allocGen(node, 100, 100), allocGen(node, 250, 250)],
+ },
+ })
+ );
+
+ await this.render(commonTemplate);
+
+ assert.equal(TopoVizNode.memoryRects.length, this.node.allocations.length);
+ assert.equal(TopoVizNode.cpuRects.length, this.node.allocations.length);
+ });
+
+ test('each allocation is sized according to its percentage of utilization', async function(assert) {
+ const node = nodeGen('Node One', 'dc1', 1000, 1000);
+ this.setProperties(
+ props({
+ node: {
+ ...node,
+ allocations: [allocGen(node, 100, 100), allocGen(node, 250, 250)],
+ },
+ })
+ );
+
+ await this.render(hbs`
+
+
+
+ `);
+
+ // Remove the width of the padding and the label from the SVG width
+ const width = 100 - 5 - 5 - 20;
+ this.node.allocations.forEach((alloc, index) => {
+ const memWidth = alloc.memoryPercent * width - (index === 0 ? 0.5 : 1);
+ const cpuWidth = alloc.cpuPercent * width - (index === 0 ? 0.5 : 1);
+ assert.equal(TopoVizNode.memoryRects[index].width, `${memWidth}px`);
+ assert.equal(TopoVizNode.cpuRects[index].width, `${cpuWidth}px`);
+ });
+ });
+
+ test('clicking either the memory or cpu rect for an allocation will call onAllocationSelect', async function(assert) {
+ const node = nodeGen('Node One', 'dc1', 1000, 1000);
+ this.setProperties(
+ props({
+ node: {
+ ...node,
+ allocations: [allocGen(node, 100, 100), allocGen(node, 250, 250)],
+ },
+ })
+ );
+
+ await this.render(commonTemplate);
+
+ await TopoVizNode.memoryRects[0].select();
+ assert.ok(this.onAllocationSelect.callCount, 1);
+ assert.ok(this.onAllocationSelect.calledWith(this.node.allocations[0]));
+
+ await TopoVizNode.cpuRects[0].select();
+ assert.ok(this.onAllocationSelect.callCount, 2);
+
+ await TopoVizNode.cpuRects[1].select();
+ assert.ok(this.onAllocationSelect.callCount, 3);
+ assert.ok(this.onAllocationSelect.calledWith(this.node.allocations[1]));
+
+ await TopoVizNode.memoryRects[1].select();
+ assert.ok(this.onAllocationSelect.callCount, 4);
+ });
+
+ test('allocations are sorted by smallest to largest delta of memory to cpu percent utilizations', async function(assert) {
+ const node = nodeGen('Node One', 'dc1', 1000, 1000);
+
+ const evenAlloc = allocGen(node, 100, 100);
+ const mediumMemoryAlloc = allocGen(node, 200, 150);
+ const largeMemoryAlloc = allocGen(node, 300, 50);
+ const mediumCPUAlloc = allocGen(node, 150, 200);
+ const largeCPUAlloc = allocGen(node, 50, 300);
+
+ this.setProperties(
+ props({
+ node: {
+ ...node,
+ allocations: [
+ largeCPUAlloc,
+ mediumCPUAlloc,
+ evenAlloc,
+ mediumMemoryAlloc,
+ largeMemoryAlloc,
+ ],
+ },
+ })
+ );
+
+ await this.render(commonTemplate);
+
+ const expectedOrder = [
+ evenAlloc,
+ mediumCPUAlloc,
+ mediumMemoryAlloc,
+ largeCPUAlloc,
+ largeMemoryAlloc,
+ ];
+ expectedOrder.forEach((alloc, index) => {
+ assert.equal(TopoVizNode.memoryRects[index].id, alloc.allocation.id);
+ assert.equal(TopoVizNode.cpuRects[index].id, alloc.allocation.id);
+ });
+ });
+
+ test('when there are no allocations, a "no allocations" note is shown', async function(assert) {
+ const node = nodeGen('Node One', 'dc1', 1000, 1000);
+ this.setProperties(
+ props({
+ node: {
+ ...node,
+ allocations: [],
+ },
+ })
+ );
+
+ await this.render(commonTemplate);
+ assert.equal(TopoVizNode.emptyMessage, 'Empty Client');
+ });
+});
diff --git a/ui/tests/pages/components/topo-viz.js b/ui/tests/pages/components/topo-viz.js
new file mode 100644
index 00000000000..657e5a165d8
--- /dev/null
+++ b/ui/tests/pages/components/topo-viz.js
@@ -0,0 +1,11 @@
+import { collection, isPresent } from 'ember-cli-page-object';
+import TopoVizDatacenter from './topo-viz/datacenter';
+
+export default scope => ({
+ scope,
+
+ datacenters: collection('[data-test-topo-viz-datacenter]', TopoVizDatacenter()),
+
+ allocationAssociationsArePresent: isPresent('[data-test-allocation-associations]'),
+ allocationAssociations: collection('[data-test-allocation-association]'),
+});
diff --git a/ui/tests/pages/components/topo-viz/datacenter.js b/ui/tests/pages/components/topo-viz/datacenter.js
new file mode 100644
index 00000000000..1388eeead53
--- /dev/null
+++ b/ui/tests/pages/components/topo-viz/datacenter.js
@@ -0,0 +1,9 @@
+import { collection, text } from 'ember-cli-page-object';
+import TopoVizNode from './node';
+
+export default scope => ({
+ scope,
+
+ label: text('[data-test-topo-viz-datacenter-label]'),
+ nodes: collection('[data-test-topo-viz-node]', TopoVizNode()),
+});
diff --git a/ui/tests/pages/components/topo-viz/node.js b/ui/tests/pages/components/topo-viz/node.js
new file mode 100644
index 00000000000..665940ebf5a
--- /dev/null
+++ b/ui/tests/pages/components/topo-viz/node.js
@@ -0,0 +1,36 @@
+import { attribute, collection, clickable, hasClass, isPresent, text } from 'ember-cli-page-object';
+
+const allocationRect = {
+ select: clickable(),
+ width: attribute('width', '> rect'),
+ height: attribute('height', '> rect'),
+ isActive: hasClass('is-active'),
+ isSelected: hasClass('is-selected'),
+ running: hasClass('running'),
+ failed: hasClass('failed'),
+ pending: hasClass('pending'),
+};
+
+export default scope => ({
+ scope,
+
+ label: text('[data-test-label]'),
+ labelIsPresent: isPresent('[data-test-label]'),
+ statusIcon: attribute('class', '[data-test-status-icon] .icon'),
+ statusIconLabel: attribute('aria-label', '[data-test-status-icon]'),
+
+ selectNode: clickable('[data-test-node-background]'),
+ nodeIsInteractive: hasClass('is-interactive', '[data-test-node-background]'),
+ nodeIsSelected: hasClass('is-selected', '[data-test-node-background]'),
+
+ memoryRects: collection('[data-test-memory-rect]', {
+ ...allocationRect,
+ id: attribute('data-test-memory-rect'),
+ }),
+ cpuRects: collection('[data-test-cpu-rect]', {
+ ...allocationRect,
+ id: attribute('data-test-cpu-rect'),
+ }),
+
+ emptyMessage: text('[data-test-empty-message]'),
+});
diff --git a/ui/tests/pages/topology.js b/ui/tests/pages/topology.js
new file mode 100644
index 00000000000..b9f2c63a6a6
--- /dev/null
+++ b/ui/tests/pages/topology.js
@@ -0,0 +1,11 @@
+import { create, text, visitable } from 'ember-cli-page-object';
+
+import TopoViz from 'nomad-ui/tests/pages/components/topo-viz';
+
+export default create({
+ visit: visitable('/topology'),
+
+ infoPanelTitle: text('[data-test-info-panel-title]'),
+
+ viz: TopoViz('[data-test-topo-viz]'),
+});
diff --git a/ui/tests/unit/components/topo-viz-test.js b/ui/tests/unit/components/topo-viz-test.js
new file mode 100644
index 00000000000..ad4a018ef28
--- /dev/null
+++ b/ui/tests/unit/components/topo-viz-test.js
@@ -0,0 +1,191 @@
+import { module, test } from 'qunit';
+import { setupTest } from 'ember-qunit';
+import setupGlimmerComponentFactory from 'nomad-ui/tests/helpers/glimmer-factory';
+
+module('Unit | Component | TopoViz', function(hooks) {
+ setupTest(hooks);
+ setupGlimmerComponentFactory(hooks, 'topo-viz');
+
+ test('the topology object properly organizes a tree of datacenters > nodes > allocations', async function(assert) {
+ const nodes = [
+ { datacenter: 'dc1', id: 'node0', resources: {} },
+ { datacenter: 'dc2', id: 'node1', resources: {} },
+ { datacenter: 'dc1', id: 'node2', resources: {} },
+ ];
+
+ const node0Allocs = [
+ alloc({ nodeId: 'node0', jobId: 'job0', taskGroupName: 'group' }),
+ alloc({ nodeId: 'node0', jobId: 'job1', taskGroupName: 'group' }),
+ ];
+ const node1Allocs = [
+ alloc({ nodeId: 'node1', jobId: 'job0', taskGroupName: 'group' }),
+ alloc({ nodeId: 'node1', jobId: 'job1', taskGroupName: 'group' }),
+ ];
+ const node2Allocs = [
+ alloc({ nodeId: 'node2', jobId: 'job0', taskGroupName: 'group' }),
+ alloc({ nodeId: 'node2', jobId: 'job1', taskGroupName: 'group' }),
+ ];
+
+ const allocations = [...node0Allocs, ...node1Allocs, ...node2Allocs];
+
+ const topoViz = this.createComponent({ nodes, allocations });
+
+ topoViz.buildTopology();
+
+ assert.deepEqual(topoViz.topology.datacenters.mapBy('name'), ['dc1', 'dc2']);
+ assert.deepEqual(topoViz.topology.datacenters[0].nodes.mapBy('node'), [nodes[0], nodes[2]]);
+ assert.deepEqual(topoViz.topology.datacenters[1].nodes.mapBy('node'), [nodes[1]]);
+ assert.deepEqual(
+ topoViz.topology.datacenters[0].nodes[0].allocations.mapBy('allocation'),
+ node0Allocs
+ );
+ assert.deepEqual(
+ topoViz.topology.datacenters[1].nodes[0].allocations.mapBy('allocation'),
+ node1Allocs
+ );
+ assert.deepEqual(
+ topoViz.topology.datacenters[0].nodes[1].allocations.mapBy('allocation'),
+ node2Allocs
+ );
+ });
+
+ test('the topology object contains an allocation index keyed by jobId+taskGroupName', async function(assert) {
+ const allocations = [
+ alloc({ nodeId: 'node0', jobId: 'job0', taskGroupName: 'one' }),
+ alloc({ nodeId: 'node0', jobId: 'job0', taskGroupName: 'one' }),
+ alloc({ nodeId: 'node0', jobId: 'job0', taskGroupName: 'two' }),
+ alloc({ nodeId: 'node0', jobId: 'job1', taskGroupName: 'one' }),
+ alloc({ nodeId: 'node0', jobId: 'job1', taskGroupName: 'two' }),
+ alloc({ nodeId: 'node0', jobId: 'job1', taskGroupName: 'three' }),
+ alloc({ nodeId: 'node0', jobId: 'job2', taskGroupName: 'one' }),
+ alloc({ nodeId: 'node0', jobId: 'job2', taskGroupName: 'one' }),
+ alloc({ nodeId: 'node0', jobId: 'job2', taskGroupName: 'one' }),
+ alloc({ nodeId: 'node0', jobId: 'job2', taskGroupName: 'one' }),
+ ];
+
+ const nodes = [{ datacenter: 'dc1', id: 'node0', resources: {} }];
+ const topoViz = this.createComponent({ nodes, allocations });
+
+ topoViz.buildTopology();
+
+ assert.deepEqual(
+ Object.keys(topoViz.topology.allocationIndex).sort(),
+ [
+ JSON.stringify(['job0', 'one']),
+ JSON.stringify(['job0', 'two']),
+
+ JSON.stringify(['job1', 'one']),
+ JSON.stringify(['job1', 'two']),
+ JSON.stringify(['job1', 'three']),
+
+ JSON.stringify(['job2', 'one']),
+ ].sort()
+ );
+
+ Object.keys(topoViz.topology.allocationIndex).forEach(key => {
+ const [jobId, group] = JSON.parse(key);
+ assert.deepEqual(
+ topoViz.topology.allocationIndex[key].mapBy('allocation'),
+ allocations.filter(alloc => alloc.jobId === jobId && alloc.taskGroupName === group)
+ );
+ });
+ });
+
+ test('isSingleColumn is true when there is only one datacenter', async function(assert) {
+ const oneDc = [{ datacenter: 'dc1', id: 'node0', resources: {} }];
+ const twoDc = [...oneDc, { datacenter: 'dc2', id: 'node1', resources: {} }];
+
+ const topoViz1 = this.createComponent({ nodes: oneDc, allocations: [] });
+ const topoViz2 = this.createComponent({ nodes: twoDc, allocations: [] });
+
+ topoViz1.buildTopology();
+ topoViz2.buildTopology();
+
+ assert.ok(topoViz1.isSingleColumn);
+ assert.notOk(topoViz2.isSingleColumn);
+ });
+
+ test('isSingleColumn is true when there are multiple datacenters with a high variance in node count', async function(assert) {
+ const uniformDcs = [
+ { datacenter: 'dc1', id: 'node0', resources: {} },
+ { datacenter: 'dc2', id: 'node1', resources: {} },
+ ];
+ const skewedDcs = [
+ { datacenter: 'dc1', id: 'node0', resources: {} },
+ { datacenter: 'dc2', id: 'node1', resources: {} },
+ { datacenter: 'dc2', id: 'node2', resources: {} },
+ { datacenter: 'dc2', id: 'node3', resources: {} },
+ { datacenter: 'dc2', id: 'node4', resources: {} },
+ ];
+
+ const twoColumnViz = this.createComponent({ nodes: uniformDcs, allocations: [] });
+ const oneColumViz = this.createComponent({ nodes: skewedDcs, allocations: [] });
+
+ twoColumnViz.buildTopology();
+ oneColumViz.buildTopology();
+
+ assert.notOk(twoColumnViz.isSingleColumn);
+ assert.ok(oneColumViz.isSingleColumn);
+ });
+
+ test('datacenterIsSingleColumn is only ever false when isSingleColumn is false and the total node count is high', async function(assert) {
+ const manyUniformNodes = Array(25)
+ .fill(null)
+ .map((_, index) => ({
+ datacenter: index > 12 ? 'dc2' : 'dc1',
+ id: `node${index}`,
+ resources: {},
+ }));
+ const manySkewedNodes = Array(25)
+ .fill(null)
+ .map((_, index) => ({
+ datacenter: index > 5 ? 'dc2' : 'dc1',
+ id: `node${index}`,
+ resources: {},
+ }));
+
+ const oneColumnViz = this.createComponent({ nodes: manyUniformNodes, allocations: [] });
+ const twoColumnViz = this.createComponent({ nodes: manySkewedNodes, allocations: [] });
+
+ oneColumnViz.buildTopology();
+ twoColumnViz.buildTopology();
+
+ assert.ok(oneColumnViz.datacenterIsSingleColumn);
+ assert.notOk(oneColumnViz.isSingleColumn);
+
+ assert.notOk(twoColumnViz.datacenterIsSingleColumn);
+ assert.ok(twoColumnViz.isSingleColumn);
+ });
+
+ test('dataForAllocation correctly calculates proportion of node utilization and group key', async function(assert) {
+ const nodes = [{ datacenter: 'dc1', id: 'node0', resources: { cpu: 100, memory: 250 } }];
+ const allocations = [
+ alloc({
+ nodeId: 'node0',
+ jobId: 'job0',
+ taskGroupName: 'group',
+ allocatedResources: { cpu: 50, memory: 25 },
+ }),
+ ];
+
+ const topoViz = this.createComponent({ nodes, allocations });
+ topoViz.buildTopology();
+
+ assert.equal(topoViz.topology.datacenters[0].nodes[0].allocations[0].cpuPercent, 0.5);
+ assert.equal(topoViz.topology.datacenters[0].nodes[0].allocations[0].memoryPercent, 0.1);
+ });
+});
+
+function alloc(props) {
+ return {
+ ...props,
+ allocatedResources: props.allocatedResources || {},
+ belongsTo(type) {
+ return {
+ id() {
+ return type === 'job' ? props.jobId : props.nodeId;
+ },
+ };
+ },
+ };
+}
diff --git a/ui/tests/unit/helpers/format-bytes-test.js b/ui/tests/unit/helpers/format-bytes-test.js
index 14da4d13e34..b837d31be1a 100644
--- a/ui/tests/unit/helpers/format-bytes-test.js
+++ b/ui/tests/unit/helpers/format-bytes-test.js
@@ -24,8 +24,13 @@ module('Unit | Helper | format-bytes', function() {
assert.equal(formatBytes([128974848]), '123 MiB');
});
- test('formats x > 1024 * 1024 * 1024 as MiB, since it is the highest allowed unit', function(assert) {
- assert.equal(formatBytes([1024 * 1024 * 1024]), '1024 MiB');
- assert.equal(formatBytes([1024 * 1024 * 1024 * 4]), '4096 MiB');
+ test('formats 1024 * 1024 * 1024 <= x < 1024 * 1024 * 1024 * 1024 as GiB', function(assert) {
+ assert.equal(formatBytes([1024 * 1024 * 1024]), '1 GiB');
+ assert.equal(formatBytes([1024 * 1024 * 1024 * 4]), '4 GiB');
+ });
+
+ test('formats x > 1024 * 1024 * 1024 * 1024 as GiB, since it is the highest allowed unit', function(assert) {
+ assert.equal(formatBytes([1024 * 1024 * 1024 * 1024]), '1024 GiB');
+ assert.equal(formatBytes([1024 * 1024 * 1024 * 1024 * 4]), '4096 GiB');
});
});
diff --git a/ui/yarn.lock b/ui/yarn.lock
index 75b7a7316b0..4df1697a3fc 100644
--- a/ui/yarn.lock
+++ b/ui/yarn.lock
@@ -30,6 +30,13 @@
dependencies:
"@babel/highlight" "^7.10.3"
+"@babel/code-frame@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a"
+ integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==
+ dependencies:
+ "@babel/highlight" "^7.10.4"
+
"@babel/compat-data@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.10.1.tgz#b1085ffe72cd17bf2c0ee790fc09f9626011b2db"
@@ -48,6 +55,15 @@
invariant "^2.2.4"
semver "^5.5.0"
+"@babel/compat-data@^7.10.4", "@babel/compat-data@^7.11.0":
+ version "7.11.0"
+ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.11.0.tgz#e9f73efe09af1355b723a7f39b11bad637d7c99c"
+ integrity sha512-TPSvJfv73ng0pfnEOh17bYMPQbI95+nGWc71Ss4vZdRBHTDqmM9Z8ZV4rYz8Ks7sfzc95n30k6ODIq5UGnXcYQ==
+ dependencies:
+ browserslist "^4.12.0"
+ invariant "^2.2.4"
+ semver "^5.5.0"
+
"@babel/core@^7.0.0", "@babel/core@^7.1.6", "@babel/core@^7.3.3", "@babel/core@^7.3.4":
version "7.4.0"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.4.0.tgz#248fd6874b7d755010bfe61f557461d4f446d9e9"
@@ -90,6 +106,28 @@
semver "^5.4.1"
source-map "^0.5.0"
+"@babel/core@^7.11.0":
+ version "7.11.6"
+ resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.6.tgz#3a9455dc7387ff1bac45770650bc13ba04a15651"
+ integrity sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg==
+ dependencies:
+ "@babel/code-frame" "^7.10.4"
+ "@babel/generator" "^7.11.6"
+ "@babel/helper-module-transforms" "^7.11.0"
+ "@babel/helpers" "^7.10.4"
+ "@babel/parser" "^7.11.5"
+ "@babel/template" "^7.10.4"
+ "@babel/traverse" "^7.11.5"
+ "@babel/types" "^7.11.5"
+ convert-source-map "^1.7.0"
+ debug "^4.1.0"
+ gensync "^1.0.0-beta.1"
+ json5 "^2.1.2"
+ lodash "^4.17.19"
+ resolve "^1.3.2"
+ semver "^5.4.1"
+ source-map "^0.5.0"
+
"@babel/core@^7.2.2":
version "7.5.5"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.5.5.tgz#17b2686ef0d6bc58f963dddd68ab669755582c30"
@@ -152,6 +190,15 @@
lodash "^4.17.13"
source-map "^0.5.0"
+"@babel/generator@^7.11.5", "@babel/generator@^7.11.6":
+ version "7.11.6"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.6.tgz#b868900f81b163b4d464ea24545c61cbac4dc620"
+ integrity sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA==
+ dependencies:
+ "@babel/types" "^7.11.5"
+ jsesc "^2.5.1"
+ source-map "^0.5.0"
+
"@babel/generator@^7.4.0", "@babel/generator@^7.6.2":
version "7.6.2"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.6.2.tgz#dac8a3c2df118334c2a29ff3446da1636a8f8c03"
@@ -187,6 +234,13 @@
dependencies:
"@babel/types" "^7.10.1"
+"@babel/helper-annotate-as-pure@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz#5bf0d495a3f757ac3bda48b5bf3b3ba309c72ba3"
+ integrity sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==
+ dependencies:
+ "@babel/types" "^7.10.4"
+
"@babel/helper-builder-binary-assignment-operator-visitor@^7.1.0":
version "7.1.0"
resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.1.0.tgz#6b69628dfe4087798e0c4ed98e3d4a6b2fbd2f5f"
@@ -203,6 +257,14 @@
"@babel/helper-explode-assignable-expression" "^7.10.1"
"@babel/types" "^7.10.1"
+"@babel/helper-builder-binary-assignment-operator-visitor@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz#bb0b75f31bf98cbf9ff143c1ae578b87274ae1a3"
+ integrity sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==
+ dependencies:
+ "@babel/helper-explode-assignable-expression" "^7.10.4"
+ "@babel/types" "^7.10.4"
+
"@babel/helper-call-delegate@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/helper-call-delegate/-/helper-call-delegate-7.4.4.tgz#87c1f8ca19ad552a736a7a27b1c1fcf8b1ff1f43"
@@ -223,6 +285,17 @@
levenary "^1.1.1"
semver "^5.5.0"
+"@babel/helper-compilation-targets@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.4.tgz#804ae8e3f04376607cc791b9d47d540276332bd2"
+ integrity sha512-a3rYhlsGV0UHNDvrtOXBg8/OpfV0OKTkxKPzIplS1zpx7CygDcWWxckxZeDd3gzPzC4kUT0A4nVFDK0wGMh4MQ==
+ dependencies:
+ "@babel/compat-data" "^7.10.4"
+ browserslist "^4.12.0"
+ invariant "^2.2.4"
+ levenary "^1.1.1"
+ semver "^5.5.0"
+
"@babel/helper-create-class-features-plugin@^7.10.1":
version "7.10.2"
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.2.tgz#7474295770f217dbcf288bf7572eb213db46ee67"
@@ -247,6 +320,18 @@
"@babel/helper-replace-supers" "^7.10.1"
"@babel/helper-split-export-declaration" "^7.10.1"
+"@babel/helper-create-class-features-plugin@^7.10.4", "@babel/helper-create-class-features-plugin@^7.10.5":
+ version "7.10.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz#9f61446ba80e8240b0a5c85c6fdac8459d6f259d"
+ integrity sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A==
+ dependencies:
+ "@babel/helper-function-name" "^7.10.4"
+ "@babel/helper-member-expression-to-functions" "^7.10.5"
+ "@babel/helper-optimise-call-expression" "^7.10.4"
+ "@babel/helper-plugin-utils" "^7.10.4"
+ "@babel/helper-replace-supers" "^7.10.4"
+ "@babel/helper-split-export-declaration" "^7.10.4"
+
"@babel/helper-create-class-features-plugin@^7.4.0", "@babel/helper-create-class-features-plugin@^7.5.5":
version "7.6.0"
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.6.0.tgz#769711acca889be371e9bc2eb68641d55218021f"
@@ -280,6 +365,15 @@
"@babel/helper-regex" "^7.10.1"
regexpu-core "^4.7.0"
+"@babel/helper-create-regexp-features-plugin@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz#fdd60d88524659a0b6959c0579925e425714f3b8"
+ integrity sha512-2/hu58IEPKeoLF45DBwx3XFqsbCXmkdAay4spVr2x0jYgRxrSNp+ePwvSsy9g6YSaNDcKIQVPXk1Ov8S2edk2g==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.10.4"
+ "@babel/helper-regex" "^7.10.4"
+ regexpu-core "^4.7.0"
+
"@babel/helper-define-map@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.10.1.tgz#5e69ee8308648470dd7900d159c044c10285221d"
@@ -298,6 +392,15 @@
"@babel/types" "^7.10.3"
lodash "^4.17.13"
+"@babel/helper-define-map@^7.10.4":
+ version "7.10.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz#b53c10db78a640800152692b13393147acb9bb30"
+ integrity sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ==
+ dependencies:
+ "@babel/helper-function-name" "^7.10.4"
+ "@babel/types" "^7.10.5"
+ lodash "^4.17.19"
+
"@babel/helper-define-map@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.4.4.tgz#6969d1f570b46bdc900d1eba8e5d59c48ba2c12a"
@@ -332,6 +435,13 @@
"@babel/traverse" "^7.10.1"
"@babel/types" "^7.10.1"
+"@babel/helper-explode-assignable-expression@^7.10.4":
+ version "7.11.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.11.4.tgz#2d8e3470252cc17aba917ede7803d4a7a276a41b"
+ integrity sha512-ux9hm3zR4WV1Y3xXxXkdG/0gxF9nvI0YVmKVhvK9AfMoaQkemL3sJpXw+Xbz65azo8qJiEz2XVDUpK3KYhH3ZQ==
+ dependencies:
+ "@babel/types" "^7.10.4"
+
"@babel/helper-function-name@^7.1.0":
version "7.1.0"
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz#a0ceb01685f73355d4360c1247f582bfafc8ff53"
@@ -359,6 +469,15 @@
"@babel/template" "^7.10.3"
"@babel/types" "^7.10.3"
+"@babel/helper-function-name@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz#d2d3b20c59ad8c47112fa7d2a94bc09d5ef82f1a"
+ integrity sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==
+ dependencies:
+ "@babel/helper-get-function-arity" "^7.10.4"
+ "@babel/template" "^7.10.4"
+ "@babel/types" "^7.10.4"
+
"@babel/helper-get-function-arity@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz#83572d4320e2a4657263734113c42868b64e49c3"
@@ -380,6 +499,13 @@
dependencies:
"@babel/types" "^7.10.3"
+"@babel/helper-get-function-arity@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2"
+ integrity sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==
+ dependencies:
+ "@babel/types" "^7.10.4"
+
"@babel/helper-hoist-variables@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.1.tgz#7e77c82e5dcae1ebf123174c385aaadbf787d077"
@@ -394,6 +520,13 @@
dependencies:
"@babel/types" "^7.10.3"
+"@babel/helper-hoist-variables@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz#d49b001d1d5a68ca5e6604dda01a6297f7c9381e"
+ integrity sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA==
+ dependencies:
+ "@babel/types" "^7.10.4"
+
"@babel/helper-hoist-variables@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.4.4.tgz#0298b5f25c8c09c53102d52ac4a98f773eb2850a"
@@ -422,6 +555,13 @@
dependencies:
"@babel/types" "^7.10.3"
+"@babel/helper-member-expression-to-functions@^7.10.4", "@babel/helper-member-expression-to-functions@^7.10.5":
+ version "7.11.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz#ae69c83d84ee82f4b42f96e2a09410935a8f26df"
+ integrity sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q==
+ dependencies:
+ "@babel/types" "^7.11.0"
+
"@babel/helper-member-expression-to-functions@^7.5.5":
version "7.5.5"
resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.5.5.tgz#1fb5b8ec4453a93c439ee9fe3aeea4a84b76b590"
@@ -450,6 +590,13 @@
dependencies:
"@babel/types" "^7.10.3"
+"@babel/helper-module-imports@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz#4c5c54be04bd31670a7382797d75b9fa2e5b5620"
+ integrity sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==
+ dependencies:
+ "@babel/types" "^7.10.4"
+
"@babel/helper-module-transforms@^7.1.0":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.4.4.tgz#96115ea42a2f139e619e98ed46df6019b94414b8"
@@ -475,6 +622,19 @@
"@babel/types" "^7.10.1"
lodash "^4.17.13"
+"@babel/helper-module-transforms@^7.10.4", "@babel/helper-module-transforms@^7.10.5", "@babel/helper-module-transforms@^7.11.0":
+ version "7.11.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz#b16f250229e47211abdd84b34b64737c2ab2d359"
+ integrity sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg==
+ dependencies:
+ "@babel/helper-module-imports" "^7.10.4"
+ "@babel/helper-replace-supers" "^7.10.4"
+ "@babel/helper-simple-access" "^7.10.4"
+ "@babel/helper-split-export-declaration" "^7.11.0"
+ "@babel/template" "^7.10.4"
+ "@babel/types" "^7.11.0"
+ lodash "^4.17.19"
+
"@babel/helper-module-transforms@^7.4.4":
version "7.5.5"
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.5.5.tgz#f84ff8a09038dcbca1fd4355661a500937165b4a"
@@ -508,6 +668,13 @@
dependencies:
"@babel/types" "^7.10.3"
+"@babel/helper-optimise-call-expression@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz#50dc96413d594f995a77905905b05893cd779673"
+ integrity sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==
+ dependencies:
+ "@babel/types" "^7.10.4"
+
"@babel/helper-plugin-utils@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250"
@@ -523,6 +690,11 @@
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.3.tgz#aac45cccf8bc1873b99a85f34bceef3beb5d3244"
integrity sha512-j/+j8NAWUTxOtx4LKHybpSClxHoq6I91DQ/mKgAXn5oNUPIUiGppjPIX3TDtJWPrdfP9Kfl7e4fgVMiQR9VE/g==
+"@babel/helper-plugin-utils@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375"
+ integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==
+
"@babel/helper-regex@^7.0.0":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.4.4.tgz#a47e02bc91fb259d2e6727c2a30013e3ac13c4a2"
@@ -537,6 +709,13 @@
dependencies:
lodash "^4.17.13"
+"@babel/helper-regex@^7.10.4":
+ version "7.10.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.10.5.tgz#32dfbb79899073c415557053a19bd055aae50ae0"
+ integrity sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg==
+ dependencies:
+ lodash "^4.17.19"
+
"@babel/helper-regex@^7.4.4":
version "7.5.5"
resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.5.5.tgz#0aa6824f7100a2e0e89c1527c23936c152cab351"
@@ -577,6 +756,16 @@
"@babel/traverse" "^7.10.3"
"@babel/types" "^7.10.3"
+"@babel/helper-remap-async-to-generator@^7.10.4":
+ version "7.11.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.11.4.tgz#4474ea9f7438f18575e30b0cac784045b402a12d"
+ integrity sha512-tR5vJ/vBa9wFy3m5LLv2faapJLnDFxNWff2SAYkSE4rLUdbp7CdObYFgI7wK4T/Mj4UzpjPwzR8Pzmr5m7MHGA==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.10.4"
+ "@babel/helper-wrap-function" "^7.10.4"
+ "@babel/template" "^7.10.4"
+ "@babel/types" "^7.10.4"
+
"@babel/helper-replace-supers@^7.1.0", "@babel/helper-replace-supers@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.4.4.tgz#aee41783ebe4f2d3ab3ae775e1cc6f1a90cefa27"
@@ -597,6 +786,16 @@
"@babel/traverse" "^7.10.1"
"@babel/types" "^7.10.1"
+"@babel/helper-replace-supers@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz#d585cd9388ea06e6031e4cd44b6713cbead9e6cf"
+ integrity sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==
+ dependencies:
+ "@babel/helper-member-expression-to-functions" "^7.10.4"
+ "@babel/helper-optimise-call-expression" "^7.10.4"
+ "@babel/traverse" "^7.10.4"
+ "@babel/types" "^7.10.4"
+
"@babel/helper-replace-supers@^7.5.5":
version "7.5.5"
resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.5.5.tgz#f84ce43df031222d2bad068d2626cb5799c34bc2"
@@ -623,6 +822,21 @@
"@babel/template" "^7.10.1"
"@babel/types" "^7.10.1"
+"@babel/helper-simple-access@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz#0f5ccda2945277a2a7a2d3a821e15395edcf3461"
+ integrity sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw==
+ dependencies:
+ "@babel/template" "^7.10.4"
+ "@babel/types" "^7.10.4"
+
+"@babel/helper-skip-transparent-expression-wrappers@^7.11.0":
+ version "7.11.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.11.0.tgz#eec162f112c2f58d3af0af125e3bb57665146729"
+ integrity sha512-0XIdiQln4Elglgjbwo9wuJpL/K7AGCY26kmEt0+pRP0TAj4jjyNq1MjoRvikrTVqKcx4Gysxt4cXvVFXP/JO2Q==
+ dependencies:
+ "@babel/types" "^7.11.0"
+
"@babel/helper-split-export-declaration@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.1.tgz#c6f4be1cbc15e3a868e4c64a17d5d31d754da35f"
@@ -630,6 +844,13 @@
dependencies:
"@babel/types" "^7.10.1"
+"@babel/helper-split-export-declaration@^7.10.4", "@babel/helper-split-export-declaration@^7.11.0":
+ version "7.11.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f"
+ integrity sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==
+ dependencies:
+ "@babel/types" "^7.11.0"
+
"@babel/helper-split-export-declaration@^7.4.0", "@babel/helper-split-export-declaration@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz#ff94894a340be78f53f06af038b205c49d993677"
@@ -647,6 +868,11 @@
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.3.tgz#60d9847f98c4cea1b279e005fdb7c28be5412d15"
integrity sha512-bU8JvtlYpJSBPuj1VUmKpFGaDZuLxASky3LhaKj3bmpSTY6VWooSM8msk+Z0CZoErFye2tlABF6yDkT3FOPAXw==
+"@babel/helper-validator-identifier@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2"
+ integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==
+
"@babel/helper-wrap-function@^7.1.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.2.0.tgz#c4e0012445769e2815b55296ead43a958549f6fa"
@@ -667,6 +893,16 @@
"@babel/traverse" "^7.10.1"
"@babel/types" "^7.10.1"
+"@babel/helper-wrap-function@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz#8a6f701eab0ff39f765b5a1cfef409990e624b87"
+ integrity sha512-6py45WvEF0MhiLrdxtRjKjufwLL1/ob2qDJgg5JgNdojBAZSAKnAjkyOCNug6n+OBl4VW76XjvgSFTdaMcW0Ug==
+ dependencies:
+ "@babel/helper-function-name" "^7.10.4"
+ "@babel/template" "^7.10.4"
+ "@babel/traverse" "^7.10.4"
+ "@babel/types" "^7.10.4"
+
"@babel/helpers@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.1.tgz#a6827b7cb975c9d9cef5fd61d919f60d8844a973"
@@ -676,6 +912,15 @@
"@babel/traverse" "^7.10.1"
"@babel/types" "^7.10.1"
+"@babel/helpers@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.4.tgz#2abeb0d721aff7c0a97376b9e1f6f65d7a475044"
+ integrity sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA==
+ dependencies:
+ "@babel/template" "^7.10.4"
+ "@babel/traverse" "^7.10.4"
+ "@babel/types" "^7.10.4"
+
"@babel/helpers@^7.4.0":
version "7.6.2"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.6.2.tgz#681ffe489ea4dcc55f23ce469e58e59c1c045153"
@@ -721,6 +966,15 @@
chalk "^2.0.0"
js-tokens "^4.0.0"
+"@babel/highlight@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143"
+ integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.10.4"
+ chalk "^2.0.0"
+ js-tokens "^4.0.0"
+
"@babel/parser@^7.10.1", "@babel/parser@^7.10.2", "@babel/parser@^7.7.0":
version "7.10.2"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.2.tgz#871807f10442b92ff97e4783b9b54f6a0ca812d0"
@@ -731,6 +985,11 @@
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.3.tgz#7e71d892b0d6e7d04a1af4c3c79d72c1f10f5315"
integrity sha512-oJtNJCMFdIMwXGmx+KxuaD7i3b8uS7TTFYW/FNG2BT8m+fmGHoiPYoH0Pe3gya07WuFmM5FCDIr1x0irkD/hyA==
+"@babel/parser@^7.10.4", "@babel/parser@^7.11.5":
+ version "7.11.5"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037"
+ integrity sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==
+
"@babel/parser@^7.3.4", "@babel/parser@^7.4.4", "@babel/parser@^7.5.5":
version "7.5.5"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.5.5.tgz#02f077ac8817d3df4a832ef59de67565e71cca4b"
@@ -759,6 +1018,15 @@
"@babel/helper-remap-async-to-generator" "^7.10.3"
"@babel/plugin-syntax-async-generators" "^7.8.0"
+"@babel/plugin-proposal-async-generator-functions@^7.10.4":
+ version "7.10.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz#3491cabf2f7c179ab820606cec27fed15e0e8558"
+ integrity sha512-cNMCVezQbrRGvXJwm9fu/1sJj9bHdGAgKodZdLqOQIpfoH3raqmRPBM17+lh7CzhiKRRBrGtZL9WcjxSoGYUSg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+ "@babel/helper-remap-async-to-generator" "^7.10.4"
+ "@babel/plugin-syntax-async-generators" "^7.8.0"
+
"@babel/plugin-proposal-async-generator-functions@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.2.0.tgz#b289b306669dce4ad20b0252889a15768c9d417e"
@@ -784,6 +1052,14 @@
"@babel/helper-create-class-features-plugin" "^7.10.1"
"@babel/helper-plugin-utils" "^7.10.1"
+"@babel/plugin-proposal-class-properties@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.4.tgz#a33bf632da390a59c7a8c570045d1115cd778807"
+ integrity sha512-vhwkEROxzcHGNu2mzUC0OFFNXdZ4M23ib8aRRcJSsW8BZK9pQMD7QB7csl97NBbgGZO7ZyHUyKDnxzOaP4IrCg==
+ dependencies:
+ "@babel/helper-create-class-features-plugin" "^7.10.4"
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-proposal-class-properties@^7.3.3":
version "7.5.5"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.5.5.tgz#a974cfae1e37c3110e71f3c6a2e48b8e71958cd4"
@@ -809,6 +1085,15 @@
"@babel/helper-plugin-utils" "^7.10.3"
"@babel/plugin-syntax-decorators" "^7.10.1"
+"@babel/plugin-proposal-decorators@^7.10.5":
+ version "7.10.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.10.5.tgz#42898bba478bc4b1ae242a703a953a7ad350ffb4"
+ integrity sha512-Sc5TAQSZuLzgY0664mMDn24Vw2P8g/VhyLyGPaWiHahhgLqeZvcGeyBZOrJW0oSKIK2mvQ22a1ENXBIQLhrEiQ==
+ dependencies:
+ "@babel/helper-create-class-features-plugin" "^7.10.5"
+ "@babel/helper-plugin-utils" "^7.10.4"
+ "@babel/plugin-syntax-decorators" "^7.10.4"
+
"@babel/plugin-proposal-decorators@^7.3.0":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.4.4.tgz#de9b2a1a8ab0196f378e2a82f10b6e2a36f21cc0"
@@ -835,6 +1120,14 @@
"@babel/helper-plugin-utils" "^7.10.1"
"@babel/plugin-syntax-dynamic-import" "^7.8.0"
+"@babel/plugin-proposal-dynamic-import@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.4.tgz#ba57a26cb98b37741e9d5bca1b8b0ddf8291f17e"
+ integrity sha512-up6oID1LeidOOASNXgv/CFbgBqTuKJ0cJjz6An5tWD+NVBNlp3VNSBxv2ZdU7SYl3NxJC7agAQDApZusV6uFwQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+ "@babel/plugin-syntax-dynamic-import" "^7.8.0"
+
"@babel/plugin-proposal-dynamic-import@^7.5.0":
version "7.5.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.5.0.tgz#e532202db4838723691b10a67b8ce509e397c506"
@@ -843,6 +1136,14 @@
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-syntax-dynamic-import" "^7.2.0"
+"@babel/plugin-proposal-export-namespace-from@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.10.4.tgz#570d883b91031637b3e2958eea3c438e62c05f54"
+ integrity sha512-aNdf0LY6/3WXkhh0Fdb6Zk9j1NMD8ovj3F6r0+3j837Pn1S1PdNtcwJ5EG9WkVPNHPxyJDaxMaAOVq4eki0qbg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+ "@babel/plugin-syntax-export-namespace-from" "^7.8.3"
+
"@babel/plugin-proposal-json-strings@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.1.tgz#b1e691ee24c651b5a5e32213222b2379734aff09"
@@ -851,6 +1152,14 @@
"@babel/helper-plugin-utils" "^7.10.1"
"@babel/plugin-syntax-json-strings" "^7.8.0"
+"@babel/plugin-proposal-json-strings@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.4.tgz#593e59c63528160233bd321b1aebe0820c2341db"
+ integrity sha512-fCL7QF0Jo83uy1K0P2YXrfX11tj3lkpN7l4dMv9Y9VkowkhkQDwFHFd8IiwyK5MZjE8UpbgokkgtcReH88Abaw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+ "@babel/plugin-syntax-json-strings" "^7.8.0"
+
"@babel/plugin-proposal-json-strings@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz#568ecc446c6148ae6b267f02551130891e29f317"
@@ -859,6 +1168,14 @@
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-syntax-json-strings" "^7.2.0"
+"@babel/plugin-proposal-logical-assignment-operators@^7.11.0":
+ version "7.11.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.11.0.tgz#9f80e482c03083c87125dee10026b58527ea20c8"
+ integrity sha512-/f8p4z+Auz0Uaf+i8Ekf1iM7wUNLcViFUGiPxKeXvxTSl63B875YPiVdUDdem7hREcI0E0kSpEhS8tF5RphK7Q==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+ "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4"
+
"@babel/plugin-proposal-nullish-coalescing-operator@^7.10.1", "@babel/plugin-proposal-nullish-coalescing-operator@^7.4.4":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.1.tgz#02dca21673842ff2fe763ac253777f235e9bbf78"
@@ -867,6 +1184,14 @@
"@babel/helper-plugin-utils" "^7.10.1"
"@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0"
+"@babel/plugin-proposal-nullish-coalescing-operator@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.4.tgz#02a7e961fc32e6d5b2db0649e01bf80ddee7e04a"
+ integrity sha512-wq5n1M3ZUlHl9sqT2ok1T2/MTt6AXE0e1Lz4WzWBr95LsAZ5qDXe4KnFuauYyEyLiohvXFMdbsOTMyLZs91Zlw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+ "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0"
+
"@babel/plugin-proposal-numeric-separator@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.1.tgz#a9a38bc34f78bdfd981e791c27c6fdcec478c123"
@@ -875,6 +1200,14 @@
"@babel/helper-plugin-utils" "^7.10.1"
"@babel/plugin-syntax-numeric-separator" "^7.10.1"
+"@babel/plugin-proposal-numeric-separator@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.4.tgz#ce1590ff0a65ad12970a609d78855e9a4c1aef06"
+ integrity sha512-73/G7QoRoeNkLZFxsoCCvlg4ezE4eM+57PnOqgaPOozd5myfj7p0muD1mRVJvbUWbOzD+q3No2bWbaKy+DJ8DA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+ "@babel/plugin-syntax-numeric-separator" "^7.10.4"
+
"@babel/plugin-proposal-object-rest-spread@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.10.1.tgz#cba44908ac9f142650b4a65b8aa06bf3478d5fb6"
@@ -893,6 +1226,15 @@
"@babel/plugin-syntax-object-rest-spread" "^7.8.0"
"@babel/plugin-transform-parameters" "^7.10.1"
+"@babel/plugin-proposal-object-rest-spread@^7.11.0":
+ version "7.11.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.11.0.tgz#bd81f95a1f746760ea43b6c2d3d62b11790ad0af"
+ integrity sha512-wzch41N4yztwoRw0ak+37wxwJM2oiIiy6huGCoqkvSTA9acYWcPfn9Y4aJqmFFJ70KTJUu29f3DQ43uJ9HXzEA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+ "@babel/plugin-syntax-object-rest-spread" "^7.8.0"
+ "@babel/plugin-transform-parameters" "^7.10.4"
+
"@babel/plugin-proposal-object-rest-spread@^7.3.2", "@babel/plugin-proposal-object-rest-spread@^7.6.2":
version "7.6.2"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.6.2.tgz#8ffccc8f3a6545e9f78988b6bf4fe881b88e8096"
@@ -925,6 +1267,14 @@
"@babel/helper-plugin-utils" "^7.10.1"
"@babel/plugin-syntax-optional-catch-binding" "^7.8.0"
+"@babel/plugin-proposal-optional-catch-binding@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.4.tgz#31c938309d24a78a49d68fdabffaa863758554dd"
+ integrity sha512-LflT6nPh+GK2MnFiKDyLiqSqVHkQnVf7hdoAvyTnnKj9xB3docGRsdPuxp6qqqW19ifK3xgc9U5/FwrSaCNX5g==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+ "@babel/plugin-syntax-optional-catch-binding" "^7.8.0"
+
"@babel/plugin-proposal-optional-catch-binding@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz#135d81edb68a081e55e56ec48541ece8065c38f5"
@@ -949,6 +1299,15 @@
"@babel/helper-plugin-utils" "^7.10.3"
"@babel/plugin-syntax-optional-chaining" "^7.8.0"
+"@babel/plugin-proposal-optional-chaining@^7.11.0":
+ version "7.11.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.11.0.tgz#de5866d0646f6afdaab8a566382fe3a221755076"
+ integrity sha512-v9fZIu3Y8562RRwhm1BbMRxtqZNFmFA2EG+pT2diuU8PT3H6T/KXoZ54KgYisfOFZHV6PfvAiBIZ9Rcz+/JCxA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+ "@babel/helper-skip-transparent-expression-wrappers" "^7.11.0"
+ "@babel/plugin-syntax-optional-chaining" "^7.8.0"
+
"@babel/plugin-proposal-private-methods@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.1.tgz#ed85e8058ab0fe309c3f448e5e1b73ca89cdb598"
@@ -957,6 +1316,14 @@
"@babel/helper-create-class-features-plugin" "^7.10.1"
"@babel/helper-plugin-utils" "^7.10.1"
+"@babel/plugin-proposal-private-methods@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.4.tgz#b160d972b8fdba5c7d111a145fc8c421fc2a6909"
+ integrity sha512-wh5GJleuI8k3emgTg5KkJK6kHNsGEr0uBTDBuQUBJwckk9xs1ez79ioheEVVxMLyPscB0LfkbVHslQqIzWV6Bw==
+ dependencies:
+ "@babel/helper-create-class-features-plugin" "^7.10.4"
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-proposal-unicode-property-regex@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.1.tgz#dc04feb25e2dd70c12b05d680190e138fa2c0c6f"
@@ -965,6 +1332,14 @@
"@babel/helper-create-regexp-features-plugin" "^7.10.1"
"@babel/helper-plugin-utils" "^7.10.1"
+"@babel/plugin-proposal-unicode-property-regex@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.4.tgz#4483cda53041ce3413b7fe2f00022665ddfaa75d"
+ integrity sha512-H+3fOgPnEXFL9zGYtKQe4IDOPKYlZdF1kqFDQRRb8PK4B8af1vAGK04tF5iQAAsui+mHNBQSAtd2/ndEDe9wuA==
+ dependencies:
+ "@babel/helper-create-regexp-features-plugin" "^7.10.4"
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-proposal-unicode-property-regex@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.4.4.tgz#501ffd9826c0b91da22690720722ac7cb1ca9c78"
@@ -1004,6 +1379,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.10.1"
+"@babel/plugin-syntax-class-properties@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz#6644e6a0baa55a61f9e3231f6c9eeb6ee46c124c"
+ integrity sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-syntax-decorators@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.10.1.tgz#16b869c4beafc9a442565147bda7ce0967bd4f13"
@@ -1011,6 +1393,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.10.1"
+"@babel/plugin-syntax-decorators@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.10.4.tgz#6853085b2c429f9d322d02f5a635018cdeb2360c"
+ integrity sha512-2NaoC6fAk2VMdhY1eerkfHV+lVYC1u8b+jmRJISqANCJlTxYy19HGdIkkQtix2UtkcPuPu+IlDgrVseZnU03bw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-syntax-decorators@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.2.0.tgz#c50b1b957dcc69e4b1127b65e1c33eef61570c1b"
@@ -1032,6 +1421,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.8.0"
+"@babel/plugin-syntax-export-namespace-from@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a"
+ integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.3"
+
"@babel/plugin-syntax-json-strings@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz#72bd13f6ffe1d25938129d2a186b11fd62951470"
@@ -1046,6 +1442,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.8.0"
+"@babel/plugin-syntax-logical-assignment-operators@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699"
+ integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9"
@@ -1060,6 +1463,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.10.1"
+"@babel/plugin-syntax-numeric-separator@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97"
+ integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-syntax-object-rest-spread@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz#3b7a3e733510c57e820b9142a6579ac8b0dfad2e"
@@ -1102,6 +1512,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.10.1"
+"@babel/plugin-syntax-top-level-await@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.4.tgz#4bbeb8917b54fcf768364e0a81f560e33a3ef57d"
+ integrity sha512-ni1brg4lXEmWyafKr0ccFWkJG0CeMt4WV1oyeBW6EFObF4oOHclbkj5cARxAPQyAQ2UTuplJyK4nfkXIMMFvsQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-syntax-typescript@^7.10.1", "@babel/plugin-syntax-typescript@^7.8.3":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.10.1.tgz#5e82bc27bb4202b93b949b029e699db536733810"
@@ -1109,6 +1526,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.10.1"
+"@babel/plugin-syntax-typescript@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.10.4.tgz#2f55e770d3501e83af217d782cb7517d7bb34d25"
+ integrity sha512-oSAEz1YkBCAKr5Yiq8/BNtvSAPwkp/IyUnwZogd8p+F0RuYQQrLeRUzIQhueQTTBy/F+a40uS7OFKxnkRvmvFQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-syntax-typescript@^7.2.0":
version "7.3.3"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.3.3.tgz#a7cc3f66119a9f7ebe2de5383cce193473d65991"
@@ -1123,6 +1547,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.10.1"
+"@babel/plugin-transform-arrow-functions@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz#e22960d77e697c74f41c501d44d73dbf8a6a64cd"
+ integrity sha512-9J/oD1jV0ZCBcgnoFWFq1vJd4msoKb/TCpGNFyyLt0zABdcvgK3aYikZ8HjzB14c26bc7E3Q1yugpwGy2aTPNA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-transform-arrow-functions@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz#9aeafbe4d6ffc6563bf8f8372091628f00779550"
@@ -1139,6 +1570,15 @@
"@babel/helper-plugin-utils" "^7.10.1"
"@babel/helper-remap-async-to-generator" "^7.10.1"
+"@babel/plugin-transform-async-to-generator@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.4.tgz#41a5017e49eb6f3cda9392a51eef29405b245a37"
+ integrity sha512-F6nREOan7J5UXTLsDsZG3DXmZSVofr2tGNwfdrVwkDWHfQckbQXnXSPfD7iO+c/2HGqycwyLST3DnZ16n+cBJQ==
+ dependencies:
+ "@babel/helper-module-imports" "^7.10.4"
+ "@babel/helper-plugin-utils" "^7.10.4"
+ "@babel/helper-remap-async-to-generator" "^7.10.4"
+
"@babel/plugin-transform-async-to-generator@^7.5.0":
version "7.5.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.5.0.tgz#89a3848a0166623b5bc481164b5936ab947e887e"
@@ -1155,6 +1595,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.10.1"
+"@babel/plugin-transform-block-scoped-functions@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.4.tgz#1afa595744f75e43a91af73b0d998ecfe4ebc2e8"
+ integrity sha512-WzXDarQXYYfjaV1szJvN3AD7rZgZzC1JtjJZ8dMHUyiK8mxPRahynp14zzNjU3VkPqPsO38CzxiWO1c9ARZ8JA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-transform-block-scoped-functions@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.2.0.tgz#5d3cc11e8d5ddd752aa64c9148d0db6cb79fd190"
@@ -1170,6 +1617,13 @@
"@babel/helper-plugin-utils" "^7.10.1"
lodash "^4.17.13"
+"@babel/plugin-transform-block-scoping@^7.10.4":
+ version "7.11.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.11.1.tgz#5b7efe98852bef8d652c0b28144cd93a9e4b5215"
+ integrity sha512-00dYeDE0EVEHuuM+26+0w/SCL0BH2Qy7LwHuI4Hi4MH5gkC8/AqMN5uWFJIsoXZrAphiMm1iXzBw6L2T+eA0ew==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-transform-block-scoping@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.4.4.tgz#c13279fabf6b916661531841a23c4b7dae29646d"
@@ -1214,6 +1668,20 @@
"@babel/helper-split-export-declaration" "^7.10.1"
globals "^11.1.0"
+"@babel/plugin-transform-classes@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz#405136af2b3e218bc4a1926228bc917ab1a0adc7"
+ integrity sha512-2oZ9qLjt161dn1ZE0Ms66xBncQH4In8Sqw1YWgBUZuGVJJS5c0OFZXL6dP2MRHrkU/eKhWg8CzFJhRQl50rQxA==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.10.4"
+ "@babel/helper-define-map" "^7.10.4"
+ "@babel/helper-function-name" "^7.10.4"
+ "@babel/helper-optimise-call-expression" "^7.10.4"
+ "@babel/helper-plugin-utils" "^7.10.4"
+ "@babel/helper-replace-supers" "^7.10.4"
+ "@babel/helper-split-export-declaration" "^7.10.4"
+ globals "^11.1.0"
+
"@babel/plugin-transform-classes@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.4.4.tgz#0ce4094cdafd709721076d3b9c38ad31ca715eb6"
@@ -1256,6 +1724,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.10.3"
+"@babel/plugin-transform-computed-properties@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.4.tgz#9ded83a816e82ded28d52d4b4ecbdd810cdfc0eb"
+ integrity sha512-JFwVDXcP/hM/TbyzGq3l/XWGut7p46Z3QvqFMXTfk6/09m7xZHJUN9xHfsv7vqqD4YnfI5ueYdSJtXqqBLyjBw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-transform-computed-properties@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.2.0.tgz#83a7df6a658865b1c8f641d510c6f3af220216da"
@@ -1270,6 +1745,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.10.1"
+"@babel/plugin-transform-destructuring@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.4.tgz#70ddd2b3d1bea83d01509e9bb25ddb3a74fc85e5"
+ integrity sha512-+WmfvyfsyF603iPa6825mq6Qrb7uLjTOsa3XOFzlYcYDHSS4QmpOWOL0NNBY5qMbvrcf3tq0Cw+v4lxswOBpgA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-transform-destructuring@^7.5.0":
version "7.5.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.5.0.tgz#f6c09fdfe3f94516ff074fe877db7bc9ef05855a"
@@ -1292,6 +1774,14 @@
"@babel/helper-create-regexp-features-plugin" "^7.10.1"
"@babel/helper-plugin-utils" "^7.10.1"
+"@babel/plugin-transform-dotall-regex@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.4.tgz#469c2062105c1eb6a040eaf4fac4b488078395ee"
+ integrity sha512-ZEAVvUTCMlMFAbASYSVQoxIbHm2OkG2MseW6bV2JjIygOjdVv8tuxrCTzj1+Rynh7ODb8GivUy7dzEXzEhuPaA==
+ dependencies:
+ "@babel/helper-create-regexp-features-plugin" "^7.10.4"
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-transform-dotall-regex@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.4.4.tgz#361a148bc951444312c69446d76ed1ea8e4450c3"
@@ -1317,6 +1807,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.10.1"
+"@babel/plugin-transform-duplicate-keys@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.4.tgz#697e50c9fee14380fe843d1f306b295617431e47"
+ integrity sha512-GL0/fJnmgMclHiBTTWXNlYjYsA7rDrtsazHG6mglaGSTh0KsrW04qml+Bbz9FL0LcJIRwBWL5ZqlNHKTkU3xAA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-transform-duplicate-keys@^7.5.0":
version "7.5.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.5.0.tgz#c5dbf5106bf84cdf691222c0974c12b1df931853"
@@ -1332,6 +1829,14 @@
"@babel/helper-builder-binary-assignment-operator-visitor" "^7.10.1"
"@babel/helper-plugin-utils" "^7.10.1"
+"@babel/plugin-transform-exponentiation-operator@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.4.tgz#5ae338c57f8cf4001bdb35607ae66b92d665af2e"
+ integrity sha512-S5HgLVgkBcRdyQAHbKj+7KyuWx8C6t5oETmUuwz1pt3WTWJhsUV0WIIXuVvfXMxl/QQyHKlSCNNtaIamG8fysw==
+ dependencies:
+ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.10.4"
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-transform-exponentiation-operator@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.2.0.tgz#a63868289e5b4007f7054d46491af51435766008"
@@ -1347,6 +1852,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.10.1"
+"@babel/plugin-transform-for-of@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.4.tgz#c08892e8819d3a5db29031b115af511dbbfebae9"
+ integrity sha512-ItdQfAzu9AlEqmusA/65TqJ79eRcgGmpPPFvBnGILXZH975G0LNjP1yjHvGgfuCxqrPPueXOPe+FsvxmxKiHHQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-transform-for-of@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.4.4.tgz#0267fc735e24c808ba173866c6c4d1440fc3c556"
@@ -1362,6 +1874,14 @@
"@babel/helper-function-name" "^7.10.1"
"@babel/helper-plugin-utils" "^7.10.1"
+"@babel/plugin-transform-function-name@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.4.tgz#6a467880e0fc9638514ba369111811ddbe2644b7"
+ integrity sha512-OcDCq2y5+E0dVD5MagT5X+yTRbcvFjDI2ZVAottGH6tzqjx/LKpgkUepu3hp/u4tZBzxxpNGwLsAvGBvQ2mJzg==
+ dependencies:
+ "@babel/helper-function-name" "^7.10.4"
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-transform-function-name@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.4.4.tgz#e1436116abb0610c2259094848754ac5230922ad"
@@ -1377,6 +1897,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.10.1"
+"@babel/plugin-transform-literals@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.4.tgz#9f42ba0841100a135f22712d0e391c462f571f3c"
+ integrity sha512-Xd/dFSTEVuUWnyZiMu76/InZxLTYilOSr1UlHV+p115Z/Le2Fi1KXkJUYz0b42DfndostYlPub3m8ZTQlMaiqQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-transform-literals@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.2.0.tgz#690353e81f9267dad4fd8cfd77eafa86aba53ea1"
@@ -1391,6 +1918,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.10.1"
+"@babel/plugin-transform-member-expression-literals@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.4.tgz#b1ec44fcf195afcb8db2c62cd8e551c881baf8b7"
+ integrity sha512-0bFOvPyAoTBhtcJLr9VcwZqKmSjFml1iVxvPL0ReomGU53CX53HsM4h2SzckNdkQcHox1bpAqzxBI1Y09LlBSw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-transform-member-expression-literals@^7.2.0", "@babel/plugin-transform-modules-amd@^7.0.0":
name "@babel/plugin-transform-member-expression-literals"
version "7.2.0"
@@ -1408,6 +1942,15 @@
"@babel/helper-plugin-utils" "^7.10.1"
babel-plugin-dynamic-import-node "^2.3.3"
+"@babel/plugin-transform-modules-amd@^7.10.4", "@babel/plugin-transform-modules-amd@^7.10.5":
+ version "7.10.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.5.tgz#1b9cddaf05d9e88b3aad339cb3e445c4f020a9b1"
+ integrity sha512-elm5uruNio7CTLFItVC/rIzKLfQ17+fX7EVz5W0TMgIHFo1zY0Ozzx+lgwhL4plzl8OzVn6Qasx5DeEFyoNiRw==
+ dependencies:
+ "@babel/helper-module-transforms" "^7.10.5"
+ "@babel/helper-plugin-utils" "^7.10.4"
+ babel-plugin-dynamic-import-node "^2.3.3"
+
"@babel/plugin-transform-modules-amd@^7.5.0":
version "7.5.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.5.0.tgz#ef00435d46da0a5961aa728a1d2ecff063e4fb91"
@@ -1427,6 +1970,16 @@
"@babel/helper-simple-access" "^7.10.1"
babel-plugin-dynamic-import-node "^2.3.3"
+"@babel/plugin-transform-modules-commonjs@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.4.tgz#66667c3eeda1ebf7896d41f1f16b17105a2fbca0"
+ integrity sha512-Xj7Uq5o80HDLlW64rVfDBhao6OX89HKUmb+9vWYaLXBZOma4gA6tw4Ni1O5qVDoZWUV0fxMYA0aYzOawz0l+1w==
+ dependencies:
+ "@babel/helper-module-transforms" "^7.10.4"
+ "@babel/helper-plugin-utils" "^7.10.4"
+ "@babel/helper-simple-access" "^7.10.4"
+ babel-plugin-dynamic-import-node "^2.3.3"
+
"@babel/plugin-transform-modules-commonjs@^7.5.0":
version "7.5.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.5.0.tgz#425127e6045231360858eeaa47a71d75eded7a74"
@@ -1467,6 +2020,16 @@
"@babel/helper-plugin-utils" "^7.10.3"
babel-plugin-dynamic-import-node "^2.3.3"
+"@babel/plugin-transform-modules-systemjs@^7.10.4":
+ version "7.10.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.5.tgz#6270099c854066681bae9e05f87e1b9cadbe8c85"
+ integrity sha512-f4RLO/OL14/FP1AEbcsWMzpbUz6tssRaeQg11RH1BP/XnPpRoVwgeYViMFacnkaw4k4wjRSjn3ip1Uw9TaXuMw==
+ dependencies:
+ "@babel/helper-hoist-variables" "^7.10.4"
+ "@babel/helper-module-transforms" "^7.10.5"
+ "@babel/helper-plugin-utils" "^7.10.4"
+ babel-plugin-dynamic-import-node "^2.3.3"
+
"@babel/plugin-transform-modules-systemjs@^7.5.0":
version "7.5.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.5.0.tgz#e75266a13ef94202db2a0620977756f51d52d249"
@@ -1484,6 +2047,14 @@
"@babel/helper-module-transforms" "^7.10.1"
"@babel/helper-plugin-utils" "^7.10.1"
+"@babel/plugin-transform-modules-umd@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.4.tgz#9a8481fe81b824654b3a0b65da3df89f3d21839e"
+ integrity sha512-mohW5q3uAEt8T45YT7Qc5ws6mWgJAaL/8BfWD9Dodo1A3RKWli8wTS+WiQ/knF+tXlPirW/1/MqzzGfCExKECA==
+ dependencies:
+ "@babel/helper-module-transforms" "^7.10.4"
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-transform-modules-umd@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.2.0.tgz#7678ce75169f0877b8eb2235538c074268dd01ae"
@@ -1499,6 +2070,13 @@
dependencies:
"@babel/helper-create-regexp-features-plugin" "^7.8.3"
+"@babel/plugin-transform-named-capturing-groups-regex@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz#78b4d978810b6f3bcf03f9e318f2fc0ed41aecb6"
+ integrity sha512-V6LuOnD31kTkxQPhKiVYzYC/Jgdq53irJC/xBSmqcNcqFGV+PER4l6rU5SH2Vl7bH9mLDHcc0+l9HUOe4RNGKA==
+ dependencies:
+ "@babel/helper-create-regexp-features-plugin" "^7.10.4"
+
"@babel/plugin-transform-named-capturing-groups-regex@^7.4.5":
version "7.4.5"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.4.5.tgz#9d269fd28a370258199b4294736813a60bbdd106"
@@ -1527,6 +2105,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.10.1"
+"@babel/plugin-transform-new-target@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.4.tgz#9097d753cb7b024cb7381a3b2e52e9513a9c6888"
+ integrity sha512-YXwWUDAH/J6dlfwqlWsztI2Puz1NtUAubXhOPLQ5gjR/qmQ5U96DY4FQO8At33JN4XPBhrjB8I4eMmLROjjLjw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-transform-new-target@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.4.4.tgz#18d120438b0cc9ee95a47f2c72bc9768fbed60a5"
@@ -1549,6 +2134,14 @@
"@babel/helper-plugin-utils" "^7.10.1"
"@babel/helper-replace-supers" "^7.10.1"
+"@babel/plugin-transform-object-super@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.4.tgz#d7146c4d139433e7a6526f888c667e314a093894"
+ integrity sha512-5iTw0JkdRdJvr7sY0vHqTpnruUpTea32JHmq/atIWqsnNussbRzjEDyWep8UNztt1B5IusBYg8Irb0bLbiEBCQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+ "@babel/helper-replace-supers" "^7.10.4"
+
"@babel/plugin-transform-object-super@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.2.0.tgz#b35d4c10f56bab5d650047dad0f1d8e8814b6598"
@@ -1573,6 +2166,14 @@
"@babel/helper-get-function-arity" "^7.10.1"
"@babel/helper-plugin-utils" "^7.10.1"
+"@babel/plugin-transform-parameters@^7.10.4":
+ version "7.10.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.5.tgz#59d339d58d0b1950435f4043e74e2510005e2c4a"
+ integrity sha512-xPHwUj5RdFV8l1wuYiu5S9fqWGM2DrYc24TMvUiRrPVm+SM3XeqU9BcokQX/kEUe+p2RBwy+yoiR1w/Blq6ubw==
+ dependencies:
+ "@babel/helper-get-function-arity" "^7.10.4"
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-transform-parameters@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.4.4.tgz#7556cf03f318bd2719fe4c922d2d808be5571e16"
@@ -1589,6 +2190,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.10.1"
+"@babel/plugin-transform-property-literals@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.4.tgz#f6fe54b6590352298785b83edd815d214c42e3c0"
+ integrity sha512-ofsAcKiUxQ8TY4sScgsGeR2vJIsfrzqvFb9GvJ5UdXDzl+MyYCaBj/FGzXuv7qE0aJcjWMILny1epqelnFlz8g==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-transform-property-literals@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.2.0.tgz#03e33f653f5b25c4eb572c98b9485055b389e905"
@@ -1618,6 +2226,13 @@
dependencies:
regenerator-transform "^0.14.2"
+"@babel/plugin-transform-regenerator@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz#2015e59d839074e76838de2159db421966fd8b63"
+ integrity sha512-3thAHwtor39A7C04XucbMg17RcZ3Qppfxr22wYzZNcVIkPHfpM9J0SO8zuCV6SZa265kxBJSrfKTvDCYqBFXGw==
+ dependencies:
+ regenerator-transform "^0.14.2"
+
"@babel/plugin-transform-regenerator@^7.4.5":
version "7.4.5"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.4.5.tgz#629dc82512c55cee01341fb27bdfcb210354680f"
@@ -1632,6 +2247,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.10.1"
+"@babel/plugin-transform-reserved-words@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.4.tgz#8f2682bcdcef9ed327e1b0861585d7013f8a54dd"
+ integrity sha512-hGsw1O6Rew1fkFbDImZIEqA8GoidwTAilwCyWqLBM9f+e/u/sQMQu7uX6dyokfOayRuuVfKOW4O7HvaBWM+JlQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-transform-reserved-words@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.2.0.tgz#4792af87c998a49367597d07fedf02636d2e1634"
@@ -1649,6 +2271,16 @@
resolve "^1.8.1"
semver "^5.5.1"
+"@babel/plugin-transform-runtime@^7.11.0":
+ version "7.11.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.11.5.tgz#f108bc8e0cf33c37da031c097d1df470b3a293fc"
+ integrity sha512-9aIoee+EhjySZ6vY5hnLjigHzunBlscx9ANKutkeWTJTx6m5Rbq6Ic01tLvO54lSusR+BxV7u4UDdCmXv5aagg==
+ dependencies:
+ "@babel/helper-module-imports" "^7.10.4"
+ "@babel/helper-plugin-utils" "^7.10.4"
+ resolve "^1.8.1"
+ semver "^5.5.1"
+
"@babel/plugin-transform-runtime@^7.2.0":
version "7.5.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.5.0.tgz#45242c2c9281158c5f06d25beebac63e498a284e"
@@ -1676,6 +2308,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.10.1"
+"@babel/plugin-transform-shorthand-properties@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.4.tgz#9fd25ec5cdd555bb7f473e5e6ee1c971eede4dd6"
+ integrity sha512-AC2K/t7o07KeTIxMoHneyX90v3zkm5cjHJEokrPEAGEy3UCp8sLKfnfOIGdZ194fyN4wfX/zZUWT9trJZ0qc+Q==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-transform-shorthand-properties@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz#6333aee2f8d6ee7e28615457298934a3b46198f0"
@@ -1690,6 +2329,14 @@
dependencies:
"@babel/helper-plugin-utils" "^7.10.1"
+"@babel/plugin-transform-spread@^7.11.0":
+ version "7.11.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.11.0.tgz#fa84d300f5e4f57752fe41a6d1b3c554f13f17cc"
+ integrity sha512-UwQYGOqIdQJe4aWNyS7noqAnN2VbaczPLiEtln+zPowRNlD+79w3oi2TWfYe0eZgd+gjZCbsydN7lzWysDt+gw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+ "@babel/helper-skip-transparent-expression-wrappers" "^7.11.0"
+
"@babel/plugin-transform-spread@^7.2.0":
version "7.2.2"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.2.2.tgz#3103a9abe22f742b6d406ecd3cd49b774919b406"
@@ -1712,6 +2359,14 @@
"@babel/helper-plugin-utils" "^7.10.1"
"@babel/helper-regex" "^7.10.1"
+"@babel/plugin-transform-sticky-regex@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.4.tgz#8f3889ee8657581130a29d9cc91d7c73b7c4a28d"
+ integrity sha512-Ddy3QZfIbEV0VYcVtFDCjeE4xwVTJWTmUtorAJkn6u/92Z/nWJNV+mILyqHKrUxXYKA2EoCilgoPePymKL4DvQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+ "@babel/helper-regex" "^7.10.4"
+
"@babel/plugin-transform-sticky-regex@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.2.0.tgz#a1e454b5995560a9c1e0d537dfc15061fd2687e1"
@@ -1736,6 +2391,14 @@
"@babel/helper-annotate-as-pure" "^7.10.1"
"@babel/helper-plugin-utils" "^7.10.3"
+"@babel/plugin-transform-template-literals@^7.10.4":
+ version "7.10.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.5.tgz#78bc5d626a6642db3312d9d0f001f5e7639fde8c"
+ integrity sha512-V/lnPGIb+KT12OQikDvgSuesRX14ck5FfJXt6+tXhdkJ+Vsd0lDCVtF6jcB4rNClYFzaB2jusZ+lNISDk2mMMw==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.10.4"
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-transform-template-literals@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.4.4.tgz#9d28fea7bbce637fb7612a0750989d8321d4bcb0"
@@ -1751,6 +2414,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.10.1"
+"@babel/plugin-transform-typeof-symbol@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.4.tgz#9509f1a7eec31c4edbffe137c16cc33ff0bc5bfc"
+ integrity sha512-QqNgYwuuW0y0H+kUE/GWSR45t/ccRhe14Fs/4ZRouNNQsyd4o3PG4OtHiIrepbM2WKUBDAXKCAK/Lk4VhzTaGA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-transform-typeof-symbol@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.2.0.tgz#117d2bcec2fbf64b4b59d1f9819894682d29f2b2"
@@ -1767,6 +2437,15 @@
"@babel/helper-plugin-utils" "^7.10.3"
"@babel/plugin-syntax-typescript" "^7.10.1"
+"@babel/plugin-transform-typescript@^7.11.0":
+ version "7.11.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.11.0.tgz#2b4879676af37342ebb278216dd090ac67f13abb"
+ integrity sha512-edJsNzTtvb3MaXQwj8403B7mZoGu9ElDJQZOKjGUnvilquxBA3IQoEIOvkX/1O8xfAsnHS/oQhe2w/IXrr+w0w==
+ dependencies:
+ "@babel/helper-create-class-features-plugin" "^7.10.5"
+ "@babel/helper-plugin-utils" "^7.10.4"
+ "@babel/plugin-syntax-typescript" "^7.10.4"
+
"@babel/plugin-transform-typescript@^7.9.0":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.10.1.tgz#2c54daea231f602468686d9faa76f182a94507a6"
@@ -1809,6 +2488,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.10.1"
+"@babel/plugin-transform-unicode-escapes@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.4.tgz#feae523391c7651ddac115dae0a9d06857892007"
+ integrity sha512-y5XJ9waMti2J+e7ij20e+aH+fho7Wb7W8rNuu72aKRwCHFqQdhkdU2lo3uZ9tQuboEJcUFayXdARhcxLQ3+6Fg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-transform-unicode-regex@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.1.tgz#6b58f2aea7b68df37ac5025d9c88752443a6b43f"
@@ -1817,6 +2503,14 @@
"@babel/helper-create-regexp-features-plugin" "^7.10.1"
"@babel/helper-plugin-utils" "^7.10.1"
+"@babel/plugin-transform-unicode-regex@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.4.tgz#e56d71f9282fac6db09c82742055576d5e6d80a8"
+ integrity sha512-wNfsc4s8N2qnIwpO/WP2ZiSyjfpTamT2C9V9FDH/Ljub9zw6P3SjkXcFmc0RQUt96k2fmIvtla2MMjgTwIAC+A==
+ dependencies:
+ "@babel/helper-create-regexp-features-plugin" "^7.10.4"
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-transform-unicode-regex@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.4.4.tgz#ab4634bb4f14d36728bf5978322b35587787970f"
@@ -1851,6 +2545,14 @@
core-js "^2.6.5"
regenerator-runtime "^0.13.4"
+"@babel/polyfill@^7.10.4":
+ version "7.11.5"
+ resolved "https://registry.yarnpkg.com/@babel/polyfill/-/polyfill-7.11.5.tgz#df550b2ec53abbc2ed599367ec59e64c7a707bb5"
+ integrity sha512-FunXnE0Sgpd61pKSj2OSOs1D44rKTD3pGOfGilZ6LGrrIH0QEtJlTjqOqdF8Bs98JmjfGhni2BBkTfv9KcKJ9g==
+ dependencies:
+ core-js "^2.6.5"
+ regenerator-runtime "^0.13.4"
+
"@babel/preset-env@^7.0.0":
version "7.5.4"
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.5.4.tgz#64bc15041a3cbb0798930319917e70fcca57713d"
@@ -1977,6 +2679,80 @@
levenary "^1.1.1"
semver "^5.5.0"
+"@babel/preset-env@^7.11.0":
+ version "7.11.5"
+ resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.11.5.tgz#18cb4b9379e3e92ffea92c07471a99a2914e4272"
+ integrity sha512-kXqmW1jVcnB2cdueV+fyBM8estd5mlNfaQi6lwLgRwCby4edpavgbFhiBNjmWA3JpB/yZGSISa7Srf+TwxDQoA==
+ dependencies:
+ "@babel/compat-data" "^7.11.0"
+ "@babel/helper-compilation-targets" "^7.10.4"
+ "@babel/helper-module-imports" "^7.10.4"
+ "@babel/helper-plugin-utils" "^7.10.4"
+ "@babel/plugin-proposal-async-generator-functions" "^7.10.4"
+ "@babel/plugin-proposal-class-properties" "^7.10.4"
+ "@babel/plugin-proposal-dynamic-import" "^7.10.4"
+ "@babel/plugin-proposal-export-namespace-from" "^7.10.4"
+ "@babel/plugin-proposal-json-strings" "^7.10.4"
+ "@babel/plugin-proposal-logical-assignment-operators" "^7.11.0"
+ "@babel/plugin-proposal-nullish-coalescing-operator" "^7.10.4"
+ "@babel/plugin-proposal-numeric-separator" "^7.10.4"
+ "@babel/plugin-proposal-object-rest-spread" "^7.11.0"
+ "@babel/plugin-proposal-optional-catch-binding" "^7.10.4"
+ "@babel/plugin-proposal-optional-chaining" "^7.11.0"
+ "@babel/plugin-proposal-private-methods" "^7.10.4"
+ "@babel/plugin-proposal-unicode-property-regex" "^7.10.4"
+ "@babel/plugin-syntax-async-generators" "^7.8.0"
+ "@babel/plugin-syntax-class-properties" "^7.10.4"
+ "@babel/plugin-syntax-dynamic-import" "^7.8.0"
+ "@babel/plugin-syntax-export-namespace-from" "^7.8.3"
+ "@babel/plugin-syntax-json-strings" "^7.8.0"
+ "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4"
+ "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0"
+ "@babel/plugin-syntax-numeric-separator" "^7.10.4"
+ "@babel/plugin-syntax-object-rest-spread" "^7.8.0"
+ "@babel/plugin-syntax-optional-catch-binding" "^7.8.0"
+ "@babel/plugin-syntax-optional-chaining" "^7.8.0"
+ "@babel/plugin-syntax-top-level-await" "^7.10.4"
+ "@babel/plugin-transform-arrow-functions" "^7.10.4"
+ "@babel/plugin-transform-async-to-generator" "^7.10.4"
+ "@babel/plugin-transform-block-scoped-functions" "^7.10.4"
+ "@babel/plugin-transform-block-scoping" "^7.10.4"
+ "@babel/plugin-transform-classes" "^7.10.4"
+ "@babel/plugin-transform-computed-properties" "^7.10.4"
+ "@babel/plugin-transform-destructuring" "^7.10.4"
+ "@babel/plugin-transform-dotall-regex" "^7.10.4"
+ "@babel/plugin-transform-duplicate-keys" "^7.10.4"
+ "@babel/plugin-transform-exponentiation-operator" "^7.10.4"
+ "@babel/plugin-transform-for-of" "^7.10.4"
+ "@babel/plugin-transform-function-name" "^7.10.4"
+ "@babel/plugin-transform-literals" "^7.10.4"
+ "@babel/plugin-transform-member-expression-literals" "^7.10.4"
+ "@babel/plugin-transform-modules-amd" "^7.10.4"
+ "@babel/plugin-transform-modules-commonjs" "^7.10.4"
+ "@babel/plugin-transform-modules-systemjs" "^7.10.4"
+ "@babel/plugin-transform-modules-umd" "^7.10.4"
+ "@babel/plugin-transform-named-capturing-groups-regex" "^7.10.4"
+ "@babel/plugin-transform-new-target" "^7.10.4"
+ "@babel/plugin-transform-object-super" "^7.10.4"
+ "@babel/plugin-transform-parameters" "^7.10.4"
+ "@babel/plugin-transform-property-literals" "^7.10.4"
+ "@babel/plugin-transform-regenerator" "^7.10.4"
+ "@babel/plugin-transform-reserved-words" "^7.10.4"
+ "@babel/plugin-transform-shorthand-properties" "^7.10.4"
+ "@babel/plugin-transform-spread" "^7.11.0"
+ "@babel/plugin-transform-sticky-regex" "^7.10.4"
+ "@babel/plugin-transform-template-literals" "^7.10.4"
+ "@babel/plugin-transform-typeof-symbol" "^7.10.4"
+ "@babel/plugin-transform-unicode-escapes" "^7.10.4"
+ "@babel/plugin-transform-unicode-regex" "^7.10.4"
+ "@babel/preset-modules" "^0.1.3"
+ "@babel/types" "^7.11.5"
+ browserslist "^4.12.0"
+ core-js-compat "^3.6.2"
+ invariant "^2.2.2"
+ levenary "^1.1.1"
+ semver "^5.5.0"
+
"@babel/preset-env@^7.4.5":
version "7.6.2"
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.6.2.tgz#abbb3ed785c7fe4220d4c82a53621d71fc0c75d3"
@@ -2143,6 +2919,13 @@
dependencies:
regenerator-runtime "^0.13.4"
+"@babel/runtime@^7.11.0":
+ version "7.11.2"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736"
+ integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==
+ dependencies:
+ regenerator-runtime "^0.13.4"
+
"@babel/runtime@^7.2.0":
version "7.5.4"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.4.tgz#cb7d1ad7c6d65676e66b47186577930465b5271b"
@@ -2198,6 +2981,15 @@
"@babel/parser" "^7.10.3"
"@babel/types" "^7.10.3"
+"@babel/template@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278"
+ integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==
+ dependencies:
+ "@babel/code-frame" "^7.10.4"
+ "@babel/parser" "^7.10.4"
+ "@babel/types" "^7.10.4"
+
"@babel/template@^7.4.0", "@babel/template@^7.6.0":
version "7.6.0"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.6.0.tgz#7f0159c7f5012230dad64cca42ec9bdb5c9536e6"
@@ -2252,6 +3044,21 @@
globals "^11.1.0"
lodash "^4.17.13"
+"@babel/traverse@^7.10.4", "@babel/traverse@^7.11.5":
+ version "7.11.5"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.11.5.tgz#be777b93b518eb6d76ee2e1ea1d143daa11e61c3"
+ integrity sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ==
+ dependencies:
+ "@babel/code-frame" "^7.10.4"
+ "@babel/generator" "^7.11.5"
+ "@babel/helper-function-name" "^7.10.4"
+ "@babel/helper-split-export-declaration" "^7.11.0"
+ "@babel/parser" "^7.11.5"
+ "@babel/types" "^7.11.5"
+ debug "^4.1.0"
+ globals "^11.1.0"
+ lodash "^4.17.19"
+
"@babel/traverse@^7.2.4", "@babel/traverse@^7.3.4", "@babel/traverse@^7.5.5":
version "7.5.5"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.5.5.tgz#f664f8f368ed32988cd648da9f72d5ca70f165bb"
@@ -2309,6 +3116,15 @@
lodash "^4.17.13"
to-fast-properties "^2.0.0"
+"@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.11.0", "@babel/types@^7.11.5":
+ version "7.11.5"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.5.tgz#d9de577d01252d77c6800cee039ee64faf75662d"
+ integrity sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.10.4"
+ lodash "^4.17.19"
+ to-fast-properties "^2.0.0"
+
"@babel/types@^7.3.2", "@babel/types@^7.3.4", "@babel/types@^7.4.4", "@babel/types@^7.5.5":
version "7.5.5"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.5.5.tgz#97b9f728e182785909aa4ab56264f090a028d18a"
@@ -4601,6 +5417,13 @@ babel-plugin-ember-modules-api-polyfill@^2.8.0, babel-plugin-ember-modules-api-p
dependencies:
ember-rfc176-data "^0.3.9"
+babel-plugin-ember-modules-api-polyfill@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-ember-modules-api-polyfill/-/babel-plugin-ember-modules-api-polyfill-3.1.1.tgz#c6e9ede43b64c4e36512f260e42e829b071d9b4f"
+ integrity sha512-hRTnr59fJ6cIiSiSgQLM9QRiVv/RrBAYRYggCPQDj4dvYhOWZeoX6e+1jFY1qC3tJnSDuMWu3OrDciSIi1MJ0A==
+ dependencies:
+ ember-rfc176-data "^0.3.15"
+
babel-plugin-emotion@^10.0.14, babel-plugin-emotion@^10.0.17:
version "10.0.21"
resolved "https://registry.yarnpkg.com/babel-plugin-emotion/-/babel-plugin-emotion-10.0.21.tgz#9ebeb12edeea3e60a5476b0e07c9868605e65968"
@@ -5538,6 +6361,24 @@ broccoli-babel-transpiler@^7.5.0:
rsvp "^4.8.4"
workerpool "^3.1.1"
+broccoli-babel-transpiler@^7.7.0:
+ version "7.7.0"
+ resolved "https://registry.yarnpkg.com/broccoli-babel-transpiler/-/broccoli-babel-transpiler-7.7.0.tgz#271d401e713bfd338d5ef0435d3c4c68f6eddd2a"
+ integrity sha512-U8Cmnv0/AcQKehiIVi6UDzqq3jqhAEbY9CvOW5vdeNRmYhFpK6bXPmVczS/nUz5g4KsPc/FdnC3zbU6yVf4e7w==
+ dependencies:
+ "@babel/core" "^7.11.0"
+ "@babel/polyfill" "^7.10.4"
+ broccoli-funnel "^2.0.2"
+ broccoli-merge-trees "^3.0.2"
+ broccoli-persistent-filter "^2.2.1"
+ clone "^2.1.2"
+ hash-for-dep "^1.4.7"
+ heimdalljs "^0.2.1"
+ heimdalljs-logger "^0.1.9"
+ json-stable-stringify "^1.0.1"
+ rsvp "^4.8.4"
+ workerpool "^3.1.1"
+
broccoli-builder@^0.18.14:
version "0.18.14"
resolved "https://registry.yarnpkg.com/broccoli-builder/-/broccoli-builder-0.18.14.tgz#4b79e2f844de11a4e1b816c3f49c6df4776c312d"
@@ -7471,6 +8312,11 @@ d3-array@^1.2.0:
resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.4.tgz#635ce4d5eea759f6f605863dbcfc30edc737f71f"
integrity sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==
+d3-array@^2.1.0:
+ version "2.8.0"
+ resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-2.8.0.tgz#f76e10ad47f1f4f75f33db5fc322eb9ffde5ef23"
+ integrity sha512-6V272gsOeg7+9pTW1jSYOR1QE37g95I3my1hBmY+vOUNHRrk9yt4OTz/gK7PMkVAVDrYYq4mq3grTiZ8iJdNIw==
+
d3-axis@^1.0.0:
version "1.0.12"
resolved "https://registry.yarnpkg.com/d3-axis/-/d3-axis-1.0.12.tgz#cdf20ba210cfbb43795af33756886fb3638daac9"
@@ -8331,6 +9177,38 @@ ember-cli-babel@^7.11.1:
rimraf "^3.0.1"
semver "^5.5.0"
+ember-cli-babel@^7.22.1:
+ version "7.22.1"
+ resolved "https://registry.yarnpkg.com/ember-cli-babel/-/ember-cli-babel-7.22.1.tgz#cad28b89cf0e184c93b863d09bc5ba4ce1d2e453"
+ integrity sha512-kCT8WbC1AYFtyOpU23ESm22a+gL6fWv8Nzwe8QFQ5u0piJzM9MEudfbjADEaoyKTrjMQTDsrWwEf3yjggDsOng==
+ dependencies:
+ "@babel/core" "^7.11.0"
+ "@babel/helper-compilation-targets" "^7.10.4"
+ "@babel/plugin-proposal-class-properties" "^7.10.4"
+ "@babel/plugin-proposal-decorators" "^7.10.5"
+ "@babel/plugin-transform-modules-amd" "^7.10.5"
+ "@babel/plugin-transform-runtime" "^7.11.0"
+ "@babel/plugin-transform-typescript" "^7.11.0"
+ "@babel/polyfill" "^7.10.4"
+ "@babel/preset-env" "^7.11.0"
+ "@babel/runtime" "^7.11.0"
+ amd-name-resolver "^1.2.1"
+ babel-plugin-debug-macros "^0.3.3"
+ babel-plugin-ember-data-packages-polyfill "^0.1.2"
+ babel-plugin-ember-modules-api-polyfill "^3.1.1"
+ babel-plugin-module-resolver "^3.1.1"
+ broccoli-babel-transpiler "^7.7.0"
+ broccoli-debug "^0.6.4"
+ broccoli-funnel "^2.0.1"
+ broccoli-source "^1.1.0"
+ clone "^2.1.2"
+ ember-cli-babel-plugin-helpers "^1.1.0"
+ ember-cli-version-checker "^4.1.0"
+ ensure-posix-path "^1.0.2"
+ fixturify-project "^1.10.0"
+ rimraf "^3.0.1"
+ semver "^5.5.0"
+
ember-cli-clipboard@^0.13.0:
version "0.13.0"
resolved "https://registry.yarnpkg.com/ember-cli-clipboard/-/ember-cli-clipboard-0.13.0.tgz#47d3de3aec09987409c162cbff36f966a2c138b7"
@@ -8655,7 +9533,7 @@ ember-cli-uglify@^3.0.0:
broccoli-uglify-sourcemap "^3.1.0"
lodash.defaultsdeep "^4.6.0"
-ember-cli-version-checker@5.1.1:
+ember-cli-version-checker@5.1.1, ember-cli-version-checker@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/ember-cli-version-checker/-/ember-cli-version-checker-5.1.1.tgz#3185c526c14671609cbd22ab0d0925787fc84f3d"
integrity sha512-YziSW1MgOuVdJSyUY2CKSC4vXrGQIHF6FgygHkJOxYGjZNQYwf5MK0sbliKatvJf7kzDSnXs+r8JLrD74W/A8A==
@@ -8795,7 +9673,7 @@ ember-compatibility-helpers@^1.1.1, ember-compatibility-helpers@^1.2.0:
ember-cli-version-checker "^2.1.1"
semver "^5.4.1"
-ember-compatibility-helpers@^1.1.2:
+ember-compatibility-helpers@^1.1.2, ember-compatibility-helpers@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/ember-compatibility-helpers/-/ember-compatibility-helpers-1.2.1.tgz#87c92c4303f990ff455c28ca39fb3ee11441aa16"
integrity sha512-6wzYvnhg1ihQUT5yGqnLtleq3Nv5KNv79WhrEuNU9SwR4uIxCO+KpyC7r3d5VI0EM7/Nmv9Nd0yTkzmTMdVG1A==
@@ -8874,6 +9752,15 @@ ember-decorators@^6.1.1:
"@ember-decorators/object" "^6.1.1"
ember-cli-babel "^7.7.3"
+ember-destroyable-polyfill@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/ember-destroyable-polyfill/-/ember-destroyable-polyfill-2.0.1.tgz#391cd95a99debaf24148ce953054008d00f151a6"
+ integrity sha512-hyK+/GPWOIxM1vxnlVMknNl9E5CAFVbcxi8zPiM0vCRwHiFS8Wuj7PfthZ1/OFitNNv7ITTeU8hxqvOZVsrbnQ==
+ dependencies:
+ ember-cli-babel "^7.22.1"
+ ember-cli-version-checker "^5.1.1"
+ ember-compatibility-helpers "^1.2.1"
+
ember-element-helper@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/ember-element-helper/-/ember-element-helper-0.2.0.tgz#eacdf4d8507d6708812623206e24ad37bad487e7"
@@ -8993,7 +9880,7 @@ ember-maybe-in-element@^0.4.0:
dependencies:
ember-cli-babel "^7.1.0"
-ember-modifier-manager-polyfill@^1.1.0:
+ember-modifier-manager-polyfill@^1.1.0, ember-modifier-manager-polyfill@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/ember-modifier-manager-polyfill/-/ember-modifier-manager-polyfill-1.2.0.tgz#cf4444e11a42ac84f5c8badd85e635df57565dda"
integrity sha512-bnaKF1LLKMkBNeDoetvIJ4vhwRPKIIumWr6dbVuW6W6p4QV8ZiO+GdF8J7mxDNlog9CeL9Z/7wam4YS86G8BYA==
@@ -9002,6 +9889,18 @@ ember-modifier-manager-polyfill@^1.1.0:
ember-cli-version-checker "^2.1.2"
ember-compatibility-helpers "^1.2.0"
+ember-modifier@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/ember-modifier/-/ember-modifier-2.1.0.tgz#99d85995caad8789220dc3208fb5ded45647dccf"
+ integrity sha512-tVmRcEloYg8AZHheEMhBhzX64r7n6AFLXG69L/jiHePvQzet9mjV18YiIPStQf+fdjTAO25S6yzNPDP3zQjWtQ==
+ dependencies:
+ ember-cli-babel "^7.22.1"
+ ember-cli-normalize-entity-name "^1.0.0"
+ ember-cli-string-utils "^1.1.0"
+ ember-cli-typescript "^3.1.3"
+ ember-destroyable-polyfill "^2.0.1"
+ ember-modifier-manager-polyfill "^1.2.0"
+
ember-moment@^7.8.1:
version "7.8.1"
resolved "https://registry.yarnpkg.com/ember-moment/-/ember-moment-7.8.1.tgz#6f77cf941d1a92e231b2f4b810e113b2fae50c5f"
@@ -9101,6 +10000,11 @@ ember-rfc176-data@^0.3.13:
resolved "https://registry.yarnpkg.com/ember-rfc176-data/-/ember-rfc176-data-0.3.13.tgz#ed1712a26e65fec703655f35410414aa1982cf3b"
integrity sha512-m9JbwQlT6PjY7x/T8HslnXP7Sz9bx/pz3FrNfNi2NesJnbNISly0Lix6NV1fhfo46572cpq4jrM+/6yYlMefTQ==
+ember-rfc176-data@^0.3.15:
+ version "0.3.15"
+ resolved "https://registry.yarnpkg.com/ember-rfc176-data/-/ember-rfc176-data-0.3.15.tgz#af3f1da5a0339b6feda380edc2f7190e0f416c2d"
+ integrity sha512-GPKa7zRDBblRy0orxTXt5yrpp/Pf5CkuRFSIR8qMFDww0CqCKjCRwdZnWYzCM4kAEfZnXRIDDefe1tBaFw7v7w==
+
ember-router-generator@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ember-router-generator/-/ember-router-generator-2.0.0.tgz#d04abfed4ba8b42d166477bbce47fccc672dbde0"
@@ -12754,6 +13658,11 @@ lodash@^4.0.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
+lodash@^4.17.19:
+ version "4.17.20"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
+ integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
+
log-symbols@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18"