From f65878c21d5ce92068cd58ec0d041965f6a713b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 18 May 2017 17:04:31 +0200 Subject: [PATCH] ux: working on query troubleshooting --- package.json | 1 + .../app/core/components/jsonview/helpers.ts | 110 +++++ .../app/core/components/jsonview/jsonview.ts | 453 ++++++++++++++++++ public/app/core/components/response_viewer.ts | 63 +++ public/app/core/core.ts | 3 +- .../app/features/panel/metrics_ds_selector.ts | 31 +- public/app/headers/common.d.ts | 5 + public/app/system.conf.js | 3 +- public/sass/_grafana.scss | 1 + public/sass/components/_response_viewer.scss | 6 + public/test/test-main.js | 3 +- tasks/options/copy.js | 1 + yarn.lock | 20 +- 13 files changed, 669 insertions(+), 31 deletions(-) create mode 100644 public/app/core/components/jsonview/helpers.ts create mode 100644 public/app/core/components/jsonview/jsonview.ts create mode 100644 public/app/core/components/response_viewer.ts create mode 100644 public/sass/components/_response_viewer.scss diff --git a/package.json b/package.json index 47102820d58..19e91a31ffe 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "grunt-jscs": "3.0.1", "grunt-sass-lint": "^0.2.2", "grunt-sync": "^0.6.2", + "json-formatter-js": "^2.2.0", "karma-sinon": "^1.0.5", "lodash": "^4.17.2", "mousetrap": "^1.6.0", diff --git a/public/app/core/components/jsonview/helpers.ts b/public/app/core/components/jsonview/helpers.ts new file mode 100644 index 00000000000..3ab36a19a7d --- /dev/null +++ b/public/app/core/components/jsonview/helpers.ts @@ -0,0 +1,110 @@ +// #<{(| +// * Escapes `"` charachters from string +// |)}># +// function escapeString(str: string): string { +// return str.replace('"', '\"'); +// } +// +// #<{(| +// * Determines if a value is an object +// |)}># +// export function isObject(value: any): boolean { +// var type = typeof value; +// return !!value && (type === 'object'); +// } +// +// #<{(| +// * Gets constructor name of an object. +// * From http://stackoverflow.com/a/332429 +// * +// |)}># +// export function getObjectName(object: Object): string { +// if (object === undefined) { +// return ''; +// } +// if (object === null) { +// return 'Object'; +// } +// if (typeof object === 'object' && !object.constructor) { +// return 'Object'; +// } +// +// const funcNameRegex = /function ([^(]*)/; +// const results = (funcNameRegex).exec((object).constructor.toString()); +// if (results && results.length > 1) { +// return results[1]; +// } else { +// return ''; +// } +// } +// +// #<{(| +// * Gets type of an object. Returns "null" for null objects +// |)}># +// export function getType(object: Object): string { +// if (object === null) { return 'null'; } +// return typeof object; +// } +// +// #<{(| +// * Generates inline preview for a JavaScript object based on a value +// |)}># +// export function getValuePreview (object: Object, value: string): string { +// var type = getType(object); +// +// if (type === 'null' || type === 'undefined') { return type; } +// +// if (type === 'string') { +// value = '"' + escapeString(value) + '"'; +// } +// if (type === 'function'){ +// +// // Remove content of the function +// return object.toString() +// .replace(/[\r\n]/g, '') +// .replace(/\{.*\}/, '') + '{…}'; +// } +// return value; +// } +// +// #<{(| +// * Generates inline preview for a JavaScript object +// |)}># +// export function getPreview(object: string): string { +// let value = ''; +// if (isObject(object)) { +// value = getObjectName(object); +// if (Array.isArray(object)) { +// value += '[' + object.length + ']'; +// } +// } else { +// value = getValuePreview(object, object); +// } +// return value; +// } +// +// #<{(| +// * Generates a prefixed CSS class name +// |)}># +// export function cssClass(className: string): string { +// return `json-formatter-${className}`; +// } +// +// #<{(| +// * Creates a new DOM element wiht given type and class +// * TODO: move me to helpers +// |)}># +// export function createElement(type: string, className?: string, content?: Element|string): Element { +// const el = document.createElement(type); +// if (className) { +// el.classList.add(cssClass(className)); +// } +// if (content !== undefined) { +// if (content instanceof Node) { +// el.appendChild(content); +// } else { +// el.appendChild(document.createTextNode(String(content))); +// } +// } +// return el; +// } diff --git a/public/app/core/components/jsonview/jsonview.ts b/public/app/core/components/jsonview/jsonview.ts new file mode 100644 index 00000000000..71994ceef59 --- /dev/null +++ b/public/app/core/components/jsonview/jsonview.ts @@ -0,0 +1,453 @@ +// import { +// isObject, +// getObjectName, +// getType, +// getValuePreview, +// getPreview, +// cssClass, +// createElement +// } from './helpers'; +// +// import './style.less'; +// +// const DATE_STRING_REGEX = /(^\d{1,4}[\.|\\/|-]\d{1,2}[\.|\\/|-]\d{1,4})(\s*(?:0?[1-9]:[0-5]|1(?=[012])\d:[0-5])\d\s*[ap]m)?$/; +// const PARTIAL_DATE_REGEX = /\d{2}:\d{2}:\d{2} GMT-\d{4}/; +// const JSON_DATE_REGEX = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/; +// +// // When toggleing, don't animated removal or addition of more than a few items +// const MAX_ANIMATED_TOGGLE_ITEMS = 10; +// +// const requestAnimationFrame = window.requestAnimationFrame || function(cb: ()=>void) { cb(); return 0; }; +// +// export interface JSONFormatterConfiguration { +// hoverPreviewEnabled?: boolean; +// hoverPreviewArrayCount?: number; +// hoverPreviewFieldCount?: number; +// animateOpen?: boolean; +// animateClose?: boolean; +// theme?: string; +// }; +// +// const _defaultConfig: JSONFormatterConfiguration = { +// hoverPreviewEnabled: false, +// hoverPreviewArrayCount: 100, +// hoverPreviewFieldCount: 5, +// animateOpen: true, +// animateClose: true, +// theme: null +// }; +// +// +// #<{(|* +// * @class JSONFormatter +// * +// * JSONFormatter allows you to render JSON objects in HTML with a +// * **collapsible** navigation. +// |)}># +// export default class JSONFormatter { +// +// // Hold the open state after the toggler is used +// private _isOpen: boolean = null; +// +// // A reference to the element that we render to +// private element: Element; +// +// #<{(|* +// * @param {object} json The JSON object you want to render. It has to be an +// * object or array. Do NOT pass raw JSON string. +// * +// * @param {number} [open=1] his number indicates up to how many levels the +// * rendered tree should expand. Set it to `0` to make the whole tree collapsed +// * or set it to `Infinity` to expand the tree deeply +// * +// * @param {object} [config=defaultConfig] - +// * defaultConfig = { +// * hoverPreviewEnabled: false, +// * hoverPreviewArrayCount: 100, +// * hoverPreviewFieldCount: 5 +// * } +// * +// * Available configurations: +// * #####Hover Preview +// * * `hoverPreviewEnabled`: enable preview on hover +// * * `hoverPreviewArrayCount`: number of array items to show in preview Any +// * array larger than this number will be shown as `Array[XXX]` where `XXX` +// * is length of the array. +// * * `hoverPreviewFieldCount`: number of object properties to show for object +// * preview. Any object with more properties that thin number will be +// * truncated. +// * +// * @param {string} [key=undefined] The key that this object in it's parent +// * context +// |)}># +// constructor(public json: any, private open = 1, private config: JSONFormatterConfiguration = _defaultConfig, private key?: string) { +// +// // Setting default values for config object +// if (this.config.hoverPreviewEnabled === undefined) { +// this.config.hoverPreviewEnabled = _defaultConfig.hoverPreviewEnabled; +// } +// if (this.config.hoverPreviewArrayCount === undefined) { +// this.config.hoverPreviewArrayCount = _defaultConfig.hoverPreviewArrayCount; +// } +// if (this.config.hoverPreviewFieldCount === undefined) { +// this.config.hoverPreviewFieldCount = _defaultConfig.hoverPreviewFieldCount; +// } +// } +// +// #<{(| +// * is formatter open? +// |)}># +// private get isOpen(): boolean { +// if (this._isOpen !== null) { +// return this._isOpen; +// } else { +// return this.open > 0; +// } +// } +// +// #<{(| +// * set open state (from toggler) +// |)}># +// private set isOpen(value: boolean) { +// this._isOpen = value; +// } +// +// #<{(| +// * is this a date string? +// |)}># +// private get isDate(): boolean { +// return (this.type === 'string') && +// (DATE_STRING_REGEX.test(this.json) || +// JSON_DATE_REGEX.test(this.json) || +// PARTIAL_DATE_REGEX.test(this.json)); +// } +// +// #<{(| +// * is this a URL string? +// |)}># +// private get isUrl(): boolean { +// return this.type === 'string' && (this.json.indexOf('http') === 0); +// } +// +// #<{(| +// * is this an array? +// |)}># +// private get isArray(): boolean { +// return Array.isArray(this.json); +// } +// +// #<{(| +// * is this an object? +// * Note: In this context arrays are object as well +// |)}># +// private get isObject(): boolean { +// return isObject(this.json); +// } +// +// #<{(| +// * is this an empty object with no properties? +// |)}># +// private get isEmptyObject(): boolean { +// return !this.keys.length && !this.isArray; +// } +// +// #<{(| +// * is this an empty object or array? +// |)}># +// private get isEmpty(): boolean { +// return this.isEmptyObject || (this.keys && !this.keys.length && this.isArray); +// } +// +// #<{(| +// * did we recieve a key argument? +// * This means that the formatter was called as a sub formatter of a parent formatter +// |)}># +// private get hasKey(): boolean { +// return typeof this.key !== 'undefined'; +// } +// +// #<{(| +// * if this is an object, get constructor function name +// |)}># +// private get constructorName(): string { +// return getObjectName(this.json); +// } +// +// #<{(| +// * get type of this value +// * Possible values: all JavaScript primitive types plus "array" and "null" +// |)}># +// private get type(): string { +// return getType(this.json); +// } +// +// #<{(| +// * get object keys +// * If there is an empty key we pad it wit quotes to make it visible +// |)}># +// private get keys(): string[] { +// if (this.isObject) { +// return Object.keys(this.json).map((key)=> key ? key : '""'); +// } else { +// return []; +// } +// } +// +// #<{(|* +// * Toggles `isOpen` state +// * +// |)}># +// toggleOpen() { +// this.isOpen = !this.isOpen; +// +// if (this.element) { +// if (this.isOpen) { +// this.appendChildren(this.config.animateOpen); +// } else{ +// this.removeChildren(this.config.animateClose); +// } +// this.element.classList.toggle(cssClass('open')); +// } +// } +// +// #<{(|* +// * Open all children up to a certain depth. +// * Allows actions such as expand all/collapse all +// * +// |)}># +// openAtDepth(depth = 1) { +// if (depth < 0) { +// return; +// } +// +// this.open = depth; +// this.isOpen = (depth !== 0); +// +// if (this.element) { +// this.removeChildren(false); +// +// if (depth === 0) { +// this.element.classList.remove(cssClass('open')); +// } else { +// this.appendChildren(this.config.animateOpen); +// this.element.classList.add(cssClass('open')); +// } +// } +// } +// +// #<{(|* +// * Generates inline preview +// * +// * @returns {string} +// |)}># +// getInlinepreview() { +// if (this.isArray) { +// +// // if array length is greater then 100 it shows "Array[101]" +// if (this.json.length > this.config.hoverPreviewArrayCount) { +// return `Array[${this.json.length}]`; +// } else { +// return `[${this.json.map(getPreview).join(', ')}]`; +// } +// } else { +// +// const keys = this.keys; +// +// // the first five keys (like Chrome Developer Tool) +// const narrowKeys = keys.slice(0, this.config.hoverPreviewFieldCount); +// +// // json value schematic information +// const kvs = narrowKeys.map(key => `${key}:${getPreview(this.json[key])}`); +// +// // if keys count greater then 5 then show ellipsis +// const ellipsis = keys.length >= this.config.hoverPreviewFieldCount ? '…' : ''; +// +// return `{${kvs.join(', ')}${ellipsis}}`; +// } +// } +// +// +// #<{(|* +// * Renders an HTML element and installs event listeners +// * +// * @returns {HTMLDivElement} +// |)}># +// render(): HTMLDivElement { +// +// // construct the root element and assign it to this.element +// this.element = createElement('div', 'row'); +// +// // construct the toggler link +// const togglerLink = createElement('a', 'toggler-link'); +// +// // if this is an object we need a wrapper span (toggler) +// if (this.isObject) { +// togglerLink.appendChild(createElement('span', 'toggler')); +// } +// +// // if this is child of a parent formatter we need to append the key +// if (this.hasKey) { +// togglerLink.appendChild(createElement('span', 'key', `${this.key}:`)); +// } +// +// // Value for objects and arrays +// if (this.isObject) { +// +// // construct the value holder element +// const value = createElement('span', 'value'); +// +// // we need a wrapper span for objects +// const objectWrapperSpan = createElement('span'); +// +// // get constructor name and append it to wrapper span +// var constructorName = createElement('span', 'constructor-name', this.constructorName); +// objectWrapperSpan.appendChild(constructorName); +// +// // if it's an array append the array specific elements like brackets and length +// if (this.isArray) { +// const arrayWrapperSpan = createElement('span'); +// arrayWrapperSpan.appendChild(createElement('span', 'bracket', '[')); +// arrayWrapperSpan.appendChild(createElement('span', 'number', (this.json.length))); +// arrayWrapperSpan.appendChild(createElement('span', 'bracket', ']')); +// objectWrapperSpan.appendChild(arrayWrapperSpan); +// } +// +// // append object wrapper span to toggler link +// value.appendChild(objectWrapperSpan); +// togglerLink.appendChild(value); +// +// // Primitive values +// } else { +// +// // make a value holder element +// const value = this.isUrl ? createElement('a') : createElement('span'); +// +// // add type and other type related CSS classes +// value.classList.add(cssClass(this.type)); +// if (this.isDate) { +// value.classList.add(cssClass('date')); +// } +// if (this.isUrl) { +// value.classList.add(cssClass('url')); +// value.setAttribute('href', this.json); +// } +// +// // Append value content to value element +// const valuePreview = getValuePreview(this.json, this.json); +// value.appendChild(document.createTextNode(valuePreview)); +// +// // append the value element to toggler link +// togglerLink.appendChild(value); +// } +// +// // if hover preview is enabled, append the inline preview element +// if (this.isObject && this.config.hoverPreviewEnabled) { +// const preview = createElement('span', 'preview-text'); +// preview.appendChild(document.createTextNode(this.getInlinepreview())); +// togglerLink.appendChild(preview); +// } +// +// // construct a children element +// const children = createElement('div', 'children'); +// +// // set CSS classes for children +// if (this.isObject) { +// children.classList.add(cssClass('object')); +// } +// if (this.isArray) { +// children.classList.add(cssClass('array')); +// } +// if (this.isEmpty) { +// children.classList.add(cssClass('empty')); +// } +// +// // set CSS classes for root element +// if (this.config && this.config.theme) { +// this.element.classList.add(cssClass(this.config.theme)); +// } +// if (this.isOpen) { +// this.element.classList.add(cssClass('open')); +// } +// +// // append toggler and children elements to root element +// this.element.appendChild(togglerLink); +// this.element.appendChild(children); +// +// // if formatter is set to be open call appendChildren +// if (this.isObject && this.isOpen) { +// this.appendChildren(); +// } +// +// // add event listener for toggling +// if (this.isObject) { +// togglerLink.addEventListener('click', this.toggleOpen.bind(this)); +// } +// +// return this.element as HTMLDivElement; +// } +// +// #<{(|* +// * Appends all the children to children element +// * Animated option is used when user triggers this via a click +// |)}># +// appendChildren(animated = false) { +// const children = this.element.querySelector(`div.${cssClass('children')}`); +// +// if (!children || this.isEmpty) { return; } +// +// if (animated) { +// let index = 0; +// const addAChild = ()=> { +// const key = this.keys[index]; +// const formatter = new JSONFormatter(this.json[key], this.open - 1, this.config, key); +// children.appendChild(formatter.render()); +// +// index += 1; +// +// if (index < this.keys.length) { +// if (index > MAX_ANIMATED_TOGGLE_ITEMS) { +// addAChild(); +// } else { +// requestAnimationFrame(addAChild); +// } +// } +// }; +// +// requestAnimationFrame(addAChild); +// +// } else { +// this.keys.forEach(key => { +// const formatter = new JSONFormatter(this.json[key], this.open - 1, this.config, key); +// children.appendChild(formatter.render()); +// }); +// } +// } +// +// #<{(|* +// * Removes all the children from children element +// * Animated option is used when user triggers this via a click +// |)}># +// removeChildren(animated = false) { +// const childrenElement = this.element.querySelector(`div.${cssClass('children')}`) as HTMLDivElement; +// +// if (animated) { +// let childrenRemoved = 0; +// const removeAChild = ()=> { +// if (childrenElement && childrenElement.children.length) { +// childrenElement.removeChild(childrenElement.children[0]); +// childrenRemoved += 1; +// if (childrenRemoved > MAX_ANIMATED_TOGGLE_ITEMS) { +// removeAChild(); +// } else { +// requestAnimationFrame(removeAChild); +// } +// } +// }; +// requestAnimationFrame(removeAChild); +// } else { +// if (childrenElement) { +// childrenElement.innerHTML = ''; +// } +// } +// } +// } diff --git a/public/app/core/components/response_viewer.ts b/public/app/core/components/response_viewer.ts new file mode 100644 index 00000000000..5861e010fb3 --- /dev/null +++ b/public/app/core/components/response_viewer.ts @@ -0,0 +1,63 @@ +/// + +import coreModule from 'app/core/core_module'; +import JsonFormatter from 'json-formatter-js'; + + +const template = ` +
+
+
+`; + +export function responseViewer() { + return { + restrict: 'E', + template: template, + scope: {response: "="}, + link: function(scope, elem) { + var jsonElem = elem.find('.response-viewer-json'); + + scope.$watch("response", newVal => { + if (!newVal) { + elem.empty(); + return; + } + + if (scope.response.headers) { + delete scope.response.headers; + } + + if (scope.response.data) { + scope.response.response = scope.response.data; + delete scope.response.data; + } + + if (scope.response.config) { + scope.response.request = scope.response.config; + delete scope.response.config; + delete scope.response.request.transformRequest; + delete scope.response.request.transformResponse; + delete scope.response.request.paramSerializer; + delete scope.response.request.jsonpCallbackParam; + delete scope.response.request.headers; + delete scope.response.request.requestId; + delete scope.response.request.inspect; + delete scope.response.request.retry; + delete scope.response.request.timeout; + } + + + const formatter = new JsonFormatter(scope.response, 2, { + theme: 'dark', + }); + + const html = formatter.render(); + jsonElem.html(html); + }); + + } + }; +} + +coreModule.directive('responseViewer', responseViewer); diff --git a/public/app/core/core.ts b/public/app/core/core.ts index 4aa2e7eb64a..4c5fa429677 100644 --- a/public/app/core/core.ts +++ b/public/app/core/core.ts @@ -45,7 +45,7 @@ import {assignModelProperties} from './utils/model_utils'; import {contextSrv} from './services/context_srv'; import {KeybindingSrv} from './services/keybindingSrv'; import {helpModal} from './components/help/help'; - +import {responseViewer} from './components/response_viewer'; export { arrayJoin, @@ -69,4 +69,5 @@ export { contextSrv, KeybindingSrv, helpModal, + responseViewer, }; diff --git a/public/app/features/panel/metrics_ds_selector.ts b/public/app/features/panel/metrics_ds_selector.ts index 67619837e30..6b7fa8da6f5 100644 --- a/public/app/features/panel/metrics_ds_selector.ts +++ b/public/app/features/panel/metrics_ds_selector.ts @@ -8,16 +8,8 @@ var module = angular.module('grafana.directives'); var template = ` -
-
-
{{ctrl.lastError}}
-
-
-
-
-
{{ctrl.lastResponse}}
-
+
@@ -49,9 +41,9 @@ var template = `
-
@@ -68,7 +60,7 @@ export class MetricsDsSelectorCtrl { datasources: any[]; current: any; lastResponse: any; - lastError: any; + responseData: any; showResponse: boolean; /** @ngInject */ @@ -95,9 +87,8 @@ export class MetricsDsSelectorCtrl { } onRequestResponse(data) { - console.log(data); - this.lastResponse = JSON.stringify(data, null, 2); - this.lastError = null; + this.responseData = data; + this.showResponse = true; } toggleShowResponse() { @@ -105,8 +96,9 @@ export class MetricsDsSelectorCtrl { } onRequestError(err) { - console.log(err); - this.lastError = JSON.stringify(err, null, 2); + this.responseData = err; + this.responseData.isError = true; + this.showResponse = true; } getOptions(includeBuiltin) { @@ -122,8 +114,7 @@ export class MetricsDsSelectorCtrl { if (ds) { this.current = ds; this.panelCtrl.setDatasource(ds); - this.lastError = null; - this.lastResponse = null; + this.responseData = null; } } diff --git a/public/app/headers/common.d.ts b/public/app/headers/common.d.ts index 9ea5e96654d..b1d064b8036 100644 --- a/public/app/headers/common.d.ts +++ b/public/app/headers/common.d.ts @@ -72,3 +72,8 @@ declare module 'd3' { var d3: any; export default d3; } + +declare module 'json-formatter-js' { + var JSONFormatter: any; + export default JSONFormatter; +} diff --git a/public/app/system.conf.js b/public/app/system.conf.js index ae8e93a726a..9354a708785 100644 --- a/public/app/system.conf.js +++ b/public/app/system.conf.js @@ -32,7 +32,8 @@ System.config({ "jquery.flot.fillbelow": "vendor/flot/jquery.flot.fillbelow", "jquery.flot.gauge": "vendor/flot/jquery.flot.gauge", "d3": "vendor/d3/d3.js", - "jquery.flot.dashes": "vendor/flot/jquery.flot.dashes" + "jquery.flot.dashes": "vendor/flot/jquery.flot.dashes", + "json-formatter-js": "vendor/npm/json-formatter-js/dist/json-formatter" }, packages: { diff --git a/public/sass/_grafana.scss b/public/sass/_grafana.scss index 6266bd9c98f..6128a9c47a8 100644 --- a/public/sass/_grafana.scss +++ b/public/sass/_grafana.scss @@ -75,6 +75,7 @@ @import "components/jsontree"; @import "components/edit_sidemenu.scss"; @import "components/row.scss"; +@import "components/response_viewer.scss"; // PAGES @import "pages/login"; diff --git a/public/sass/components/_response_viewer.scss b/public/sass/components/_response_viewer.scss new file mode 100644 index 00000000000..f554f98e382 --- /dev/null +++ b/public/sass/components/_response_viewer.scss @@ -0,0 +1,6 @@ +.response-viewer { + background: $card-background; + box-shadow: $card-shadow; + padding: 1rem; + border-radius: 4px; +} diff --git a/public/test/test-main.js b/public/test/test-main.js index 50553b7c8b3..6cdf19c5372 100644 --- a/public/test/test-main.js +++ b/public/test/test-main.js @@ -40,7 +40,8 @@ "jquery.flot.fillbelow": "vendor/flot/jquery.flot.fillbelow", "jquery.flot.gauge": "vendor/flot/jquery.flot.gauge", "d3": "vendor/d3/d3.js", - "jquery.flot.dashes": "vendor/flot/jquery.flot.dashes" + "jquery.flot.dashes": "vendor/flot/jquery.flot.dashes", + "json-formatter-js": "vendor/npm/json-formatter-js/dist/json-formatter" }, packages: { diff --git a/tasks/options/copy.js b/tasks/options/copy.js index 59921576da3..6a902c7188b 100644 --- a/tasks/options/copy.js +++ b/tasks/options/copy.js @@ -34,6 +34,7 @@ module.exports = function(config) { 'remarkable/dist/*', 'virtual-scroll/**/*', 'mousetrap/**/*', + 'json-formatter-js/dist/*.js', ], dest: '<%= srcDir %>/vendor/npm' } diff --git a/yarn.lock b/yarn.lock index 74cad3c2407..cb45e85d1fb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1663,9 +1663,9 @@ glob@7.0.5: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@~7.0.0: - version "7.0.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.6.tgz#211bafaf49e525b8cd93260d14ab136152b3f57a" +glob@^7.0.0, glob@^7.1.1, glob@~7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -1674,9 +1674,9 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@~7.0.0: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.1.1, glob@~7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" +glob@^7.0.3, glob@^7.0.5, glob@~7.0.0: + version "7.0.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.6.tgz#211bafaf49e525b8cd93260d14ab136152b3f57a" dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -2561,6 +2561,10 @@ jshint@~2.9.4: shelljs "0.3.x" strip-json-comments "1.0.x" +json-formatter-js@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/json-formatter-js/-/json-formatter-js-2.2.0.tgz#1ed987223ef2f1d945304597faae78b580a8212b" + json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" @@ -3807,11 +3811,11 @@ resolve-pkg@^0.1.0: dependencies: resolve-from "^2.0.0" -resolve@1.1.x, resolve@^1.1.6, resolve@~1.1.0: +resolve@1.1.x, resolve@~1.1.0: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" -resolve@^1.3.2: +resolve@^1.1.6, resolve@^1.3.2: version "1.3.3" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.3.3.tgz#655907c3469a8680dc2de3a275a8fdd69691f0e5" dependencies: