diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index e0bb8c7d..00000000 --- a/LICENSE.txt +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013 - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index bab7fdc7..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1,4 +0,0 @@ -recursive-include server/app/web/templates * -recursive-include server/app/web/static * - -include server/requirements.txt \ No newline at end of file diff --git a/ROADMAP.md b/ROADMAP.md deleted file mode 100644 index a9291201..00000000 --- a/ROADMAP.md +++ /dev/null @@ -1,54 +0,0 @@ -# cellxgene roadmap - -We are very exited for _cellxgene_ to become a valuable tool in collaborations -between computational biologists and experimental biologists working on -single-cell transcriptomics data. _cellxgene_ is in active development, and we -would love to include the community as we plan new features to work on. If you -have questions of feedback about this roadmap, please submit an issue on -GitHub. - -Please note: this roadmap is subject to change. - -*Last updated: April 11, 2019* - -## what we are building now - -In the near term, our goal is to enable teams of computational and experimental -biologists to collaboratively explore and annotate their single-cell RNA-seq data. - -There are 4 key features we plan to implement in the near term. - -- Click install and launch -- Manual annotation workflows -- Toggle embeddings -- Gene information - -### simple install and launch - -The command line interface for installing and launching cellxgene is a barrier -for users who are not used to Python or using the command line. We plan to -support installation and launch of cellxgene on Mac and Windows. See -[Issue #687](https://github.com/chanzuckerberg/cellxgene/issues/687) for more details. - -### manual annotation workflows - -The exploratory visualization that cellxgene offers is critical for manual -annotation workflows, especially in collaborative environments. We plan to -support manually annotate cells with labels (i.e., cell type or QC flags) for -downstream analysis. See [Issue #524](https://github.com/chanzuckerberg/cellxgene/issues/524) -for more details. - -### toggle embeddings - -While a single dataset may have multiple embeddings calculated (tSNE, umap, in -situ coordinates, trajectories, etc), cellxgene currently requires the user to select the -embedding to use in the main layout at launch. We plan to support letting users -toggle between any embedding present in a file from the cellxgene interface. -See [Issue #594](https://github.com/chanzuckerberg/cellxgene/issues/594) for details. - -### gene information - -Differential expression returns only the names of genes, but no additional information -about gene metadata, function, or known associations. We plan to help users learn -more about genes they discover by exposing additional gene metadata. See -[Issue #96](https://github.com/chanzuckerberg/cellxgene/issues/96) for details. diff --git a/client/__tests__/e2e/config.js b/client/__tests__/e2e/config.js deleted file mode 100644 index 45a32346..00000000 --- a/client/__tests__/e2e/config.js +++ /dev/null @@ -1,6 +0,0 @@ -export const jest_env = process.env.JEST_ENV || "dev"; -export const appPort = process.env.JEST_CXG_PORT || 3000; -export const appUrlBase = `http://localhost:${appPort}`; -export const DEV = jest_env === "dev"; -export const DEBUG = jest_env === "debug"; -export const DATASET = "pbmc3k"; diff --git a/client/__tests__/e2e/data.js b/client/__tests__/e2e/data.js deleted file mode 100644 index 02391376..00000000 --- a/client/__tests__/e2e/data.js +++ /dev/null @@ -1,122 +0,0 @@ -export const datasets = { - pbmc3k: { - title: "pbmc3k", - dataframe: { - nObs: "2638", - nVar: "1838", - type: "float32" - }, - categorical: { - louvain: { - "B cells": "342", - "CD14+ Monocytes": "480", - "CD4 T cells": "1144", - "CD8 T cells": "316", - "Dendritic cells": "37", - "FCGR3A+ Monocytes": "150", - Megakaryocytes: "15", - "NK cells": "154" - } - }, - continuous: { - n_genes: "int32", - percent_mito: "float32", - n_counts: "float32" - }, - cellsets: { - lasso: [ - { - "coordinates-as-percent": { x1: 0.05, y1: 0.25, x2: 0.15, y2: 0.35 }, - count: "71" - } - ], - categorical: [ - { - metadata: "louvain", - values: ["B cells", "Megakaryocytes"], - count: "357" - } - ], - continuous: [ - { - metadata: "n_genes", - "coordinates-as-percent": { x1: 0.25, y1: 0.5, x2: 0.55, y2: 0.5 }, - count: "1537" - } - ] - }, - - diffexp: { - cellset1: [ - { kind: "categorical", metadata: "louvain", values: ["B cells"] } - ], - cellset2: [ - { - kind: "categorical", - metadata: "louvain", - values: ["CD4 T cells", "NK cells"] - } - ], - "gene-results": [ - "HLA-DRB1", - "HLA-DPB1", - "CD79A", - "HLA-DPA1", - "HLA-DQA1", - "CD79B", - "HLA-DQB1", - "MS4A1", - "IL32", - "CD37" - ] - }, - - genes: { - bulkadd: ["S100A8", "FCGR3A", "LGALS2", "GSTP1"], - search: "ACD" - }, - subset: { - cellset1: [ - { - kind: "categorical", - metadata: "louvain", - values: ["B cells", "Megakaryocytes"] - } - ], - count: "357", - categorical: { - louvain: { - "B cells": "342", - Megakaryocytes: "15" - } - }, - lasso: { - "coordinates-as-percent": { x1: 0.45, y1: 0.05, x2: 0.65, y2: 0.15 }, - count: "36" - } - }, - scatter: { - genes: { x: "S100A8", y: "FCGR3A" } - }, - pan: { - "coordinates-as-percent": { x1: 0.75, y1: 0.75, x2: 0.35, y2: 0.35 } - }, - features: { - panzoom: { - lasso: { - "coordinates-as-percent": { x1: 0.3, y1: 0.3, x2: 0.5, y2: 0.5 }, - count: "24" - } - } - }, - clip: { - min: "30", - max: "70", - metadata: "n_genes", - gene: "S100A8", - "coordinates-as-percent": { x1: 0.25, y1: 0.5, x2: 0.55, y2: 0.5 }, - count: "392", - "gene-cell-count": "421" - } - } -}; diff --git a/client/__tests__/e2e/e2e.test.js b/client/__tests__/e2e/e2e.test.js deleted file mode 100644 index 783e8f31..00000000 --- a/client/__tests__/e2e/e2e.test.js +++ /dev/null @@ -1,343 +0,0 @@ -/* -Smoke test suite that will be run in Travis CI - -Tests included in this file are expected to be relatively stable and test core features - */ -import puppeteer from "puppeteer"; -import { appUrlBase, DEBUG, DEV, DATASET } from "./config"; -import { puppeteerUtils, cellxgeneActions } from "./puppeteerUtils"; -import { datasets } from "./data"; - -let browser, page, utils, cxgActions, spy; -const browserViewport = { width: 1280, height: 960 }; -let data = datasets[DATASET]; - -if (DEBUG) jest.setTimeout(100000); -if (DEV) jest.setTimeout(10000); - -beforeAll(async () => { - const browserParams = DEV - ? { headless: false, slowMo: 5 } - : DEBUG - ? { headless: false, slowMo: 100, devtools: true } - : {}; - browser = await puppeteer.launch(browserParams); - page = await browser.newPage(); - await page.setViewport(browserViewport); - if (DEV || DEBUG) { - page.on("console", async msg => { - // If there is a console.error but an error is not thrown, this will ensure the test fails - if (msg.type() === "error") { - const errorMsgText = await Promise.all( - // TODO can we do this without internal properties? - msg.args().map(arg => arg._remoteObject.description) - ); - throw new Error(`Console error: ${errorMsgText}`); - } - console.log(`PAGE LOG: ${msg.text()}`); - }); - } - page.on("pageerror", err => { - throw new Error(`Console error: ${err}`); - }); - utils = puppeteerUtils(page); - cxgActions = cellxgeneActions(page); -}); - -beforeEach(async () => { - await page.goto(appUrlBase); -}); - -afterAll(() => { - if (!DEBUG) { - browser.close(); - } -}); - -describe("did launch", () => { - test("page launched", async () => { - let el = await utils.getOneElementInnerHTML("[data-testid='header']"); - expect(el).toBe(data.title); - }); -}); - -describe("metadata loads", () => { - test("categories and values from dataset appear", async () => { - for (const label in data.categorical) { - await utils.waitByID(`category-${label}`); - const categoryName = await utils.getOneElementInnerText( - `[data-testid="category-${label}"]` - ); - expect(categoryName).toMatch(label); - await utils.clickOn(`category-expand-${label}`); - const categories = await cxgActions.getAllCategoriesAndCounts(label); - expect(Object.keys(categories)).toMatchObject( - Object.keys(data.categorical[label]) - ); - expect(Object.values(categories)).toMatchObject( - Object.values(data.categorical[label]) - ); - } - }); - - test("continuous data appears", async () => { - for (const label in data.continuous) { - await utils.waitByID(`histogram-${label}`); - } - }); -}); - -describe("cell selection", () => { - test("selects all cells cellset 1", async () => { - const cellCount = await cxgActions.cellSet(1); - expect(cellCount).toBe(data.dataframe.nObs); - }); - - test("selects all cells cellset 2", async () => { - const cellCount = await cxgActions.cellSet(2); - expect(cellCount).toBe(data.dataframe.nObs); - }); - - test("selects cells via lasso", async () => { - for (const cellset of data.cellsets.lasso) { - const cellset1 = await cxgActions.calcDragCoordinates( - "layout-graph", - cellset["coordinates-as-percent"] - ); - await cxgActions.drag("layout-graph", cellset1.start, cellset1.end, true); - const cellCount = await cxgActions.cellSet(1); - expect(cellCount).toBe(cellset.count); - } - }); - - test("selects cells via categorical", async () => { - for (const cellset of data.cellsets.categorical) { - await utils.clickOn(`category-expand-${cellset.metadata}`); - await utils.clickOn(`category-select-${cellset.metadata}`); - for (const val of cellset.values) { - await utils.clickOn( - `categorical-value-select-${cellset.metadata}-${val}` - ); - } - const cellCount = await cxgActions.cellSet(1); - expect(cellCount).toBe(cellset.count); - } - }); - - test("selects cells via continuous", async () => { - for (const cellset of data.cellsets.continuous) { - const histId = `histogram-${cellset.metadata}-plot-brush`; - const coords = await cxgActions.calcDragCoordinates( - histId, - cellset["coordinates-as-percent"] - ); - await cxgActions.drag(histId, coords.start, coords.end); - const cellCount = await cxgActions.cellSet(1); - expect(cellCount).toBe(cellset.count); - } - }); -}); - -describe("gene entry", () => { - test("search for single gene", async () => { - // blueprint's typeahead is treating typing weird, clicking & waiting first solves this - await utils.typeInto("gene-search", data.genes.search); - await page.keyboard.press("Enter"); - await page.waitForSelector( - `[data-testid='histogram-${data.genes.search}']` - ); - }); - - test("bulk add genes", async () => { - await cxgActions.reset(); - const testGenes = data.genes.bulkadd; - await utils.clickOn("section-bulk-add"); - await utils.typeInto("input-bulk-add", testGenes.join(",")); - await page.keyboard.press("Enter"); - - const allHistograms = await cxgActions.getAllHistograms( - "histogram-user-gene", - testGenes - ); - expect(allHistograms).toEqual(expect.arrayContaining(testGenes)); - expect(allHistograms.length).toEqual(testGenes.length); - }); -}); - -describe("diffexp", () => { - test("selects cells, saves them and performs diffexp", async () => { - for (const select of data.diffexp.cellset1) { - if (select.kind === "categorical") { - await cxgActions.selectCategory(select.metadata, select.values, true); - } - } - await cxgActions.cellSet(1); - for (const select of data.diffexp.cellset2) { - if (select.kind === "categorical") { - await cxgActions.selectCategory(select.metadata, select.values, true); - } - } - await cxgActions.cellSet(2); - await utils.clickOn("diffexp-button"); - const allHistograms = await cxgActions.getAllHistograms( - "histogram-diffexp", - data.diffexp["gene-results"] - ); - expect(allHistograms).toEqual( - expect.arrayContaining(data.diffexp["gene-results"]) - ); - expect(allHistograms.length).toEqual(data.diffexp["gene-results"].length); - }); -}); - -describe("subset/reset", () => { - test("subset - cell count matches", async () => { - for (const select of data.subset.cellset1) { - if (select.kind === "categorical") { - await cxgActions.selectCategory(select.metadata, select.values, true); - } - } - await utils.clickOn("subset-button"); - for (const label in data.subset.categorical) { - const categories = await cxgActions.getAllCategoriesAndCounts(label); - expect(Object.keys(categories)).toMatchObject( - Object.keys(data.subset.categorical[label]) - ); - expect(Object.values(categories)).toMatchObject( - Object.values(data.subset.categorical[label]) - ); - } - }); - - test("reset after subset", async () => { - for (const select of data.subset.cellset1) { - if (select.kind === "categorical") { - await cxgActions.selectCategory(select.metadata, select.values, true); - } - } - await utils.clickOn("subset-button"); - for (const label in data.subset.categorical) { - const categories = await cxgActions.getAllCategoriesAndCounts(label); - expect(Object.keys(categories)).toMatchObject( - Object.keys(data.subset.categorical[label]) - ); - expect(Object.values(categories)).toMatchObject( - Object.values(data.subset.categorical[label]) - ); - } - await cxgActions.reset(); - for (const label in data.categorical) { - await utils.waitByID(`category-${label}`); - const categoryName = await utils.getOneElementInnerText( - `[data-testid="category-${label}"]` - ); - expect(categoryName).toMatch(label); - const categories = await cxgActions.getAllCategoriesAndCounts(label); - expect(Object.keys(categories)).toMatchObject( - Object.keys(data.categorical[label]) - ); - expect(Object.values(categories)).toMatchObject( - Object.values(data.categorical[label]) - ); - } - }); - - test("lasso after subset", async () => { - for (const select of data.subset.cellset1) { - if (select.kind === "categorical") { - await cxgActions.selectCategory(select.metadata, select.values, true); - } - } - await utils.clickOn("subset-button"); - const lassoSelection = await cxgActions.calcDragCoordinates( - "layout-graph", - data.subset.lasso["coordinates-as-percent"] - ); - await cxgActions.drag( - "layout-graph", - lassoSelection.start, - lassoSelection.end, - true - ); - const cellCount = await cxgActions.cellSet(1); - expect(cellCount).toBe(data.subset.lasso.count); - }); -}); - -describe("scatter plot", () => { - test("scatter plot appears", async () => { - await cxgActions.reset(); - const testGenes = data.scatter.genes; - await utils.clickOn("section-bulk-add"); - await utils.typeInto("input-bulk-add", Object.values(testGenes).join(",")); - await page.keyboard.press("Enter"); - await utils.clickOn(`plot-x-${data.scatter.genes.x}`); - await utils.clickOn(`plot-y-${data.scatter.genes.y}`); - await utils.waitByID("scatterplot"); - }); -}); - -describe("clipping", () => { - test("clip continuous", async () => { - await cxgActions.clip(data.clip.min, data.clip.max); - const histId = `histogram-${data.clip.metadata}-plot-brush`; - const coords = await cxgActions.calcDragCoordinates( - histId, - data.clip["coordinates-as-percent"] - ); - await cxgActions.drag(histId, coords.start, coords.end); - const cellCount = await cxgActions.cellSet(1); - expect(cellCount).toBe(data.clip.count); - }); - - test("clip gene", async () => { - await utils.typeInto("gene-search", data.clip.gene); - await page.keyboard.press("Enter"); - await page.waitForSelector(`[data-testid='histogram-${data.clip.gene}']`); - await cxgActions.clip(data.clip.min, data.clip.max); - const histId = `histogram-${data.clip.gene}-plot-brush`; - const coords = await cxgActions.calcDragCoordinates( - histId, - data.clip["coordinates-as-percent"] - ); - await cxgActions.drag(histId, coords.start, coords.end); - const cellCount = await cxgActions.cellSet(1); - expect(cellCount).toBe(data.clip["gene-cell-count"]); - }); -}); - -// interact with UI elements just that they do not break -describe("ui elements don't error", () => { - test("color by", async () => { - for (const label in data.categorical) { - await utils.clickOn(`colorby-${label}`); - } - for (const label in data.continuous) { - await utils.clickOn(`colorby-${label}`); - } - }); - - test("color by for gene", async () => { - await utils.typeInto("gene-search", data.genes.search); - await page.keyboard.press("Enter"); - await page.waitForSelector( - `[data-testid='histogram-${data.genes.search}']` - ); - await utils.clickOn(`colorby-${data.genes.search}`); - }); - - test("pan and zoom", async () => { - await utils.clickOn("mode-pan-zoom"); - const panCoords = await cxgActions.calcDragCoordinates( - "layout-graph", - data.pan["coordinates-as-percent"] - ); - await cxgActions.drag( - "layout-graph", - panCoords.start, - panCoords.end, - false - ); - await page.evaluate(`window.scrollBy(0, 1000);`); - }); -}); diff --git a/client/__tests__/e2e/e2eJestConfig.json b/client/__tests__/e2e/e2eJestConfig.json deleted file mode 100644 index cedae771..00000000 --- a/client/__tests__/e2e/e2eJestConfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "preset": "jest-puppeteer", - "testMatch": [ - "**/__tests__/**/?(*.)(spec|test).js?(x)" - ], - "testURL": "http://localhost/", - "setupFiles": [ - "../setupMissingGlobals.js" - ] -} diff --git a/client/__tests__/e2e/feature.test.js b/client/__tests__/e2e/feature.test.js deleted file mode 100644 index 8573ce5a..00000000 --- a/client/__tests__/e2e/feature.test.js +++ /dev/null @@ -1,118 +0,0 @@ -/* -NOT run in Travis CI - -UX tests using puppeteer to be run locally. - -To run locally, ensure you are running the client is running on port 3000. -Then run jest --verbose false --config __tests__/e2e/e2eJestConfig.json feature. - */ - -import puppeteer from "puppeteer"; -import { appUrlBase, DEBUG, DEV, DATASET } from "./config"; -import { puppeteerUtils, cellxgeneActions } from "./puppeteerUtils"; -import { datasets } from "./data"; - -let browser, page, utils, cxgActions, spy; -const browserViewport = { width: 1280, height: 960 }; -let data = datasets[DATASET].features; - -if (DEBUG) jest.setTimeout(100000); -if (DEV) jest.setTimeout(10000); - -beforeAll(async () => { - const browserParams = DEV - ? { headless: false, slowMo: 5 } - : DEBUG - ? { headless: false, slowMo: 100, devtools: true } - : {}; - browser = await puppeteer.launch(browserParams); - page = await browser.newPage(); - await page.setViewport(browserViewport); - if (DEV || DEBUG) { - page.on("console", msg => console.log(`PAGE LOG: ${msg.text()}`)); - } - page.on("pageerror", err => { - throw new Error(`Console error: ${err}`); - }); - utils = puppeteerUtils(page); - cxgActions = cellxgeneActions(page); -}); - -beforeEach(async () => { - await page.goto(appUrlBase); -}); - -afterAll(() => { - if (!DEBUG) { - browser.close(); - } -}); - -describe("zoom interaction", async () => { - // Skip this test since UI is to hide lasso path when switching modes - test.skip("lasso visible after switching modes to pan/zoom", async () => { - const lassoSelection = await cxgActions.calcDragCoordinates( - "layout-graph", - data.panzoom.lasso["coordinates-as-percent"] - ); - await cxgActions.drag( - "layout-graph", - lassoSelection.start, - lassoSelection.end, - true - ); - await utils.waitByID("lasso-element", { visible: true }); - await utils.clickOn("mode-pan-zoom"); - await utils.waitByID("lasso-element", { visible: true }); - }); - - test("pan zoom mode resets lasso selection", async () => { - const lassoSelection = await cxgActions.calcDragCoordinates( - "layout-graph", - data.panzoom.lasso["coordinates-as-percent"] - ); - await cxgActions.drag( - "layout-graph", - lassoSelection.start, - lassoSelection.end, - true - ); - await utils.waitByID("lasso-element", { visible: true }); - const initialCount = await cxgActions.cellSet(1); - expect(initialCount).toBe(data.panzoom.lasso.count); - await utils.clickOn("mode-pan-zoom"); - await utils.clickOn("mode-lasso"); - const modeSwitchCount = await cxgActions.cellSet(1); - expect(modeSwitchCount).toBe(initialCount); - }); - - test("lasso moves after pan", async () => { - const lassoSelection = await cxgActions.calcDragCoordinates( - "layout-graph", - data.panzoom.lasso["coordinates-as-percent"] - ); - await cxgActions.drag( - "layout-graph", - lassoSelection.start, - lassoSelection.end, - true - ); - await utils.waitByID("lasso-element", { visible: true }); - const initialCount = await cxgActions.cellSet(1); - expect(initialCount).toBe(data.panzoom.lasso.count); - await utils.clickOn("mode-pan-zoom"); - const panCoords = await cxgActions.calcDragCoordinates( - "layout-graph", - data.panzoom.lasso["coordinates-as-percent"] - ); - await cxgActions.drag( - "layout-graph", - panCoords.start, - panCoords.end, - false - ); - await utils.clickOn("mode-lasso"); - const panCount = await cxgActions.cellSet(2); - expect(panCount).toBe(initialCount); - }); -}); diff --git a/client/__tests__/e2e/puppeteerUtils.js b/client/__tests__/e2e/puppeteerUtils.js deleted file mode 100644 index 69b068a1..00000000 --- a/client/__tests__/e2e/puppeteerUtils.js +++ /dev/null @@ -1,211 +0,0 @@ -export const puppeteerUtils = puppeteerPage => ({ - async waitByID(testid, props = {}) { - return await puppeteerPage.waitForSelector( - `[data-testid='${testid}']`, - props - ); - }, - - async waitByClass(testclass, props = {}) { - return await puppeteerPage.waitForSelector( - `[data-testclass='${testclass}']`, - props - ); - }, - - async waitForAllByIds(testids, props = {}) { - await Promise.all( - testids.map(testid => - puppeteerPage.waitForSelector(`[data-testid='${testid}']`) - ) - ); - }, - - async getAllByClass(testclass, props = {}) { - const elements = await puppeteerPage.$$eval( - `[data-testclass=${testclass}]`, - els => { - return els.map(el => { - return el.dataset.testid; - }); - } - ); - return elements; - }, - - async typeInto(testid, text) { - // only works for text without special characters - await this.waitByID(testid); - const selector = `[data-testid='${testid}']`; - // type ahead can be annoying if you don't pause before you type - await puppeteerPage.click(selector); - await puppeteerPage.waitFor(200); - await puppeteerPage.type(selector, text); - }, - - async clearInputAndTypeInto(testid, text) { - await this.waitByID(testid); - const selector = `[data-testid='${testid}']`; - // only works for text without special characters - // type ahead can be annoying if you don't pause before you type - await puppeteerPage.click(selector); - await puppeteerPage.waitFor(200); - // select all - - await puppeteerPage.click(selector, { clickCount: 3 }); - await puppeteerPage.keyboard.type("Backspace"); - await puppeteerPage.type(selector, text); - }, - - async clickOn(testid) { - await this.waitByID(testid); - await puppeteerPage.click(`[data-testid='${testid}']`); - await puppeteerPage.waitFor(50); - }, - - async getOneElementInnerHTML(selector) { - await puppeteerPage.waitForSelector(selector); - let text = await puppeteerPage.$eval(selector, el => el.innerHTML); - return text; - }, - - async getOneElementInnerText(selector) { - await puppeteerPage.waitForSelector(selector); - let text = await puppeteerPage.$eval(selector, el => el.innerText); - return text; - } -}); - -export const cellxgeneActions = puppeteerPage => ({ - async drag(testid, start, end, lasso = false) { - const layout = await puppeteerUtils(puppeteerPage).waitByID(testid); - const elBox = await layout.boxModel(); - const x1 = elBox.content[0].x + start.x; - const x2 = elBox.content[0].x + end.x; - const y1 = elBox.content[0].y + start.y; - const y2 = elBox.content[0].y + end.y; - await puppeteerPage.mouse.move(x1, y1); - await puppeteerPage.mouse.down(); - if (lasso) { - await puppeteerPage.mouse.move(x2, y1); - await puppeteerPage.mouse.move(x2, y2); - await puppeteerPage.mouse.move(x1, y2); - await puppeteerPage.mouse.move(x1, y1); - } else { - await puppeteerPage.mouse.move(x2, y2); - } - await puppeteerPage.mouse.up(); - }, - - async getAllHistograms(testclass, testids) { - const histTestIds = testids.map(tid => `histogram-${tid}`); - // these load asynchronously, so we need to wait for each histogram individually - await puppeteerUtils(puppeteerPage).waitForAllByIds(histTestIds); - const allHistograms = await puppeteerUtils(puppeteerPage).getAllByClass( - testclass - ); - return allHistograms.map(hist => - hist.substr("histogram_".length, hist.length) - ); - }, - - async getAllCategoriesAndCounts(category) { - await puppeteerUtils(puppeteerPage).waitByClass("categorical-row"); - const categories = await puppeteerPage.$$eval( - `[data-testid="category-${category}"] [data-testclass='categorical-row']`, - els => { - let result = {}; - els.forEach(el => { - const cat = el.querySelector("[data-testclass='categorical-value']") - .innerText; - const count = el.querySelector( - "[data-testclass='categorical-value-count']" - ).innerText; - result[cat] = count; - }); - return result; - } - ); - return categories; - }, - - async cellSet(num) { - await puppeteerUtils(puppeteerPage).clickOn(`cellset-button-${num}`); - return await puppeteerUtils(puppeteerPage).getOneElementInnerText( - `[data-testid='cellset-count-${num}']` - ); - }, - - async resetCategory(category) { - const checkboxId = `category-select-${category}`; - await puppeteerUtils(puppeteerPage).waitByID(checkboxId); - const checkedPseudoclass = await puppeteerPage.$eval( - `[data-testid='${checkboxId}']`, - el => { - return el.matches(":checked"); - } - ); - if (!checkedPseudoclass) { - await puppeteerUtils(puppeteerPage).clickOn(checkboxId); - } - try { - const categoryRow = await puppeteerUtils(puppeteerPage).waitByID( - `category-expand-${category}` - ); - const isExpanded = await categoryRow.$( - "[data-testclass='category-expand-is-expanded']" - ); - if (isExpanded) { - await puppeteerUtils(puppeteerPage).clickOn( - `category-expand-${category}` - ); - } - } catch {} - }, - - async calcDragCoordinates(testid, coordinateAsPercent) { - const el = await puppeteerUtils(puppeteerPage).waitByID(testid); - const size = await el.boxModel(); - const coords = { - start: { - x: Math.floor(size.width * coordinateAsPercent.x1), - y: Math.floor(size.height * coordinateAsPercent.y1) - }, - end: { - x: Math.floor(size.width * coordinateAsPercent.x2), - y: Math.floor(size.height * coordinateAsPercent.y2) - } - }; - return coords; - }, - - async selectCategory(category, values, reset = true) { - if (reset) await this.resetCategory(category); - await puppeteerUtils(puppeteerPage).clickOn(`category-expand-${category}`); - await puppeteerUtils(puppeteerPage).clickOn(`category-select-${category}`); - for (const val of values) { - await puppeteerUtils(puppeteerPage).clickOn( - `categorical-value-select-${category}-${val}` - ); - } - }, - - async reset() { - await puppeteerUtils(puppeteerPage).clickOn("reset"); - // loading state never actually happens, reset is too fast - await page.waitFor(200); - }, - - async clip(min = 0, max = 100) { - await puppeteerUtils(puppeteerPage).clickOn("visualization-settings"); - await puppeteerUtils(puppeteerPage).clearInputAndTypeInto( - "clip-min-input", - min - ); - await puppeteerUtils(puppeteerPage).clearInputAndTypeInto( - "clip-max-input", - max - ); - await puppeteerUtils(puppeteerPage).clickOn("clip-commit"); - } -}); diff --git a/client/__tests__/reducers/cascade.test.js b/client/__tests__/reducers/cascade.test.js deleted file mode 100644 index 5395025b..00000000 --- a/client/__tests__/reducers/cascade.test.js +++ /dev/null @@ -1,48 +0,0 @@ -import cascadeReducers from "../../src/reducers/cascade"; - -describe("create", () => { - test("from Array", () => { - expect(cascadeReducers([["foo", () => 0]])).toBeInstanceOf(Function); - }); - - test("from Map", () => { - expect(cascadeReducers(new Map([["foo", () => 0]]))).toBeInstanceOf( - Function - ); - }); -}); - -describe("cascade", () => { - test("expected arguments provided & cascade ordering", () => { - const topLevelState = {}; - const topLevelAction = { type: "test" }; - - const reducer = cascadeReducers([ - [ - "foo", - (currentState, action, nextSharedState, prevSharedState) => { - expect(currentState).toBeUndefined(); - expect(action).toEqual(topLevelAction); - expect(nextSharedState).toStrictEqual({}); - expect(prevSharedState).toBe(topLevelState); - return 0; - } - ], - [ - "bar", - (currentState, action, nextSharedState, prevSharedState) => { - expect(currentState).toBeUndefined(); - expect(action).toEqual(topLevelAction); - expect(nextSharedState).toStrictEqual({ foo: 0 }); - expect(prevSharedState).toBe(topLevelState); - return 99; - } - ] - ]); - - const nextState = reducer(topLevelState, topLevelAction); - expect(nextState).toStrictEqual({ foo: 0, bar: 99 }); - expect(topLevelState).toStrictEqual({}); - expect(topLevelAction).toStrictEqual({ type: "test" }); - }); -}); diff --git a/client/__tests__/reducers/undoable.test.js b/client/__tests__/reducers/undoable.test.js deleted file mode 100644 index 40754e1e..00000000 --- a/client/__tests__/reducers/undoable.test.js +++ /dev/null @@ -1,87 +0,0 @@ -import undoable from "../../src/reducers/undoable"; - -describe("create", () => { - test("no keys", () => { - expect(() => undoable(() => {})).toThrow(); - expect(() => undoable(() => {}, null)).toThrow(); - expect(() => undoable(() => {}, [])).toThrow(); - expect(() => undoable(() => {}, [], {})).toThrow(); - }); - - test("simple", () => { - expect(undoable(() => {}, ["foo"])).toBeInstanceOf(Function); - expect(undoable(() => {}, ["foo"], {})).toBeInstanceOf(Function); - }); - - test("handles undefined initial state", () => { - expect( - undoable(() => {}, ["a"])(undefined, { type: "test" }) - ).toMatchObject({}); - }); -}); - -describe("undo", () => { - test("expected state modifications", () => { - const initialState = { a: 0, b: 1000 }; - const reducer = state => { - return { a: state.a + 1, b: state.b + 1 }; - }; - const undoableReducer = undoable(reducer, ["a"]); - - const s1 = undoableReducer(initialState, { type: "test" }); - expect(s1).toMatchObject({ a: 1, b: 1001 }); - - // test that only specified keys are undone - const s2 = undoableReducer(s1, { type: "@@undoable/undo" }); - expect(s2).toMatchObject({ a: 0, b: 1001 }); - - // test backstop when no more history - const s3 = undoableReducer(s2, { type: "@@undoable/undo" }); - expect(s3).toMatchObject({ a: 0, b: 1001 }); - }); -}); - -describe("redo", () => { - const initialState = { a: 0, b: 1000 }; - const reducer = state => { - return { a: state.a + 1, b: state.b + 1 }; - }; - let UR; - - beforeEach(() => { - UR = undoable(reducer, ["a"]); - }); - - test("expected state modifications", () => { - const s1 = UR(initialState, { type: "test" }); - expect(s1).toMatchObject({ a: 1, b: 1001 }); - - // verify undo->redo reverts state. - const s2 = UR(UR(s1, { type: "@@undoable/undo" }), { - type: "@@undoable/redo" - }); - expect(s2).toMatchObject({ a: 1, b: 1001 }); - - // verify backstop when no redo future - const s3 = UR(s2, { type: "@@undoable/redo" }); - expect(s3).toMatchObject({ a: 1, b: 1001 }); - }); - - test("history cleared", () => { - // verify future cleared upon a normal state transition - const s1 = UR(initialState, { type: "test" }); - expect(s1).toMatchObject({ a: 1, b: 1001 }); - const s2 = UR(s1, { type: "@@undoable/undo" }); - expect(s2).toMatchObject({ a: 0, b: 1001 }); - const s3 = UR(s2, { type: "test" }); - expect(s3).toMatchObject({ a: 1, b: 1002 }); - const s4 = UR(s3, { type: "@@undoable/redo" }); - expect(s4).toMatchObject({ a: 1, b: 1002 }); - }); -}); - -/* -TODO: -- historyLimit is enforced -- action filters -*/ diff --git a/client/__tests__/setupMissingGlobals.js b/client/__tests__/setupMissingGlobals.js deleted file mode 100644 index 679cc21f..00000000 --- a/client/__tests__/setupMissingGlobals.js +++ /dev/null @@ -1,9 +0,0 @@ -/* -Define globals which are present in the client, but not in node (and therefore not in -the jest test environment). -*/ - -import { TextDecoder, TextEncoder } from "util"; - -global.TextDecoder = TextDecoder; -global.TextEncoder = TextEncoder; diff --git a/client/__tests__/util/actionHelpers.test.js b/client/__tests__/util/actionHelpers.test.js deleted file mode 100644 index 08d91b70..00000000 --- a/client/__tests__/util/actionHelpers.test.js +++ /dev/null @@ -1,60 +0,0 @@ -import { rangeEncodeIndices } from "../../src/util/actionHelpers"; - -describe("rangeEncodeIndices", () => { - test("small array edge cases", () => { - expect(rangeEncodeIndices([])).toMatchObject([]); - expect(rangeEncodeIndices([1])).toMatchObject([1]); - expect(rangeEncodeIndices([1, 99])).toMatchObject([1, 99]); - expect(rangeEncodeIndices([99, 1])).toMatchObject([1, 99]); - expect(rangeEncodeIndices([1, 100, 4])).toMatchObject([1, 4, 100]); - }); - - test("sorted flag", () => { - expect(rangeEncodeIndices([1, 9, 432], 10, true)).toMatchObject([ - 1, - 9, - 432 - ]); - expect(rangeEncodeIndices([1, 9, 432], 10, false)).toMatchObject([ - 1, - 9, - 432 - ]); - expect(rangeEncodeIndices([0, 1, 2, 3, 9, 10, 432], 2, true)).toMatchObject( - [[0, 3], [9, 10], 432] - ); - expect( - rangeEncodeIndices([0, 1, 2, 3, 9, 10, 432], 2, false) - ).toMatchObject([[0, 3], [9, 10], 432]); - }); - - test("begin or end edge cases", () => { - expect( - rangeEncodeIndices([3, 4, 5, 6, 7, 10, 11, 12, 99], 3, false) - ).toMatchObject([[3, 7], [10, 12], 99]); - expect( - rangeEncodeIndices([3, 4, 5, 6, 7, 10, 11, 12], 3, false) - ).toMatchObject([[3, 7], [10, 12]]); - expect( - rangeEncodeIndices([0, 3, 4, 5, 6, 7, 10, 11, 12], 3, false) - ).toMatchObject([0, [3, 7], [10, 12]]); - expect( - rangeEncodeIndices([0, 3, 4, 5, 6, 7, 10, 11, 12, 99], 3, false) - ).toMatchObject([0, [3, 7], [10, 12], 99]); - }); - - test("minRangeLength", () => { - expect( - rangeEncodeIndices([3, 4, 5, 6, 7, 10, 11, 12, 99], 4, false) - ).toMatchObject([[3, 7], 10, 11, 12, 99]); - expect( - rangeEncodeIndices([3, 4, 5, 6, 7, 10, 11, 12], 4, false) - ).toMatchObject([[3, 7], 10, 11, 12]); - expect( - rangeEncodeIndices([0, 3, 4, 5, 6, 7, 10, 11, 12], 4, false) - ).toMatchObject([0, [3, 7], 10, 11, 12]); - expect( - rangeEncodeIndices([0, 3, 4, 5, 6, 7, 10, 11, 12, 99], 4, false) - ).toMatchObject([0, [3, 7], 10, 11, 12, 99]); - }); -}); diff --git a/client/__tests__/util/dataframe/dataframe.test.js b/client/__tests__/util/dataframe/dataframe.test.js deleted file mode 100644 index a89b3486..00000000 --- a/client/__tests__/util/dataframe/dataframe.test.js +++ /dev/null @@ -1,652 +0,0 @@ -import * as Dataframe from "../../../src/util/dataframe"; - -describe("dataframe constructor", () => { - test("empty dataframe", () => { - const df = new Dataframe.Dataframe([0, 0], []); - expect(df).toBeDefined(); - expect(df.dims).toEqual([0, 0]); - expect(df).toHaveLength(0); - expect(df.icol(0)).not.toBeDefined(); - }); - - test("create with default indices", () => { - const df = new Dataframe.Dataframe( - [3, 2], - [new Int32Array(3).fill(0), new Int32Array(3).fill(1)] - ); - - expect(df).toBeDefined(); - expect(df.dims).toEqual([3, 2]); - expect(df.rowIndex).toBeInstanceOf(Dataframe.IdentityInt32Index); - expect(df.colIndex).toBeInstanceOf(Dataframe.IdentityInt32Index); - expect(df.at(0, 0)).toEqual(0); - expect(df.at(2, 1)).toEqual(1); - expect(df.iat(0, 0)).toEqual(0); - expect(df.iat(2, 1)).toEqual(1); - }); - - test("create with labelled indices", () => { - const df = new Dataframe.Dataframe( - [3, 2], - [new Int32Array([0, 1, 2]), new Int32Array([3, 4, 5])], - new Dataframe.DenseInt32Index([2, 1, 0]), - new Dataframe.KeyIndex(["A", "B"]) - ); - - expect(df).toBeDefined(); - expect(df.dims).toEqual([3, 2]); - - expect(df.rowIndex).toBeInstanceOf(Dataframe.DenseInt32Index); - expect(df.colIndex).toBeInstanceOf(Dataframe.KeyIndex); - expect(df.rowIndex.keys()).toEqual(new Int32Array([2, 1, 0])); - expect(df.colIndex.keys()).toEqual(["A", "B"]); - - expect(df.at(0, "A")).toEqual(2); - expect(df.at(2, "B")).toEqual(3); - expect(df.iat(0, 0)).toEqual(0); - expect(df.iat(2, 1)).toEqual(5); - }); -}); - -describe("simple data access", () => { - const df = new Dataframe.Dataframe( - [4, 2], - [ - new Float64Array([0.0, Number.NaN, Number.POSITIVE_INFINITY, 3.14159]), - ["red", "blue", "green", "nan"] - ], - new Dataframe.DenseInt32Index([3, 2, 1, 0]), - new Dataframe.KeyIndex(["numbers", "colors"]) - ); - - test("iat", () => { - expect(df).toBeDefined(); - - // present - expect(df.iat(0, 0)).toEqual(0.0); - expect(df.iat(0, 1)).toEqual("red"); - expect(df.iat(1, 0)).toEqual(Number.NaN); - expect(df.iat(1, 1)).toEqual("blue"); - expect(df.iat(2, 0)).toEqual(Number.POSITIVE_INFINITY); - expect(df.iat(2, 1)).toEqual("green"); - expect(df.iat(3, 0)).toEqual(3.14159); - expect(df.iat(3, 1)).toEqual("nan"); - - // labels out of range have no defined behavior - }); - - test("at", () => { - expect(df).toBeDefined(); - - // present - expect(df.at(3, "numbers")).toEqual(0.0); - expect(df.at(3, "colors")).toEqual("red"); - expect(df.at(2, "numbers")).toEqual(Number.NaN); - expect(df.at(2, "colors")).toEqual("blue"); - expect(df.at(1, "numbers")).toEqual(Number.POSITIVE_INFINITY); - expect(df.at(1, "colors")).toEqual("green"); - expect(df.at(0, "numbers")).toEqual(3.14159); - expect(df.at(0, "colors")).toEqual("nan"); - - // labels out of range have no defined behavior - }); - - test("ihas", () => { - expect(df).toBeDefined(); - - // present - expect(df.ihas(0, 0)).toBeTruthy(); - expect(df.ihas(1, 1)).toBeTruthy(); - expect(df.ihas(3, 1)).toBeTruthy(); - - // not present - expect(df.ihas(-1, -1)).toBeFalsy(); - expect(df.ihas(0, 99)).toBeFalsy(); - expect(df.ihas(99, 0)).toBeFalsy(); - expect(df.ihas(99, 99)).toBeFalsy(); - expect(df.ihas(-1, 0)).toBeFalsy(); - expect(df.ihas(0, -1)).toBeFalsy(); - }); - - test("has", () => { - expect(df).toBeDefined(); - - // present - expect(df.has(3, "numbers")).toBeTruthy(); - expect(df.has(0, "numbers")).toBeTruthy(); - expect(df.has(3, "colors")).toBeTruthy(); - expect(df.has(0, "colors")).toBeTruthy(); - - // not present - expect(df.has(3, "foo")).toBeFalsy(); - expect(df.has(-1, "numbers")).toBeFalsy(); - expect(df.has(-1, -1)).toBeFalsy(); - expect(df.has(null, null)).toBeFalsy(); - expect(df.has(0, "foo")).toBeFalsy(); - expect(df.has(99, "numbers")).toBeFalsy(); - expect(df.has(99, "foo")).toBeFalsy(); - }); -}); - -describe("dataframe subsetting", () => { - describe("subset", () => { - const sourceDf = new Dataframe.Dataframe( - [3, 4], - [ - new Int32Array([0, 1, 2]), - ["A", "B", "C"], - new Float32Array([4.4, 5.5, 6.6]), - ["red", "green", "blue"] - ], - null, - new Dataframe.KeyIndex(["int32", "string", "float32", "colors"]) - ); - - test("all rows, one column", () => { - const dfA = sourceDf.subset(null, ["colors"]); - expect(dfA).toBeDefined(); - expect(dfA.dims).toEqual([3, 1]); - expect(dfA.iat(0, 0)).toEqual("red"); - expect(dfA.at(2, "colors")).toEqual("blue"); - expect(dfA.col("colors").asArray()).toEqual(["red", "green", "blue"]); - expect(dfA.icol(0).asArray()).toEqual(["red", "green", "blue"]); - expect(dfA.col("colors").asArray()).toEqual( - sourceDf.col("colors").asArray() - ); - expect(dfA.rowIndex.keys()).toEqual(sourceDf.rowIndex.keys()); - expect(dfA.colIndex.keys()).toEqual(["colors"]); - }); - - test("all rows, two columns", () => { - const dfB = sourceDf.subset(null, ["colors", "float32"]); - expect(dfB).toBeDefined(); - expect(dfB.dims).toEqual([3, 2]); - expect(dfB.iat(0, 0)).toBeCloseTo(4.4); - expect(dfB.iat(0, 1)).toEqual("red"); - expect(dfB.at(2, "colors")).toEqual("blue"); - expect(dfB.at(2, "float32")).toBeCloseTo(6.6); - expect(dfB.col("colors").asArray()).toEqual(["red", "green", "blue"]); - expect(dfB.col("float32").asArray()).toEqual( - new Float32Array([4.4, 5.5, 6.6]) - ); - expect(dfB.icol(0).asArray()).toEqual(dfB.col("float32").asArray()); - expect(dfB.icol(1).asArray()).toEqual(dfB.col("colors").asArray()); - expect(dfB.col("colors").asArray()).toEqual( - sourceDf.col("colors").asArray() - ); - expect(dfB.col("float32").asArray()).toEqual( - sourceDf.col("float32").asArray() - ); - expect(dfB.rowIndex.keys()).toEqual(sourceDf.rowIndex.keys()); - expect(dfB.colIndex.keys()).toEqual(["float32", "colors"]); - }); - - test("one row, all columns", () => { - const dfC = sourceDf.subset([1], null); - expect(dfC).toBeDefined(); - expect(dfC.dims).toEqual([1, 4]); - expect(dfC.iat(0, 0)).toEqual(1); - expect(dfC.iat(0, 1)).toEqual("B"); - expect(dfC.iat(0, 2)).toBeCloseTo(5.5); - expect(dfC.iat(0, 3)).toEqual("green"); - expect(dfC.rowIndex.keys()).toEqual(new Int32Array([1])); - expect(dfC.colIndex.keys()).toEqual(sourceDf.colIndex.keys()); - }); - - test("two rows, all columns", () => { - const dfD = sourceDf.subset([0, 2], null); - expect(dfD).toBeDefined(); - expect(dfD.dims).toEqual([2, 4]); - expect(dfD.icol(0).asArray()).toEqual(new Int32Array([0, 2])); - expect(dfD.icol(1).asArray()).toEqual(["A", "C"]); - expect(dfD.icol(2).asArray()).toEqual(new Float32Array([4.4, 6.6])); - expect(dfD.icol(3).asArray()).toEqual(["red", "blue"]); - expect(dfD.rowIndex.keys()).toEqual(new Int32Array([0, 2])); - expect(dfD.colIndex.keys()).toEqual(sourceDf.colIndex.keys()); - }); - - test("all rows, all columns", () => { - const dfE = sourceDf.subset(null, null); - expect(dfE).toBeDefined(); - expect(dfE.dims).toEqual([3, 4]); - expect(dfE.icol(0).asArray()).toEqual(sourceDf.icol(0).asArray()); - expect(dfE.icol(1).asArray()).toEqual(sourceDf.icol(1).asArray()); - expect(dfE.icol(2).asArray()).toEqual(sourceDf.icol(2).asArray()); - expect(dfE.icol(3).asArray()).toEqual(sourceDf.icol(3).asArray()); - expect(dfE.rowIndex.keys()).toEqual(sourceDf.rowIndex.keys()); - expect(dfE.colIndex.keys()).toEqual(sourceDf.colIndex.keys()); - }); - - test("two rows, two colums", () => { - const dfF = sourceDf.subset([0, 2], ["int32", "float32"]); - expect(dfF).toBeDefined(); - expect(dfF.dims).toEqual([2, 2]); - expect(dfF.icol(0).asArray()).toEqual(new Int32Array([0, 2])); - expect(dfF.icol(1).asArray()).toEqual(new Float32Array([4.4, 6.6])); - expect(dfF.rowIndex.keys()).toEqual(new Int32Array([0, 2])); - expect(dfF.colIndex.keys()).toEqual(["int32", "float32"]); - }); - - test("withRowIndex", () => { - const df = sourceDf.subset( - null, - ["int32", "float32"], - new Dataframe.DenseInt32Index([3, 2, 1]) - ); - expect(df.colIndex).toBeInstanceOf(Dataframe.KeyIndex); - expect(df.rowIndex).toBeInstanceOf(Dataframe.DenseInt32Index); - expect(df.at(3, "int32")).toEqual(df.iat(0, 0)); - }); - - test("withRowIndex error checks", () => { - expect(() => - sourceDf.subset(null, ["red"], new Dataframe.IdentityInt32Index(1)) - ).toThrow(RangeError); - expect(() => - sourceDf.subset(null, ["red"], new Dataframe.DenseInt32Index([0, 1])) - ).toThrow(RangeError); - expect(() => - sourceDf.subset(null, ["red"], new Dataframe.KeyIndex([0, 1, 2, 3])) - ).toThrow(RangeError); - }); - }); - - test("isubsetMask", () => { - const sourceDf = new Dataframe.Dataframe( - [3, 4], - [ - new Int32Array([0, 1, 2]), - ["A", "B", "C"], - new Float32Array([4.4, 5.5, 6.6]), - ["red", "green", "blue"] - ], - new Dataframe.DenseInt32Index([2, 4, 6]), - new Dataframe.KeyIndex(["int32", "string", "float32", "colors"]) - ); - - const dfA = sourceDf.isubsetMask( - new Uint8Array([0, 1, 1]), - new Uint8Array([1, 0, 0, 1]) - ); - expect(dfA.dims).toEqual([2, 2]); - expect(dfA.icol(0).asArray()).toEqual(new Int32Array([1, 2])); - expect(dfA.icol(1).asArray()).toEqual(["green", "blue"]); - expect(dfA.rowIndex.keys()).toEqual(new Int32Array([4, 6])); - expect(dfA.colIndex.keys()).toEqual(["int32", "colors"]); - }); -}); - -describe("dataframe factories", () => { - test("create", () => { - const df = Dataframe.Dataframe.create( - [3, 3], - [ - new Array(3).fill(0), - new Int16Array(3).fill(99), - new Float64Array(3).fill(1.1) - ] - ); - - expect(df).toBeDefined(); - expect(df.dims).toEqual([3, 3]); - expect(df).toHaveLength(3); - expect(df.iat(0, 0)).toEqual(0); - expect(df.iat(1, 1)).toEqual(99); - expect(df.iat(2, 2)).toBeCloseTo(1.1); - expect(df.iat(0, 0)).toEqual(df.at(0, 0)); - expect(df.iat(1, 1)).toEqual(df.at(1, 1)); - expect(df.iat(2, 2)).toEqual(df.at(2, 2)); - }); - - test("clone", () => { - const dfA = new Dataframe.Dataframe( - [3, 2], - [new Int32Array([0, 1, 2]), new Int32Array([3, 4, 5])], - new Dataframe.DenseInt32Index([2, 1, 0]), - new Dataframe.KeyIndex(["A", "B"]) - ); - - const dfB = dfA.clone(); - expect(dfB).not.toBe(dfA); - expect(dfB.dims).toEqual(dfA.dims); - expect(dfB).toHaveLength(dfA.length); - expect(dfB.rowIndex.keys()).toEqual(dfA.rowIndex.keys()); - expect(dfB.colIndex.keys()).toEqual(dfA.colIndex.keys()); - for (let i = 0, l = dfB.dims[1]; i < l; i += 1) { - expect(dfB.icol(i).asArray()).toEqual(dfA.icol(i).asArray()); - } - }); - - describe("withCol", () => { - test("KeyIndex", () => { - const df = new Dataframe.Dataframe( - [2, 2], - [["red", "blue"], [true, false]], - null, - new Dataframe.KeyIndex(["colors", "bools"]) - ); - const dfA = df.withCol("numbers", [1, 0]); - - expect(dfA).toBeDefined(); - expect(dfA.dims).toEqual([2, 3]); - expect(dfA.icol(0).asArray()).toEqual(["red", "blue"]); - expect(dfA.icol(1).asArray()).toEqual([true, false]); - expect(dfA.icol(2).asArray()).toEqual([1, 0]); - expect(dfA.col("numbers").asArray()).toEqual([1, 0]); - expect(dfA.colIndex.keys()).toEqual(["colors", "bools", "numbers"]); - expect(df.colIndex.keys()).toEqual(["colors", "bools"]); - expect(df.rowIndex.keys()).toEqual(dfA.rowIndex.keys()); - }); - - test("DenseInt32Index", () => { - const df = new Dataframe.Dataframe( - [2, 2], - [["red", "blue"], [true, false]], - null, - new Dataframe.DenseInt32Index([74, 75]) - ); - const dfA = df.withCol(72, [1, 0]); - - expect(dfA).toBeDefined(); - expect(dfA.dims).toEqual([2, 3]); - expect(dfA.icol(0).asArray()).toEqual(["red", "blue"]); - expect(dfA.icol(1).asArray()).toEqual([true, false]); - expect(dfA.icol(2).asArray()).toEqual([1, 0]); - expect(dfA.col(74).asArray()).toEqual(["red", "blue"]); - expect(dfA.col(75).asArray()).toEqual([true, false]); - expect(dfA.col(72).asArray()).toEqual([1, 0]); - expect(dfA.colIndex.keys()).toEqual(new Int32Array([74, 75, 72])); - expect(df.colIndex.keys()).toEqual(new Int32Array([74, 75])); - expect(df.rowIndex.keys()).toEqual(dfA.rowIndex.keys()); - }); - - test("DenseInt32Index promote", () => { - const df = new Dataframe.Dataframe( - [2, 2], - [["red", "blue"], [true, false]], - null, - new Dataframe.DenseInt32Index([74, 75]) - ); - const dfA = df.withCol(999, [1, 0]); - - expect(dfA).toBeDefined(); - expect(dfA.dims).toEqual([2, 3]); - expect(dfA.icol(0).asArray()).toEqual(["red", "blue"]); - expect(dfA.icol(1).asArray()).toEqual([true, false]); - expect(dfA.icol(2).asArray()).toEqual([1, 0]); - expect(dfA.col(74).asArray()).toEqual(["red", "blue"]); - expect(dfA.col(75).asArray()).toEqual([true, false]); - expect(dfA.col(999).asArray()).toEqual([1, 0]); - expect(dfA.colIndex.keys()).toEqual(new Int32Array([74, 75, 999])); - expect(df.colIndex.keys()).toEqual(new Int32Array([74, 75])); - expect(df.rowIndex.keys()).toEqual(dfA.rowIndex.keys()); - }); - - test("IdentityInt32Index with last", () => { - const df = new Dataframe.Dataframe( - [2, 2], - [["red", "blue"], [true, false]], - null, - null - ); - const dfA = df.withCol(2, [1, 0]); - - expect(dfA).toBeDefined(); - expect(dfA.dims).toEqual([2, 3]); - expect(dfA.icol(0).asArray()).toEqual(["red", "blue"]); - expect(dfA.icol(1).asArray()).toEqual([true, false]); - expect(dfA.icol(2).asArray()).toEqual([1, 0]); - expect(dfA.col(0).asArray()).toEqual(["red", "blue"]); - expect(dfA.col(1).asArray()).toEqual([true, false]); - expect(dfA.col(2).asArray()).toEqual([1, 0]); - expect(dfA.colIndex.keys()).toEqual(new Int32Array([0, 1, 2])); - expect(df.colIndex.keys()).toEqual(new Int32Array([0, 1])); - expect(df.rowIndex.keys()).toEqual(dfA.rowIndex.keys()); - }); - - test("IdentityInt32Index promote", () => { - const df = new Dataframe.Dataframe( - [2, 2], - [["red", "blue"], [true, false]], - null, - null - ); - const dfA = df.withCol(99, [1, 0]); - - expect(dfA).toBeDefined(); - expect(dfA.dims).toEqual([2, 3]); - expect(dfA.icol(0).asArray()).toEqual(["red", "blue"]); - expect(dfA.icol(1).asArray()).toEqual([true, false]); - expect(dfA.icol(2).asArray()).toEqual([1, 0]); - expect(dfA.col(0).asArray()).toEqual(["red", "blue"]); - expect(dfA.col(1).asArray()).toEqual([true, false]); - expect(dfA.col(99).asArray()).toEqual([1, 0]); - expect(dfA.colIndex.keys()).toEqual(new Int32Array([0, 1, 99])); - expect(df.colIndex.keys()).toEqual(new Int32Array([0, 1])); - expect(df.rowIndex.keys()).toEqual(dfA.rowIndex.keys()); - }); - - describe("handle column dimensions correctly", () => { - /* - there are two conditions: - - empty dataframe - will accept an add of any dimensionality - - non-empty dataframe - added column must match row-count dimension - */ - test("empty.withCol", () => { - const edf = Dataframe.Dataframe.empty(); - const df = edf.withCol("foo", [1, 2, 3]); - - expect(edf).toBeDefined(); - expect(df).toBeDefined(); - expect(edf).not.toEqual(df); - expect(df.dims).toEqual([3, 1]); - expect(df.icol(0).asArray()).toEqual([1, 2, 3]); - }); - - test("withCol dimension check", () => { - const dfA = new Dataframe.Dataframe([1, 1], [["a"]]); - expect(() => { - dfA.withCol(1, []); - }).toThrow(RangeError); - }); - }); - }); - - describe("dropCol", () => { - test("KeyIndex", () => { - const df = new Dataframe.Dataframe( - [2, 3], - [["red", "blue"], [true, false], [1, 0]], - null, - new Dataframe.KeyIndex(["colors", "bools", "numbers"]) - ); - const dfA = df.dropCol("colors"); - - expect(dfA).toBeDefined(); - expect(dfA.dims).toEqual([2, 2]); - expect(dfA.icol(0).asArray()).toEqual([true, false]); - expect(dfA.icol(1).asArray()).toEqual([1, 0]); - expect(dfA.col("numbers").asArray()).toEqual([1, 0]); - expect(dfA.colIndex.keys()).toEqual(["bools", "numbers"]); - expect(df.colIndex.keys()).toEqual(["colors", "bools", "numbers"]); - expect(df.rowIndex.keys()).toEqual(dfA.rowIndex.keys()); - }); - - test("IdentityInt32Index drop first", () => { - const df = new Dataframe.Dataframe( - [2, 3], - [["red", "blue"], [true, false], [1, 0]], - null, - null - ); - const dfA = df.dropCol(0); - - expect(dfA).toBeDefined(); - expect(dfA.dims).toEqual([2, 2]); - expect(dfA.icol(0).asArray()).toEqual([true, false]); - expect(dfA.icol(1).asArray()).toEqual([1, 0]); - expect(df.col(1).asArray()).toEqual(dfA.col(1).asArray()); - expect(df.col(2).asArray()).toEqual(dfA.col(2).asArray()); - expect(dfA.colIndex.keys()).toEqual(new Int32Array([1, 2])); - expect(df.colIndex.keys()).toEqual(new Int32Array([0, 1, 2])); - expect(df.rowIndex.keys()).toEqual(dfA.rowIndex.keys()); - }); - - test("IdentityInt32Index drop last", () => { - const df = new Dataframe.Dataframe( - [2, 3], - [["red", "blue"], [true, false], [1, 0]], - null, - null - ); - const dfA = df.dropCol(2); - - expect(dfA).toBeDefined(); - expect(dfA.dims).toEqual([2, 2]); - expect(dfA.icol(0).asArray()).toEqual(["red", "blue"]); - expect(dfA.icol(1).asArray()).toEqual([true, false]); - expect(df.col(0).asArray()).toEqual(dfA.col(0).asArray()); - expect(df.col(1).asArray()).toEqual(dfA.col(1).asArray()); - expect(dfA.colIndex.keys()).toEqual(new Int32Array([0, 1])); - expect(df.colIndex.keys()).toEqual(new Int32Array([0, 1, 2])); - expect(df.rowIndex.keys()).toEqual(dfA.rowIndex.keys()); - }); - - test("DenseInt32Index", () => { - const df = new Dataframe.Dataframe( - [2, 3], - [["red", "blue"], [true, false], [1, 0]], - null, - new Dataframe.DenseInt32Index([102, 101, 100]) - ); - const dfA = df.dropCol(101); - - expect(dfA).toBeDefined(); - expect(dfA.dims).toEqual([2, 2]); - expect(dfA.icol(0).asArray()).toEqual(["red", "blue"]); - expect(dfA.icol(1).asArray()).toEqual([1, 0]); - expect(dfA.col(100).asArray()).toEqual([1, 0]); - expect(dfA.col(102).asArray()).toEqual(["red", "blue"]); - expect(dfA.colIndex.keys()).toEqual(new Int32Array([102, 100])); - expect(df.colIndex.keys()).toEqual(new Int32Array([102, 101, 100])); - expect(df.rowIndex.keys()).toEqual(dfA.rowIndex.keys()); - }); - }); - - describe("mapColumns", () => { - test("identity", () => { - const dfA = Dataframe.Dataframe.create( - [3, 3], - [ - new Array(3).fill(0), - new Int16Array(3).fill(99), - new Float64Array(3).fill(1.1) - ] - ); - const dfB = dfA.mapColumns((col, idx) => { - expect(dfA.icol(idx).asArray()).toBe(col); - return col; - }); - expect(dfA).not.toBe(dfB); - expect(dfA.dims).toEqual(dfB.dims); - for (let c = 0; c < dfA.dims[1]; c += 1) { - expect(dfA.icol(c).asArray()).toBe(dfB.icol(c).asArray()); - } - }); - - test("transform", () => { - const dfA = Dataframe.Dataframe.create( - [3, 3], - [new Array(3).fill(0), new Array(3).fill(0), new Array(3).fill(0)] - ); - const dfB = dfA.mapColumns(() => { - return new Array(3).fill(1); - }); - expect(dfA).not.toBe(dfB); - expect(dfB.iat(0, 0)).toEqual(1); - expect(dfB.iat(0, 1)).toEqual(1); - expect(dfB.iat(0, 2)).toEqual(1); - }); - }); -}); - -describe("dataframe col", () => { - let df = null; - beforeEach(() => { - df = new Dataframe.Dataframe( - [2, 2], - [[true, false], [1, 0]], - null, - new Dataframe.KeyIndex(["A", "B"]) - ); - }); - - test("col", () => { - expect(df).toBeDefined(); - expect(df.col("A")).toBe(df.icol(0)); - expect(df.col("B")).toBe(df.icol(1)); - expect(df.col("undefined")).toBeUndefined(); - expect(df.icol("undefined")).toBeUndefined(); - - const colA = df.col("A"); - expect(colA).toBeInstanceOf(Function); - expect(colA.asArray).toBeInstanceOf(Function); - expect(colA.has).toBeInstanceOf(Function); - expect(colA.ihas).toBeInstanceOf(Function); - expect(colA.indexOf).toBeInstanceOf(Function); - expect(colA.iget).toBeInstanceOf(Function); - }); - - test("col.asArray", () => { - expect(df).toBeDefined(); - expect(df.col("A").asArray()).toEqual([true, false]); - expect(df.icol(0).asArray()).toEqual([true, false]); - expect(df.col("B").asArray()).toEqual([1, 0]); - expect(df.icol(1).asArray()).toEqual([1, 0]); - }); - - test("col.has", () => { - expect(df).toBeDefined(); - expect(df.col("A").has(-1)).toBe(false); - expect(df.col("A").has(0)).toBe(true); - expect(df.col("A").has(1)).toBe(true); - expect(df.col("A").has(2)).toBe(false); - expect(df.col("B").has(-1)).toBe(false); - expect(df.col("B").has(0)).toBe(true); - expect(df.col("B").has(1)).toBe(true); - expect(df.col("B").has(2)).toBe(false); - }); - - test("col.ihas", () => { - expect(df).toBeDefined(); - expect(df.col("A").ihas(-1)).toBe(false); - expect(df.col("A").ihas(0)).toBe(true); - expect(df.col("A").ihas(1)).toBe(true); - expect(df.col("A").ihas(2)).toBe(false); - expect(df.col("B").ihas(-1)).toBe(false); - expect(df.col("B").ihas(0)).toBe(true); - expect(df.col("B").ihas(1)).toBe(true); - expect(df.col("B").ihas(2)).toBe(false); - }); - - test("col.iget", () => { - expect(df).toBeDefined(); - expect(df.col("A").iget(0)).toEqual(df.iat(0, 0)); - expect(df.col("B").iget(1)).toEqual(df.iat(1, 1)); - }); - - test("col.indexOf", () => { - expect(df).toBeDefined(); - expect(df.col("A").indexOf(true)).toEqual(0); - expect(df.col("A").indexOf(false)).toEqual(1); - expect(df.col("A").indexOf(99)).toBeUndefined(); - expect(df.col("A").indexOf(undefined)).toBeUndefined(); - expect(df.col("A").indexOf(1)).toBeUndefined(); - - expect(df.col("B").indexOf(1)).toEqual(0); - expect(df.col("B").indexOf(0)).toEqual(1); - expect(df.col("B").indexOf(99)).toBeUndefined(); - expect(df.col("B").indexOf(undefined)).toBeUndefined(); - expect(df.col("B").indexOf(true)).toBeUndefined(); - }); -}); diff --git a/client/__tests__/util/dataframe/summarize.test.js b/client/__tests__/util/dataframe/summarize.test.js deleted file mode 100644 index 702d0fae..00000000 --- a/client/__tests__/util/dataframe/summarize.test.js +++ /dev/null @@ -1,253 +0,0 @@ -import * as Dataframe from "../../../src/util/dataframe"; - -function float32Conversion(f) { - return new Float32Array([f])[0]; -} - -describe("Dataframe column summary", () => { - test("empty column test", () => { - const df = Dataframe.Dataframe.create([0, 1], [[]]); - const summary = df.icol(0).summarize(); - expect(summary).toEqual( - expect.objectContaining({ - categorical: true, - categories: [], - categoryCounts: new Map(), - numCategories: 0 - }) - ); - }); - - test("simple test", () => { - const df = new Dataframe.Dataframe( - [1, 6], - [ - ["n1"], - ["hi"], - [true], - new Float32Array([39.3]), - new Int32Array([99]), - [1] - ], - null, - new Dataframe.KeyIndex([ - "name", - "nameString", - "nameBoolean", - "nameFloat32", - "nameInt32", - "nameCategorical" - ]) - ); - - expect(df.icol(0).summarize()).toEqual( - expect.objectContaining({ - categorical: true, - categories: ["n1"], - categoryCounts: new Map([["n1", 1]]), - numCategories: 1 - }) - ); - expect(df.icol(1).summarize()).toEqual( - expect.objectContaining({ - categorical: true, - categories: ["hi"], - categoryCounts: new Map([["hi", 1]]), - numCategories: 1 - }) - ); - expect(df.icol(2).summarize()).toEqual( - expect.objectContaining({ - categorical: true, - categories: [true], - categoryCounts: new Map([[true, 1]]), - numCategories: 1 - }) - ); - expect(df.icol(3).summarize()).toEqual( - expect.objectContaining({ - categorical: false, - min: float32Conversion(39.3), - max: float32Conversion(39.3), - nan: 0, - ninf: 0, - pinf: 0 - }) - ); - expect(df.icol(4).summarize()).toEqual( - expect.objectContaining({ - categorical: false, - min: 99, - max: 99, - nan: 0, - ninf: 0, - pinf: 0 - }) - ); - expect(df.icol(5).summarize()).toEqual( - expect.objectContaining({ - categorical: true, - categories: [1], - categoryCounts: new Map([[1, 1]]), - numCategories: 1 - }) - ); - }); - - test("multi test", () => { - const df = new Dataframe.Dataframe( - [3, 6], - [ - ["n0", "n1", "n2"], - ["hi", "hi", "bye"], - [false, true, true], - new Float32Array([39.3, 39.3, 0]), - new Int32Array([99, 99, 99]), - [1, false, "0"] - ], - null, - new Dataframe.KeyIndex([ - "name", - "nameString", - "nameBoolean", - "nameFloat32", - "nameInt32", - "nameCategorical" - ]) - ); - - expect(df.icol(0).summarize()).toEqual( - expect.objectContaining({ - categorical: true, - categories: expect.arrayContaining(["n0", "n1", "n2"]), - categoryCounts: new Map([["n0", 1], ["n1", 1], ["n2", 1]]), - numCategories: 3 - }) - ); - expect(df.icol(1).summarize()).toEqual( - expect.objectContaining({ - categorical: true, - categories: expect.arrayContaining(["hi", "bye"]), - categoryCounts: new Map([["hi", 2], ["bye", 1]]), - numCategories: 2 - }) - ); - expect(df.icol(2).summarize()).toEqual( - expect.objectContaining({ - categorical: true, - categories: expect.arrayContaining([true, false]), - categoryCounts: new Map([[true, 2], [false, 1]]), - numCategories: 2 - }) - ); - expect(df.icol(3).summarize()).toEqual( - expect.objectContaining({ - categorical: false, - min: 0, - max: float32Conversion(39.3), - nan: 0, - ninf: 0, - pinf: 0 - }) - ); - expect(df.icol(4).summarize()).toEqual( - expect.objectContaining({ - categorical: false, - min: 99, - max: 99, - nan: 0, - ninf: 0, - pinf: 0 - }) - ); - expect(df.icol(5).summarize()).toEqual( - expect.objectContaining({ - categorical: true, - categories: expect.arrayContaining([1, false, "0"]), - categoryCounts: new Map([[1, 1], [false, 1], ["0", 1]]), - numCategories: 3 - }) - ); - }); - - test("non-finite numbers", () => { - const df = new Dataframe.Dataframe( - [4, 6], - [ - ["n0", "n1", "n2", "n2"], - ["hi", "hi", "bye", "bye"], - [false, true, true, true], - new Float32Array([ - 39.3, - Number.NEGATIVE_INFINITY, - Number.NaN, - Number.POSITIVE_INFINITY - ]), - new Int32Array([99, 99, 99, 99]), - [1, false, "0", "0"] - ], - null, - new Dataframe.KeyIndex([ - "name", - "nameString", - "nameBoolean", - "nameFloat32", - "nameInt32", - "nameCategorical" - ]) - ); - - expect(df.icol(0).summarize()).toEqual( - expect.objectContaining({ - categorical: true, - categories: expect.arrayContaining(["n0", "n1", "n2"]), - categoryCounts: new Map([["n0", 1], ["n1", 1], ["n2", 2]]), - numCategories: 3 - }) - ); - expect(df.icol(1).summarize()).toEqual( - expect.objectContaining({ - categorical: true, - categories: expect.arrayContaining(["hi", "bye"]), - categoryCounts: new Map([["hi", 2], ["bye", 1]]), - numCategories: 2 - }) - ); - expect(df.icol(2).summarize()).toEqual( - expect.objectContaining({ - categorical: true, - categories: expect.arrayContaining([true, false]), - categoryCounts: new Map([[true, 2], [false, 1]]), - numCategories: 2 - }) - ); - expect(df.icol(3).summarize()).toEqual( - expect.objectContaining({ - categorical: false, - min: float32Conversion(39.3), - max: float32Conversion(39.3), - nan: 1, - ninf: 1, - pinf: 1 - }) - ); - expect(df.icol(4).summarize()).toEqual( - expect.objectContaining({ - categorical: false, - min: 99, - max: 99, - nan: 0, - ninf: 0, - pinf: 0 - }) - ); - expect(df.icol(5).summarize()).toEqual( - expect.objectContaining({ - categorical: true, - categories: expect.arrayContaining([1, false, "0"]), - categoryCounts: new Map([[1, 1], [false, 1], ["0", 1]]), - numCategories: 3 - }) - ); - }); -}); diff --git a/client/__tests__/util/nameCreators.test.js b/client/__tests__/util/nameCreators.test.js deleted file mode 100644 index 5c6f462c..00000000 --- a/client/__tests__/util/nameCreators.test.js +++ /dev/null @@ -1,68 +0,0 @@ -/* -Test cases for nameCreators.js. -*/ -import { - layoutDimensionName, - obsAnnoDimensionName, - diffexpDimensionName, - userDefinedDimensionName, - makeContinuousDimensionName -} from "../../src/util/nameCreators"; - -describe("nameCreators", () => { - const nameCreators = [ - layoutDimensionName, - obsAnnoDimensionName, - diffexpDimensionName, - userDefinedDimensionName - ]; - - test("check for namespace isolation", () => { - const foo = "foo"; - nameCreators.forEach(fn => expect(fn(foo)).not.toBe(foo)); - - const bar = "bar"; - nameCreators.forEach(fn => expect(fn(foo)).not.toBe(fn(bar))); - - nameCreators.forEach(fn => { - const allOtherFn = nameCreators.filter(elmnt => elmnt !== fn); - allOtherFn.forEach(otherFn => expect(fn(foo)).not.toBe(otherFn(foo))); - }); - }); - - test("check for legal keys", () => { - /* need namespace creators to return strings only */ - nameCreators.forEach(fn => expect(fn("X")).toMatch(/X/)); - }); -}); - -describe("makeContinuousDimensionName", () => { - test("namespaces", () => { - expect(makeContinuousDimensionName({ isObs: true }, "OK")).toMatch(/OK/); - expect(makeContinuousDimensionName({ isDiffExp: true }, "OK")).toMatch( - /OK/ - ); - expect(makeContinuousDimensionName({ isUserDefined: true }, "OK")).toMatch( - /OK/ - ); - }); - - test("error handling", () => { - expect(() => makeContinuousDimensionName({}, "oops")).toThrow(); - expect(() => - makeContinuousDimensionName( - { isObs: false, isDiffExp: false, isUserDefined: false }, - "oops" - ) - ).toThrow(); - expect(() => - makeContinuousDimensionName({ isObs: true }, "OK") - ).not.toThrow(); - expect(() => - makeContinuousDimensionName({ isDiffExp: true }, "OK") - ).not.toThrow(); - expect(() => - makeContinuousDimensionName({ isUserDefined: true }, "OK") - ).not.toThrow(); - }); -}); diff --git a/client/__tests__/util/quantile.test.js b/client/__tests__/util/quantile.test.js deleted file mode 100644 index b8b49954..00000000 --- a/client/__tests__/util/quantile.test.js +++ /dev/null @@ -1,29 +0,0 @@ -import quantile from "../../src/util/quantile"; - -describe("quantile", () => { - test("single q", () => { - const arr = new Float32Array([9, 3, 5, 6, 0]); - expect(quantile([1.0], arr)).toMatchObject([9]); - expect(quantile([0.9], arr)).toMatchObject([9]); - expect(quantile([0.8], arr)).toMatchObject([9]); - expect(quantile([0.7], arr)).toMatchObject([6]); - expect(quantile([0.6], arr)).toMatchObject([6]); - expect(quantile([0.5], arr)).toMatchObject([5]); - expect(quantile([0.4], arr)).toMatchObject([5]); - expect(quantile([0.3], arr)).toMatchObject([3]); - expect(quantile([0.2], arr)).toMatchObject([3]); - expect(quantile([0.1], arr)).toMatchObject([0]); - expect(quantile([0], arr)).toMatchObject([0]); - }); - - test("multi q", () => { - const arr = new Float32Array([9, 3, 5, 6, 0]); - expect(quantile([0, 0.25, 0.5, 0.75, 1.0], arr)).toMatchObject([ - 0, - 3, - 5, - 6, - 9 - ]); - }); -}); diff --git a/client/__tests__/util/range.test.js b/client/__tests__/util/range.test.js deleted file mode 100644 index 0567c9eb..00000000 --- a/client/__tests__/util/range.test.js +++ /dev/null @@ -1,42 +0,0 @@ -import { range, rangeFill } from "../../src/util/range"; - -describe("range", () => { - test("no defaults", () => { - expect(range(0, 3, 1)).toMatchObject([0, 1, 2]); - }); - - test("range(stop)", () => { - expect(range(3)).toMatchObject([0, 1, 2]); - expect(range(0)).toMatchObject([]); - expect(range(1)).toMatchObject([0]); - }); - - test("range(start,stop)", () => { - expect(range(0, 0)).toMatchObject([]); - expect(range(0, 2)).toMatchObject([0, 1]); - expect(range(4, 8)).toMatchObject([4, 5, 6, 7]); - }); - - test("range(start, stop, step", () => { - expect(range(4, 0, -1)).toMatchObject([4, 3, 2, 1]); - expect(range(0, 4, 2)).toMatchObject([0, 2]); - }); -}); - -describe("rangefill", () => { - test("rangeFill(arr)", () => { - expect(rangeFill(new Int32Array(3))).toMatchObject( - new Int32Array([0, 1, 2]) - ); - }); - test("rangeFill(arr, start)", () => { - expect(rangeFill(new Int32Array(2), 1)).toMatchObject( - new Int32Array([1, 2]) - ); - }); - test("rangeFill(arr, start, step)", () => { - expect(rangeFill(new Int32Array(3), 2, -1)).toMatchObject( - new Int32Array([2, 1, 0]) - ); - }); -}); diff --git a/client/__tests__/util/stateManager/sampleResponses.js b/client/__tests__/util/stateManager/sampleResponses.js deleted file mode 100644 index 2b66bac0..00000000 --- a/client/__tests__/util/stateManager/sampleResponses.js +++ /dev/null @@ -1,193 +0,0 @@ -/* eslint no-bitwise: "off" */ -import _ from "lodash"; -import { flatbuffers } from "flatbuffers"; -import { NetEncoding } from "../../../src/util/stateManager/matrix_generated"; - -/* -test data mocking REST 0.2 API responses. Used in several tests. -*/ -const nObs = 10; -const nVar = 32; -const field4Categories = [83, true, "foo", 2.222222]; -const fieldDCategories = [99, false, "mumble", 3.1415]; - -const aConfigResponse = { - config: { - features: [ - { method: "POST", path: "/cluster/", available: false }, - { method: "POST", path: "/layout/", available: false }, - { method: "POST", path: "/diffexp/", available: false }, - { method: "POST", path: "/saveLocal/", available: false } - ], - displayNames: { - engine: "the little engine that could", - dataset: "all your zeros are mine" - } - } -}; - -const aSchemaResponse = { - schema: { - dataframe: { - nObs, - nVar, - type: "float32" - }, - annotations: { - obs: { - index: "name", - columns: [ - { name: "name", type: "string" }, - { name: "field1", type: "int32" }, - { name: "field2", type: "float32" }, - { name: "field3", type: "boolean" }, - { - name: "field4", - type: "categorical", - categories: field4Categories - } - ] - }, - var: { - index: "name", - columns: [ - { name: "name", type: "string" }, - { name: "fieldA", type: "int32" }, - { name: "fieldB", type: "float32" }, - { name: "fieldC", type: "boolean" }, - { - name: "fieldD", - type: "categorical", - categories: fieldDCategories - } - ] - } - }, - layout: { - obs: [{ name: "umap", type: "float32", dims: ["umap_0", "umap_1"] }], - var: [] - } - } -}; - -const anAnnotationsObsJSONResponse = { - names: ["name", "field1", "field2", "field3", "field4"], - data: _() - .range(nObs) - .map(idx => [ - idx, - `obs${idx}`, - 2 * idx, - idx + 0.0133, - !!(idx & 1), - field4Categories[idx % field4Categories.length] - ]) - .value() -}; - -const anAnnotationsVarJSONResponse = { - names: ["fieldA", "fieldB", "fieldC", "fieldD", "name"], - data: _() - .range(nVar) - .map(idx => [ - idx, - 10 * idx, - idx + 2.90143, - !!(idx & 1), - fieldDCategories[idx % fieldDCategories.length], - `var${idx}` - ]) - .value() -}; - -function encodeTypedArray(builder, uType, uData) { - const uTypeName = NetEncoding.TypedArray[uType]; - const ArrayType = NetEncoding[uTypeName]; - const dv = ArrayType.createDataVector(builder, uData); - builder.startObject(1); - builder.addFieldOffset(0, dv, 0); - return builder.endObject(); -} - -function encodeMatrix(columns, colIndex = undefined) { - const utf8Encoder = new TextEncoder("utf-8"); - const builder = new flatbuffers.Builder(1024); - const cols = _.map(columns, carr => { - let uType; - let tarr; - if (_.every(carr, _.isNumber)) { - uType = NetEncoding.TypedArray.Float32Array; - tarr = encodeTypedArray(builder, uType, new Float32Array(carr)); - } else { - uType = NetEncoding.TypedArray.JSONEncodedArray; - const json = JSON.stringify(carr); - const jsonUTF8 = utf8Encoder.encode(json); - tarr = encodeTypedArray(builder, uType, jsonUTF8); - } - NetEncoding.Column.startColumn(builder); - NetEncoding.Column.addUType(builder, uType); - NetEncoding.Column.addU(builder, tarr); - return NetEncoding.Column.endColumn(builder); - }); - - const encColumns = NetEncoding.Matrix.createColumnsVector(builder, cols); - - let encColIndex; - if (colIndex) { - encColIndex = encodeTypedArray( - builder, - NetEncoding.TypedArray.JSONEncodedArray, - utf8Encoder.encode(JSON.stringify(colIndex)) - ); - } - - NetEncoding.Matrix.startMatrix(builder); - NetEncoding.Matrix.addNRows(builder, columns[0].length); - NetEncoding.Matrix.addNCols(builder, columns.length); - NetEncoding.Matrix.addColumns(builder, encColumns); - if (colIndex) { - NetEncoding.Matrix.addColIndexType( - builder, - NetEncoding.TypedArray.JSONEncodedArray - ); - NetEncoding.Matrix.addColIndex(builder, encColIndex); - } - const root = NetEncoding.Matrix.endMatrix(builder); - builder.finish(root); - return builder.asUint8Array(); -} - -const anAnnotationsObsFBSResponse = (() => { - const columns = _.zip(...anAnnotationsObsJSONResponse.data).slice(1); - return encodeMatrix(columns, anAnnotationsObsJSONResponse.names); -})(); - -const anAnnotationsVarFBSResponse = (() => { - const columns = _.zip(...anAnnotationsVarJSONResponse.data).slice(1); - return encodeMatrix(columns, anAnnotationsVarJSONResponse.names); -})(); - -const aLayoutFBSResponse = (() => { - const coords = [ - new Float32Array(nObs).fill(Math.random()), - new Float32Array(nObs).fill(Math.random()) - ]; - return encodeMatrix(coords, ["umap_0", "umap_1"]); -})(); - -const aDataObsResponse = { - var: [2, 4, 29], - obs: _() - .range(nObs) - .map(idx => [idx, Math.random(), Math.random(), Math.random()]) - .value() -}; - -export { - aLayoutFBSResponse as layoutObs, - aDataObsResponse as dataObs, - anAnnotationsVarFBSResponse as annotationsVar, - anAnnotationsObsFBSResponse as annotationsObs, - aSchemaResponse as schema, - aConfigResponse as config -}; diff --git a/client/__tests__/util/stateManager/universe.test.js b/client/__tests__/util/stateManager/universe.test.js deleted file mode 100644 index 3bac0b69..00000000 --- a/client/__tests__/util/stateManager/universe.test.js +++ /dev/null @@ -1,68 +0,0 @@ -import * as Universe from "../../../src/util/stateManager/universe"; -import * as Dataframe from "../../../src/util/dataframe"; -import * as REST from "./sampleResponses"; - -describe("createUniverseFromResponse", () => { - /* - test createUniverseFromResponse - this function converts - a set of REST 0.2 responses into a "new" Universe. - - createUniverseFromResponse( - configResponse, - schemaResponse, - annotationsObsResponse, - annotationsVarResponse, - layoutObsResponse - ) --> Universe - - where: - configResponse: GET /.../config - schemaResponse: GET /.../schema - annotationsObsResponse: GET /.../annotations/obs - annotationsVarResponse: GET /.../annotations/var - layoutObsResponse: GET /.../layout/obs - - See spec in docs/REST_API.md. - */ - - test("create from test data", () => { - /* - create a universe from sample data nad validate its shape & contents - */ - const { nObs, nVar } = REST.schema.schema.dataframe; - const universe = Universe.createUniverseFromResponse( - REST.config, - REST.schema, - REST.annotationsObs, - REST.annotationsVar, - REST.layoutObs - ); - - expect(universe).toBeDefined(); - expect(universe).toMatchObject( - expect.objectContaining({ - nObs, - nVar, - schema: REST.schema.schema, - obsAnnotations: expect.any(Dataframe.Dataframe), - varAnnotations: expect.any(Dataframe.Dataframe), - obsLayout: expect.any(Dataframe.Dataframe), - varData: expect.any(Dataframe.Dataframe) - }) - ); - - expect(universe.obsAnnotations.dims).toEqual([ - nObs, - REST.schema.schema.annotations.obs.columns.length - ]); - expect(universe.obsLayout.dims).toEqual([nObs, 2]); - expect(universe.obsLayout.colIndex.keys()).toEqual( - universe.schema.layout.obs[0].dims - ); - expect(universe.varAnnotations.dims).toEqual([ - nVar, - REST.schema.schema.annotations.var.columns.length - ]); - expect(universe.varData.isEmpty()).toBeTruthy(); - }); -}); diff --git a/client/__tests__/util/stateManager/world.test.js b/client/__tests__/util/stateManager/world.test.js deleted file mode 100644 index 6ba3424b..00000000 --- a/client/__tests__/util/stateManager/world.test.js +++ /dev/null @@ -1,190 +0,0 @@ -import _ from "lodash"; -import * as Universe from "../../../src/util/stateManager/universe"; -import * as World from "../../../src/util/stateManager/world"; -import * as Dataframe from "../../../src/util/dataframe"; -import Crossfilter from "../../../src/util/typedCrossfilter"; -import { DimTypes } from "../../../src/util/typedCrossfilter/crossfilter"; -import * as REST from "./sampleResponses"; -import { - obsAnnoDimensionName, - layoutDimensionName -} from "../../../src/util/nameCreators"; - -/* -Helper - creates universe, world, corssfilter and dimensionMap from -the default REST test response. -*/ -const defaultBigBang = () => { - /* create unverse, world, crossfilter and dimensionMap */ - /* create universe */ - const universe = Universe.createUniverseFromResponse( - REST.config, - REST.schema, - REST.annotationsObs, - REST.annotationsVar, - REST.layoutObs - ); - /* create world */ - const world = World.createWorldFromEntireUniverse(universe); - /* create crossfilter */ - const crossfilter = World.createObsDimensions( - new Crossfilter(world.obsAnnotations), - world, - REST.schema.schema.layout.obs[0].dims - ); - - return { - universe, - world, - crossfilter - }; -}; - -describe("createWorldFromEntireUniverse", () => { - test("create from REST sample", () => { - const universe = Universe.createUniverseFromResponse( - REST.config, - REST.schema, - REST.annotationsObs, - REST.annotationsVar, - REST.layoutObs - ); - expect(universe).toBeDefined(); - - const world = World.createWorldFromEntireUniverse(universe); - expect(world).toBeDefined(); - - expect(world).toMatchObject( - expect.objectContaining({ - nObs: universe.nObs, - nVar: universe.nVar, - schema: universe.schema, - obsAnnotations: expect.any(Dataframe.Dataframe), - varAnnotations: expect.any(Dataframe.Dataframe), - obsLayout: expect.any(Dataframe.Dataframe), - varData: expect.any(Dataframe.Dataframe), - clipQuantiles: { min: 0, max: 1 }, - unclipped: { - obsAnnotations: expect.any(Dataframe.Dataframe), - varData: expect.any(Dataframe.Dataframe) - } - }) - ); - }); -}); - -describe("createWorldFromCurrentSelection", () => { - test("create from REST sample", () => { - const { - universe, - world: originalWorld, - crossfilter: originalCrossfilter - } = defaultBigBang(); - - /* mock a selection */ - const crossfilter = originalCrossfilter - .select(obsAnnoDimensionName("field1"), { mode: "range", lo: 0, hi: 5 }) - .select(obsAnnoDimensionName("field3"), { - mode: "exact", - values: [false] - }); - - /* create the world from the selection */ - const world = World.createWorldBySelection( - universe, - originalWorld, - crossfilter - ); - expect(world).toBeDefined(); - expect(world.nObs).toEqual(crossfilter.countSelected()); - - /* - calculate expected values and match against result - */ - - /* matchFilter must match the dimension filters above */ - const matchFilter = (df, row) => { - const field1 = df.at(row, "field1"); - const field3 = df.at(row, "field3"); - return field1 >= 0 && field1 < 5 && !field3; - }; - const matchingIndices = _() - .range(universe.nObs) - .filter(idx => matchFilter(universe.obsAnnotations, idx)) - .value(); - - expect(world).toMatchObject( - expect.objectContaining({ - nObs: matchingIndices.length, - nVar: universe.nVar, - schema: universe.schema, - clipQuantiles: { min: 0, max: 1 }, - obsAnnotations: expect.any(Dataframe.Dataframe), - varAnnotations: expect.any(Dataframe.Dataframe), - obsLayout: expect.any(Dataframe.Dataframe), - varData: expect.any(Dataframe.Dataframe), - unclipped: { - obsAnnotations: expect.any(Dataframe.Dataframe), - varData: expect.any(Dataframe.Dataframe) - } - }) - ); - - expect(world.obsAnnotations.rowIndex.keys()).toEqual( - new Int32Array(matchingIndices) - ); - expect(world.obsAnnotations.colIndex.keys()).toEqual( - universe.obsAnnotations.colIndex.keys() - ); - expect(world.obsLayout.rowIndex.keys()).toEqual( - new Int32Array(matchingIndices) - ); - expect(world.obsLayout.colIndex.keys()).toEqual( - world.schema.layout.obs[0].dims - ); - }); -}); - -describe("createObsDimensionMap", () => { - test("when universe eq world", () => { - /* - check for: - - creates a dimension for all obsAnnotations, PLUS X/Y layout - - check that dimension typing is sane - */ - - const { crossfilter } = defaultBigBang(); - const annotationNames = _.map( - REST.schema.schema.annotations.obs.columns, - c => c.name - ); - const obsIndexColName = REST.schema.schema.annotations.obs.index; - const schemaByObsName = _.keyBy( - REST.schema.schema.annotations.obs.columns, - "name" - ); - expect(crossfilter).toBeDefined(); - annotationNames.forEach(name => { - const dim = crossfilter.dimensions[obsAnnoDimensionName(name)]; - if (name === obsIndexColName) { - expect(dim).toBeUndefined(); - } else { - const { type } = schemaByObsName[name]; - if (type === "string" || type === "boolean" || type === "categorical") { - expect(dim.dim).toBeInstanceOf(DimTypes.enum); - } else { - expect(dim.dim).toBeInstanceOf(DimTypes.scalar); - } - } - }); - expect( - crossfilter.dimensions[layoutDimensionName("XY")].dim - ).toBeInstanceOf(DimTypes.spatial); - }); -}); - -describe("worldEqUniverse", () => { - const { universe, world } = defaultBigBang(); - const result = World.worldEqUniverse(world, universe); - expect(result).toBe(true); -}); diff --git a/client/__tests__/util/stateManager/worldUtil.test.js b/client/__tests__/util/stateManager/worldUtil.test.js deleted file mode 100644 index 59ddc974..00000000 --- a/client/__tests__/util/stateManager/worldUtil.test.js +++ /dev/null @@ -1,62 +0,0 @@ -import { - countCategoryValues2D, - clearCaches -} from "../../../src/util/stateManager/worldUtil"; -import * as Dataframe from "../../../src/util/dataframe"; - -describe("WorldUtil cache management", () => { - test("empty", () => { - const count = countCategoryValues2D( - "a", - "b", - new Dataframe.Dataframe([0, 0], []) - ); - expect(count).toMatchObject(new Map()); - expect(count.size).toBe(0); - }); - - test("simple couts", () => { - const df = new Dataframe.Dataframe( - [3, 2], - [[0, 0, 1], [false, true, false]], - null, - new Dataframe.KeyIndex(["a", "b"]) - ); - const count = countCategoryValues2D("a", "b", df); - expect(count).toMatchObject( - new Map([ - [0, new Map([[true, 1], [false, 1]])], - [1, new Map([[false, 1]])] - ]) - ); - }); - - test("memo cache clear", () => { - clearCaches(); - const df1 = new Dataframe.Dataframe([0, 0], []); - const df2 = new Dataframe.Dataframe( - [3, 2], - [[0, 0, 1], [false, true, false]], - null, - new Dataframe.KeyIndex(["a", "b"]) - ); - - const count1 = countCategoryValues2D("a", "b", df1); - const count2 = countCategoryValues2D("a", "b", df1); - const count3 = countCategoryValues2D("a", "b", df1.clone()); - const count4 = countCategoryValues2D("a", "b", df2); - - clearCaches(); - const count10 = countCategoryValues2D("a", "b", df1); - const count11 = countCategoryValues2D("a", "b", df2); - - expect(count1).toEqual(count2); - expect(count1).toEqual(count3); - expect(count1).toEqual(count10); - expect(count1).not.toBe(count3); - expect(count1).not.toBe(count10); - - expect(count4).toEqual(count11); - expect(count4).not.toBe(count11); - }); -}); diff --git a/client/__tests__/util/typedCrossfilter/bitArray.test.js b/client/__tests__/util/typedCrossfilter/bitArray.test.js deleted file mode 100644 index e4b3e305..00000000 --- a/client/__tests__/util/typedCrossfilter/bitArray.test.js +++ /dev/null @@ -1,185 +0,0 @@ -// jshint esversion: 6 - -import BitArray from "../../../src/util/typedCrossfilter/bitArray"; - -const defaultTestLength = 8; - -describe("default select state", () => { - test("newly created Bitarray should be deselected", () => { - const ba = new BitArray(defaultTestLength); - expect(ba).toBeDefined(); - - for (let i = 0; i < defaultTestLength; i += 1) { - expect(ba.isSelected(i)).toEqual(false); - } - - const dim = ba.allocDimension(); - expect(dim).toBeDefined(); - for (let i = 0; i < defaultTestLength; i += 1) { - expect(ba.isSelected(i)).toEqual(false); - } - - ba.freeDimension(dim); - for (let i = 0; i < defaultTestLength; i += 1) { - expect(ba.isSelected(i)).toEqual(false); - } - }); -}); - -describe("select and deselect", () => { - test("selectAll and deselectAll", () => { - const ba = new BitArray(defaultTestLength); - expect(ba).toBeDefined(); - const dim1 = ba.allocDimension(); - expect(dim1).toBeDefined(); - ba.selectAll(dim1); - - for (let i = 0; i < defaultTestLength; i += 1) { - expect(ba.isSelected(i)).toEqual(true); - } - - const dim2 = ba.allocDimension(); - expect(dim2).toBeDefined(); - for (let i = 0; i < defaultTestLength; i += 1) { - expect(ba.isSelected(i)).toEqual(false); - } - - ba.selectAll(dim2); - for (let i = 0; i < defaultTestLength; i += 1) { - expect(ba.isSelected(i)).toEqual(true); - } - - ba.deselectAll(dim1); - for (let i = 0; i < defaultTestLength; i += 1) { - expect(ba.isSelected(i)).toEqual(false); - } - - ba.deselectAll(dim2); - for (let i = 0; i < defaultTestLength; i += 1) { - expect(ba.isSelected(i)).toEqual(false); - } - - ba.selectAll(dim1); - ba.selectAll(dim2); - for (let i = 0; i < defaultTestLength; i += 1) { - expect(ba.isSelected(i)).toEqual(true); - } - - ba.freeDimension(dim1); - for (let i = 0; i < defaultTestLength; i += 1) { - expect(ba.isSelected(i)).toEqual(true); - } - - ba.freeDimension(dim2); - for (let i = 0; i < defaultTestLength; i += 1) { - expect(ba.isSelected(i)).toEqual(false); - } - }); - - test("selectOne and deselectOne", () => { - const ba = new BitArray(defaultTestLength); - expect(ba).toBeDefined(); - const dim = ba.allocDimension(); - expect(dim).toBeDefined(); - - ba.selectOne(dim, 0); - expect(ba.isSelected(0)).toEqual(true); - for (let i = 1; i < defaultTestLength; i += 1) { - expect(ba.isSelected(i)).toEqual(false); - } - - ba.deselectOne(dim, 0); - for (let i = 0; i < defaultTestLength; i += 1) { - expect(ba.isSelected(i)).toEqual(false); - } - - ba.selectOne(dim, 1); - expect(ba.isSelected(1)).toEqual(true); - expect(ba.isSelected(0)).toEqual(false); - for (let i = 2; i < defaultTestLength; i += 1) { - expect(ba.isSelected(i)).toEqual(false); - } - - ba.selectAll(dim); - ba.deselectOne(dim, defaultTestLength - 1); - expect(ba.isSelected(defaultTestLength - 1)).toEqual(false); - for (let i = 0; i < defaultTestLength - 1; i += 1) { - expect(ba.isSelected(i)).toEqual(true); - } - }); -}); - -describe("selectionCount", () => { - test("simple", () => { - const ba = new BitArray(defaultTestLength); - expect(ba).toBeDefined(); - const dim1 = ba.allocDimension(); - expect(dim1).toBeDefined(); - const dim2 = ba.allocDimension(); - expect(dim2).toBeDefined(); - - expect(ba.selectionCount()).toEqual(0); - ba.selectAll(dim1); - expect(ba.selectionCount()).toEqual(0); - ba.selectAll(dim2); - expect(ba.selectionCount()).toEqual(defaultTestLength); - - for (let i = 0; i < defaultTestLength; i += 1) { - ba.deselectOne(dim1, i); - expect(ba.selectionCount()).toEqual(defaultTestLength - i - 1); - expect(ba.selectionCount()).toEqual(ba.countAllOnes()); - } - - ba.freeDimension(dim1); - ba.freeDimension(dim2); - }); -}); - -describe("fillBySelection", () => { - test("sets values correctly", () => { - const ba = new BitArray(defaultTestLength); - expect(ba).toBeDefined(); - const dim = ba.allocDimension(); - expect(dim).toBeDefined(); - - const arr = new Int32Array(defaultTestLength); - arr.fill(0); - const truth = new Int32Array(defaultTestLength); - truth.fill(0); - - // initial state should be deselected - ba.fillBySelection(arr, 1, 0); - expect(arr).toEqual(expect.not.arrayContaining([1])); - - // selectAll - ba.selectAll(dim); - ba.fillBySelection(arr, 1, 0); - expect(arr).toEqual(expect.not.arrayContaining([0])); - - // deselectOne - ba.deselectOne(dim, 3); - ba.fillBySelection(arr, 1, 0); - truth.fill(1); - truth[3] = 0; - expect(arr).toEqual(truth); - - // deselectAll - ba.deselectAll(dim); - ba.fillBySelection(arr, 1, 0); - truth.fill(0); - expect(arr).toEqual(truth); - - // selectOne - ba.selectOne(dim, 5); - ba.fillBySelection(arr, 6, 1); - truth.fill(1); - truth[5] = 6; - expect(arr).toEqual(truth); - - // should be deselected after dimension disposal - ba.freeDimension(dim); - ba.fillBySelection(arr, 3, 9); - truth.fill(9); - expect(arr).toEqual(truth); - }); -}); diff --git a/client/__tests__/util/typedCrossfilter/crossfilter.test.js b/client/__tests__/util/typedCrossfilter/crossfilter.test.js deleted file mode 100644 index c94fb3fd..00000000 --- a/client/__tests__/util/typedCrossfilter/crossfilter.test.js +++ /dev/null @@ -1,434 +0,0 @@ -import _ from "lodash"; -import { polygonContains } from "d3"; - -import Crossfilter from "../../../src/util/typedCrossfilter"; - -const someData = [ - { - date: "2011-11-14T16:17:54Z", - quantity: 2, - total: 190, - tip: 100, - type: "tab", - productIDs: ["001"], - coords: [0, 0], - nonFinite: 0.0 - }, - { - date: "2011-11-14T16:20:19Z", - quantity: 2, - total: 190, - tip: 100, - type: "tab", - productIDs: ["001", "005"], - coords: [0.4, 0.4], - nonFinite: Number.NaN - }, - { - date: "2011-11-14T16:28:54Z", - quantity: 1, - total: 300, - tip: 200, - type: "visa", - productIDs: ["004", "005"], - coords: [0.3, 0.1], - nonFinite: Number.POSITIVE_INFINITY - }, - { - date: "2011-11-14T16:30:43Z", - quantity: 2, - total: 90, - tip: 0, - type: "tab", - productIDs: ["001", "002"], - coords: [0.392, 0.1], - nonFinite: Number.NEGATIVE_INFINITY - }, - { - date: "2011-11-14T16:48:46Z", - quantity: 2, - total: 90, - tip: 0, - type: "tab", - productIDs: ["005"], - coords: [0.7, 0.0482], - nonFinite: 1.0 - }, - { - date: "2011-11-14T16:53:41Z", - quantity: 2, - total: 90, - tip: 0, - type: "tab", - productIDs: ["001", "004", "005"], - coords: [0.9999, 1.0], - nonFinite: Number.NaN - }, - { - date: "2011-11-14T16:54:06Z", - quantity: 1, - total: 100, - tip: 0, - type: "cash", - productIDs: ["001", "002", "003", "004", "005"], - coords: [0.384, 0.6938], - nonFinite: 99.0 - }, - { - date: "2011-11-14T16:58:03Z", - quantity: 2, - total: 90, - tip: 0, - type: "tab", - productIDs: ["001"], - coords: [0.4822, 0.482], - nonFinite: Number.NaN - }, - { - date: "2011-11-14T17:07:21Z", - quantity: 2, - total: 90, - tip: 0, - type: "tab", - productIDs: ["004", "005"], - coords: [0.2234, 0], - nonFinite: Number.NaN - }, - { - date: "2011-11-14T17:22:59Z", - quantity: 2, - total: 90, - tip: 0, - type: "tab", - productIDs: ["001", "002", "004", "005"], - coords: [0.382, 0.38485], - nonFinite: -1 - }, - { - date: "2011-11-14T17:25:45Z", - quantity: 2, - total: 200, - tip: 0, - type: "cash", - productIDs: ["002"], - coords: [0.998, 0.8472], - nonFinite: 0.0 - }, - { - date: "2011-11-14T17:29:52Z", - quantity: 1, - total: 200, - tip: 100, - type: "visa", - productIDs: ["004"], - coords: [0.8273, 0.3384], - nonFinite: 0.0 - } -]; - -let payments = null; -beforeEach(() => { - payments = new Crossfilter(someData); -}); - -describe("ImmutableTypedCrossfilter", () => { - test("create crossfilter", () => { - expect(payments).toBeDefined(); - expect(payments.size()).toEqual(someData.length); - expect(payments.all()).toEqual(someData); - - const p = payments - .addDimension("quantity", "scalar", (i, d) => d[i].quantity, Int32Array) - .select("quantity", { mode: "all" }); - expect(p).toBeDefined(); - expect(p.all()).toEqual(someData); - expect(p.size()).toEqual(someData.length); - expect(p.isElementSelected(0)).toBeTruthy(); - expect(p.countSelected()).toEqual(someData.length); - expect(p.allSelected()).toEqual(someData); - }); - - test("immutability", () => { - /* - the following should return a new crossfilter: - - addDimension() - - delDimension() - - select - */ - const p2 = payments.addDimension( - "quantity", - "scalar", - (i, data) => data[i].quantity, - Int32Array - ); - - expect(payments).not.toBe(p2); - const p3 = p2.select("quantity", { mode: "all" }); - expect(p3).not.toBe(p2); - - const p4 = p3.delDimension("quantity"); - expect(p4).not.toBe(p3); - }); - - test("select all and none", () => { - let p = payments - .addDimension("quantity", "scalar", (i, d) => d[i].quantity, Int32Array) - .addDimension("tip", "scalar", (i, d) => d[i].tip, Float32Array) - .addDimension("total", "scalar", (i, d) => d[i].total, Float32Array) - .addDimension("type", "enum", (i, d) => d[i].type); - expect(p).toBeDefined(); - - /* expect all records to be selected - default init state */ - expect(p.allSelected()).toEqual(someData); - expect(p.countSelected()).toEqual(someData.length); - expect(p.allSelectedMask()).toEqual( - new Uint8Array(someData.length).fill(1) - ); - expect(p.fillByIsSelected(new Uint8Array(someData.length), 99, 0)).toEqual( - new Uint8Array(someData.length).fill(99) - ); - for (let i = 0; i < someData.length; i += 1) { - expect(p.isElementSelected(i)).toBeTruthy(); - } - - /* expect a selectAll on one dimension to change nothing */ - p = p.select("tip", { mode: "all" }); - expect(p.allSelected()).toEqual(someData); - - /* ditto */ - p = p.select("quantity", { mode: "all" }); - expect(p.allSelected()).toEqual(someData); - - /* select none on one dimension */ - p = p.select("type", { mode: "none" }); - expect(p.allSelected()).toEqual([]); - expect(p.countSelected()).toEqual(0); - expect(p.allSelectedMask()).toEqual( - new Uint8Array(someData.length).fill(0) - ); - expect(p.fillByIsSelected(new Uint8Array(someData.length), 99, 0)).toEqual( - new Uint8Array(someData.length).fill(0) - ); - for (let i = 0; i < someData.length; i += 1) { - expect(p.isElementSelected(i)).toBeFalsy(); - } - - p = p.select("quantity", { mode: "none" }); - expect(p.allSelected()).toEqual([]); - - // invert the first none; should have no effect because type is - // still not filtered. - p = p.select("quantity", { mode: "all" }); - expect(p.allSelected()).toEqual([]); - - /* select all of type; should select all records */ - p = p.select("type", { mode: "all" }); - expect(p.allSelected()).toEqual(someData); - }); - - describe("scalar dimension", () => { - let p; - beforeEach(() => { - p = payments - .addDimension("quantity", "scalar", (i, d) => d[i].quantity, Int32Array) - .addDimension("tip", "scalar", (i, d) => d[i].tip, Float32Array) - .select("tip", { mode: "all" }); - }); - - /* - select modes: all, none, exact, range - */ - test("all", () => { - expect(p.select("quantity", { mode: "all" }).countSelected()).toEqual( - someData.length - ); - }); - test("none", () => { - expect(p.select("quantity", { mode: "none" }).countSelected()).toEqual(0); - }); - test.each([[[]], [[2]], [[2, 1]], [[9, 82]], [[0, 1]]])("exact: %p", v => - expect( - p.select("quantity", { mode: "exact", values: v }).countSelected() - ).toEqual(_.filter(someData, d => v.includes(d.quantity)).length) - ); - test.each([[0, 1], [1, 2], [0, 99], [99, 100000]])("range %p", (lo, hi) => - expect( - p.select("quantity", { mode: "range", lo, hi }).countSelected() - ).toEqual( - _.filter(someData, d => d.quantity >= lo && d.quantity < hi).length - ) - ); - test("bad mode", () => { - expect(() => p.select("type", { mode: "bad mode" })).toThrow(Error); - }); - }); - - describe("enum dimension", () => { - let p; - beforeEach(() => { - p = payments.addDimension("type", "enum", (i, d) => d[i].type); - }); - - test("all", () => { - expect(p.select("type", { mode: "all" }).countSelected()).toEqual( - someData.length - ); - }); - test("none", () => { - expect(p.select("type", { mode: "none" }).countSelected()).toEqual(0); - }); - test.each([ - [[]], - [["tab"]], - [["visa"]], - [["visa", "tab"]], - [["cash", "tab", "visa"]] - ])("exact: %p", v => - expect( - p.select("type", { mode: "exact", values: v }).countSelected() - ).toEqual(_.filter(someData, d => v.includes(d.type)).length) - ); - test("range", () => { - expect(() => p.select("type", { mode: "range", lo: 0, hi: 9 })).toThrow( - Error - ); - }); - test("bad mode", () => { - expect(() => p.select("type", { mode: "bad mode" })).toThrow(Error); - }); - }); - - describe("spatial dimension", () => { - let p; - beforeEach(() => { - const X = someData.map(r => r.coords[0]); - const Y = someData.map(r => r.coords[1]); - p = payments.addDimension("coords", "spatial", X, Y); - }); - - test("all", () => { - expect(p.select("coords", { mode: "all" }).countSelected()).toEqual( - someData.length - ); - }); - test("none", () => { - expect(p.select("coords", { mode: "none" }).countSelected()).toEqual(0); - }); - test.each([[0, 0, 1, 1], [0, 0, 0.5, 0.5], [0.5, 0.5, 1, 1]])( - "within-rect %d %d %d %d", - (minX, minY, maxX, maxY) => { - expect( - p - .select("coords", { mode: "within-rect", minX, minY, maxX, maxY }) - .allSelected() - ).toEqual( - _.filter(someData, d => { - const [x, y] = d.coords; - return minX <= x && x < maxX && minY <= y && y < maxY; - }) - ); - } - ); - - test.each([ - [[[0, 0], [0, 1], [1, 1], [1, 0]]], - [[[0, 0], [0, 0.5], [0.5, 0.5], [0.5, 0]]] - ])("within-polygon %p", polygon => { - expect( - p.select("coords", { mode: "within-polygon", polygon }).allSelected() - ).toEqual(_.filter(someData, d => polygonContains(polygon, d.coords))); - }); - }); - - describe("non-finite scalars", () => { - let p; - beforeEach(() => { - p = payments - .addDimension("quantity", "scalar", (i, d) => d[i].quantity, Int32Array) - .addDimension( - "nonFinite", - "scalar", - (i, d) => d[i].nonFinite, - Float32Array - ) - .select("quantity", { mode: "all" }); - }); - - test("all or none", () => { - expect(p.select("nonFinite", { mode: "all" }).countSelected()).toEqual( - someData.length - ); - expect(p.select("nonFinite", { mode: "none" }).countSelected()).toEqual( - 0 - ); - }); - - test("exact", () => { - expect( - p.select("nonFinite", { mode: "exact", values: [0] }).countSelected() - ).toEqual(3); - expect( - p.select("nonFinite", { mode: "exact", values: [1] }).countSelected() - ).toEqual(1); - expect( - p - .select("nonFinite", { - mode: "exact", - values: [Number.POSITIVE_INFINITY] - }) - .countSelected() - ).toEqual(1); - expect( - p - .select("nonFinite", { - mode: "exact", - values: [Number.NEGATIVE_INFINITY] - }) - .countSelected() - ).toEqual(1); - expect( - p - .select("nonFinite", { mode: "exact", values: [Number.NaN] }) - .countSelected() - ).toEqual(4); - expect( - p - .select("nonFinite", { - mode: "exact", - values: [Number.POSITIVE_INFINITY, 0, 1, 99] - }) - .countSelected() - ).toEqual(6); - }); - - test("range", () => { - expect( - p - .select("nonFinite", { - mode: "range", - lo: 0, - hi: Number.POSITIVE_INFINITY - }) - .countSelected() - ).toEqual(5); - expect( - p - .select("nonFinite", { - mode: "range", - lo: 0, - hi: Number.NaN - }) - .countSelected() - ).toEqual(6); - expect( - p - .select("nonFinite", { - mode: "range", - lo: Number.NEGATIVE_INFINITY, - hi: Number.POSITIVE_INFINITY - }) - .countSelected() - ).toEqual(7); - }); - }); -}); diff --git a/client/__tests__/util/typedCrossfilter/positiveInterval.test.js b/client/__tests__/util/typedCrossfilter/positiveInterval.test.js deleted file mode 100644 index 399ad9ec..00000000 --- a/client/__tests__/util/typedCrossfilter/positiveInterval.test.js +++ /dev/null @@ -1,141 +0,0 @@ -// jshint esversion: 6 - -// const PositiveIntervals = require("../../src/util/typedCrossfilter/positiveIntervals"); -import PositiveIntervals from "../../../src/util/typedCrossfilter/positiveIntervals"; - -describe("canonicalize", () => { - test("empty", () => { - expect(PositiveIntervals.canonicalize([])).toEqual([]); - }); - - test("simple, already correct", () => { - expect(PositiveIntervals.canonicalize([[0, 1]])).toEqual([[0, 1]]); - expect(PositiveIntervals.canonicalize([[0, 1], [2, 3]])).toEqual([ - [0, 1], - [2, 3] - ]); - }); - - test("non-canonical, need to be canonicalized", () => { - expect(PositiveIntervals.canonicalize([[0, 1], [1, 2]])).toEqual([[0, 2]]); - expect(PositiveIntervals.canonicalize([[1, 2], [2, 3]])).toEqual([[1, 3]]); - }); -}); - -describe("union", () => { - test("empty range", () => { - expect(PositiveIntervals.union([], [])).toEqual([]); - expect(PositiveIntervals.union([], [[1, 2]])).toEqual([[1, 2]]); - expect(PositiveIntervals.union([], [[1, 2], [3, 4]])).toEqual([ - [1, 2], - [3, 4] - ]); - expect(PositiveIntervals.union([[3, 4]], [])).toEqual([[3, 4]]); - expect(PositiveIntervals.union([[1, 2], [3, 4]], [])).toEqual([ - [1, 2], - [3, 4] - ]); - expect(PositiveIntervals.union([[3, 3]], [])).toEqual([[3, 3]]); - expect(PositiveIntervals.union([], [[3, 3]])).toEqual([[3, 3]]); - }); - - test("simple ranges", () => { - expect(PositiveIntervals.union([[1, 2]], [[2, 3]])).toEqual([[1, 3]]); - expect(PositiveIntervals.union([[2, 3]], [[1, 2]])).toEqual([[1, 3]]); - expect(PositiveIntervals.union([[1, 2]], [[3, 4]])).toEqual([ - [1, 2], - [3, 4] - ]); - expect( - PositiveIntervals.union([[1, 2], [3, 4]], [[6, 7], [19, 40]]) - ).toEqual([[1, 2], [3, 4], [6, 7], [19, 40]]); - expect(PositiveIntervals.union([[1, 4]], [[1, 1], [3, 4]])).toEqual([ - [1, 4] - ]); - expect(PositiveIntervals.union([[3, 3]], [[4, 4]])).toEqual([ - [3, 3], - [4, 4] - ]); - }); -}); - -describe("intersection", () => { - test("empty range", () => { - expect(PositiveIntervals.intersection([], [])).toEqual([]); - expect(PositiveIntervals.intersection([], [[1, 2]])).toEqual([]); - expect(PositiveIntervals.intersection([[1, 2]], [])).toEqual([]); - }); - - test("simple", () => { - expect(PositiveIntervals.intersection([[1, 2]], [[2, 3]])).toEqual([]); - expect(PositiveIntervals.intersection([[2, 3]], [[1, 2]])).toEqual([]); - expect(PositiveIntervals.intersection([[1, 10]], [[1, 10]])).toEqual([ - [1, 10] - ]); - expect(PositiveIntervals.intersection([[1, 10]], [[2, 8]])).toEqual([ - [2, 8] - ]); - expect(PositiveIntervals.intersection([[2, 8]], [[1, 10]])).toEqual([ - [2, 8] - ]); - expect(PositiveIntervals.intersection([[1, 10]], [[2, 12]])).toEqual([ - [2, 10] - ]); - expect(PositiveIntervals.intersection([[2, 12]], [[1, 10]])).toEqual([ - [2, 10] - ]); - expect(PositiveIntervals.intersection([[1, 10]], [[1, 8]])).toEqual([ - [1, 8] - ]); - expect(PositiveIntervals.intersection([[1, 8]], [[1, 10]])).toEqual([ - [1, 8] - ]); - expect(PositiveIntervals.intersection([[1, 10]], [[1, 2], [6, 9]])).toEqual( - [[1, 2], [6, 9]] - ); - expect(PositiveIntervals.intersection([[0, 2638]], [[1363, 2638]])).toEqual( - [[1363, 2638]] - ); - expect(PositiveIntervals.intersection([[1, 2]], [[1, 2]])).toEqual([ - [1, 2] - ]); - }); -}); - -describe("difference", () => { - test("empty", () => { - expect(PositiveIntervals.difference([], [])).toEqual([]); - expect(PositiveIntervals.difference([], [[1, 10]])).toEqual([]); - expect(PositiveIntervals.difference([[1, 10]], [])).toEqual([[1, 10]]); - }); - - test("simple", () => { - expect(PositiveIntervals.difference([[1, 2], [3, 4]], [])).toEqual([ - [1, 2], - [3, 4] - ]); - expect(PositiveIntervals.difference([[1, 2], [3, 10]], [[5, 10]])).toEqual([ - [1, 2], - [3, 5] - ]); - expect(PositiveIntervals.difference([[1, 2], [3, 10]], [[0, 5]])).toEqual([ - [5, 10] - ]); - expect( - PositiveIntervals.difference([[0, 2638]], [[0, 1363], [2055, 2638]]) - ).toEqual([[1363, 2055]]); - expect( - PositiveIntervals.difference([[0, 1363], [2055, 2638]], [[0, 2638]]) - ).toEqual([]); - expect(PositiveIntervals.difference([[0, 10]], [[0, 1]])).toEqual([ - [1, 10] - ]); - expect(PositiveIntervals.difference([[0, 10]], [[1, 2]])).toEqual([ - [0, 1], - [2, 10] - ]); - expect(PositiveIntervals.difference([[0, 10]], [[9, 10]])).toEqual([ - [0, 9] - ]); - }); -}); diff --git a/client/__tests__/util/typedCrossfilter/sort.test.js b/client/__tests__/util/typedCrossfilter/sort.test.js deleted file mode 100644 index a1d1125d..00000000 --- a/client/__tests__/util/typedCrossfilter/sort.test.js +++ /dev/null @@ -1,255 +0,0 @@ -import { - sortArray, - sortIndex, - lowerBound, - upperBound, - lowerBoundIndirect, - upperBoundIndirect -} from "../../../src/util/typedCrossfilter/sort"; - -/* -Sort tests should keep in mind that there are separate code -paths for: - - small vs. large arrays (insertionsort only) - - float-only typed arrays vs. other array types (non-finite handling) - - indexed vs. direct sort -*/ - -const pInf = Number.POSITIVE_INFINITY; -const nInf = Number.NEGATIVE_INFINITY; - -function fillRange(arr, start = 0) { - const larr = arr; - for (let i = 0, len = larr.length; i < len; i += 1) { - larr[i] = i + start; - } - return larr; -} - -function fillRand(arr) { - for (let i = 0, len = arr.length; i < len; i += 1) { - arr[i] = Math.random(); - } - return arr; -} - -describe("sortArray", () => { - describe("JS vals", () => { - [ - [true, false], - ["a", "b", "0", "1"], - [0, "a", true, null, undefined, 3.1415], - fillRand(new Array(1000)), - ["a", NaN, null, pInf] - ].map((val, idx) => - test(`JS vals ${idx}`, () => { - expect(sortArray(val)).toMatchObject(val.sort()); - }) - ); - }); - - describe("finite numbers", () => { - [Array, Float32Array, Uint32Array, Int32Array, Float64Array].map(Type => - test(Type.name, () => { - expect(sortArray(Type.from([6, 5, 4, 3, 2, 1, 0]))).toMatchObject( - Type.from([0, 1, 2, 3, 4, 5, 6]) - ); - - expect(sortArray(Type.from([6, 5, 4, 3, 2, 1]))).toMatchObject( - Type.from([1, 2, 3, 4, 5, 6]) - ); - - const source = fillRand(new Type(1000)); - expect(sortArray(Type.from(source))).toMatchObject( - Type.from(source).sort() - ); - }) - ); - }); - - describe("non-finite numbers", () => { - test("inifinity", () => { - expect(sortArray(new Float32Array([pInf, nInf, 0, 1, 2]))).toMatchObject( - new Float32Array([nInf, 0, 1, 2, pInf]) - ); - expect( - sortArray(new Float32Array([pInf, nInf, pInf, nInf])) - ).toMatchObject(new Float32Array([nInf, nInf, pInf, pInf])); - expect( - sortArray(new Float32Array([pInf, nInf, pInf, nInf, pInf])) - ).toMatchObject(new Float32Array([nInf, nInf, pInf, pInf, pInf])); - expect( - sortArray( - new Float32Array(100).fill(Infinity, 0, 50).fill(-Infinity, 50, 100) - ) - ).toMatchObject( - new Float32Array(100).fill(-Infinity, 0, 50).fill(Infinity, 50, 100) - ); - }); - test("NaN", () => { - expect(sortArray(new Float64Array([NaN, 2, 1, 0]))).toMatchObject( - new Float64Array([0, 1, 2, NaN]) - ); - expect(sortArray(new Float32Array([NaN, 2, 1, 0]))).toMatchObject( - new Float32Array([0, 1, 2, NaN]) - ); - expect(sortArray(new Float32Array([NaN, 2, NaN, 1, 0]))).toMatchObject( - new Float32Array([0, 1, 2, NaN, NaN]) - ); - expect(sortArray(new Float32Array([NaN, 2, 1, NaN, 0]))).toMatchObject( - new Float32Array([0, 1, 2, NaN, NaN]) - ); - expect( - sortArray(fillRange(new Float32Array(100)).fill(NaN, 0, 10)) - ).toMatchObject(fillRange(new Float32Array(100), 10).fill(NaN, 90, 100)); - }); - - test("mixed numbers", () => { - expect( - sortArray(new Float32Array([NaN, pInf, nInf, NaN, NaN])) - ).toMatchObject(new Float32Array([nInf, pInf, NaN, NaN, NaN])); - expect( - sortArray(new Float32Array([NaN, pInf, nInf, NaN, 1, NaN, 2])) - ).toMatchObject(new Float32Array([nInf, 1, 2, pInf, NaN, NaN, NaN])); - expect( - sortArray(new Float32Array([NaN, pInf, nInf, 0, 1, NaN, 2])) - ).toMatchObject(new Float32Array([nInf, 0, 1, 2, pInf, NaN, NaN])); - expect( - sortArray( - fillRange(new Float32Array(100)) - .fill(NaN, 0, 10) - .fill(Infinity, 10, 20) - ) - ).toMatchObject( - fillRange(new Float32Array(100), 20) - .fill(Infinity, 80, 90) - .fill(NaN, 90, 100) - ); - }); - }); -}); - -describe("sortIndex", () => { - describe("finite numbers", () => { - [Array, Float32Array, Uint32Array, Int32Array, Float64Array].map(Type => - test(Type.name, () => { - const source1 = Type.from([6, 5, 4, 3, 2, 1, 0]); - const index1 = fillRange(new Uint32Array(source1.length)); - expect(sortIndex(index1, source1)).toMatchObject( - index1.sort((a, b) => source1[a] - source1[b]) - ); - - const source2 = Type.from([6, 5, 4, 3, 2, 1]); - const index2 = fillRange(new Uint32Array(source2.length)); - expect(sortIndex(index2, source2)).toMatchObject( - index2.sort((a, b) => source1[a] - source1[b]) - ); - - const source3 = fillRand(new Type(1000)); - const index3 = fillRange(new Uint32Array(source3.length)); - expect(sortIndex(index3, source3)).toMatchObject( - index3.sort((a, b) => source1[a] - source1[b]) - ); - }) - ); - }); - - describe("non-finite numbers", () => { - test("mixed numbers", () => { - const source1 = new Float32Array([NaN, pInf, nInf, NaN, 1, NaN, 2]); - const index1 = fillRange(new Uint32Array(source1.length)); - expect(sortIndex(index1, source1)).toMatchObject( - new Uint32Array([2, 4, 6, 1, 0, 3, 5]) - ); - - const source2 = new Float32Array([NaN, pInf, nInf, 0, 1, NaN, 2]); - const index2 = fillRange(new Uint32Array(source2.length)); - expect(sortIndex(index2, source2)).toMatchObject( - new Uint32Array([2, 3, 4, 6, 1, 0, 5]) - ); - }); - }); -}); - -describe("lowerBound", () => { - test("non-float path", () => { - expect(lowerBound([], 0, 0, 0)).toEqual(0); - - expect(lowerBound([0, 1, 2, 3], -1, 0, 4)).toEqual(0); - expect(lowerBound([0, 1, 2, 3], 0, 0, 4)).toEqual(0); - expect(lowerBound([0, 1, 2, 3], 1, 0, 4)).toEqual(1); - expect(lowerBound([0, 1, 2, 3], 3, 0, 4)).toEqual(3); - expect(lowerBound([0, 1, 2, 3], 4, 0, 4)).toEqual(4); - - expect(lowerBound([0, 1, 2, 3, 4], -1, 0, 5)).toEqual(0); - expect(lowerBound([0, 1, 2, 3, 4], 0, 0, 5)).toEqual(0); - expect(lowerBound([0, 1, 2, 3, 4], 2, 0, 5)).toEqual(2); - expect(lowerBound([0, 1, 2, 3, 4], 4, 0, 5)).toEqual(4); - expect(lowerBound([0, 1, 2, 3, 4], 5, 0, 5)).toEqual(5); - - expect(lowerBound([0, 2, 4, 6, 8], 5, 0, 5)).toEqual(3); - expect(lowerBound([0, 2, 2, 2, 8], 5, 0, 5)).toEqual(4); - - expect(lowerBound([0, 1, 2, 3, 4, 5, 6, 7, 8], 3, 2, 4)).toEqual(3); - expect(lowerBound([0, 1, 2, 3, 4, 5, 6, 7, 8], 99, 2, 4)).toEqual(4); - }); - - test("float path, finites", () => { - expect(lowerBound(new Float32Array([0, 1, 2, 3]), 1, 0, 4)).toEqual(1); - - expect(lowerBound(new Float32Array([]), 0, 0, 0)).toEqual(0); - - expect(lowerBound(new Float32Array([0, 1, 2, 3]), -1, 0, 4)).toEqual(0); - expect(lowerBound(new Float32Array([0, 1, 2, 3]), 0, 0, 4)).toEqual(0); - expect(lowerBound(new Float32Array([0, 1, 2, 3]), 1, 0, 4)).toEqual(1); - expect(lowerBound(new Float32Array([0, 1, 2, 3]), 3, 0, 4)).toEqual(3); - expect(lowerBound(new Float32Array([0, 1, 2, 3]), 4, 0, 4)).toEqual(4); - - expect(lowerBound(new Float32Array([0, 1, 2, 3, 4]), -1, 0, 5)).toEqual(0); - expect(lowerBound(new Float32Array([0, 1, 2, 3, 4]), 0, 0, 5)).toEqual(0); - expect(lowerBound(new Float32Array([0, 1, 2, 3, 4]), 2, 0, 5)).toEqual(2); - expect(lowerBound(new Float32Array([0, 1, 2, 3, 4]), 4, 0, 5)).toEqual(4); - expect(lowerBound(new Float32Array([0, 1, 2, 3, 4]), 5, 0, 5)).toEqual(5); - - expect(lowerBound(new Float32Array([0, 2, 4, 6, 8]), 5, 0, 5)).toEqual(3); - expect(lowerBound(new Float32Array([0, 2, 2, 2, 8]), 5, 0, 5)).toEqual(4); - - expect( - lowerBound(new Float32Array([0, 1, 2, 3, 4, 5, 6, 7, 8]), 3, 2, 4) - ).toEqual(3); - expect( - lowerBound(new Float32Array([0, 1, 2, 3, 4, 5, 6, 7, 8]), 99, 2, 4) - ).toEqual(4); - }); - - test("float path, non-finite", () => { - expect( - lowerBound( - new Float32Array([-Infinity, 0, 1, Infinity, NaN]), - -Infinity, - 0, - 5 - ) - ).toEqual(0); - expect( - lowerBound(new Float32Array([-Infinity, 0, 1, Infinity, NaN]), 0, 0, 5) - ).toEqual(1); - expect( - lowerBound(new Float32Array([-Infinity, 0, 1, Infinity, NaN]), 1, 0, 5) - ).toEqual(2); - expect( - lowerBound(new Float32Array([-Infinity, 0, 1, Infinity, NaN]), 2, 0, 5) - ).toEqual(3); - expect( - lowerBound( - new Float32Array([-Infinity, 0, 1, Infinity, NaN]), - Infinity, - 0, - 5 - ) - ).toEqual(3); - expect( - lowerBound(new Float32Array([-Infinity, 0, 1, Infinity, NaN]), NaN, 0, 5) - ).toEqual(4); - }); -}); diff --git a/client/__tests__/util/typedCrossfilter/util.test.js b/client/__tests__/util/typedCrossfilter/util.test.js deleted file mode 100644 index 33b026d9..00000000 --- a/client/__tests__/util/typedCrossfilter/util.test.js +++ /dev/null @@ -1,112 +0,0 @@ -import { - sliceByIndex, - makeSortIndex -} from "../../../src/util/typedCrossfilter/util"; -import { rangeFill as fillRange } from "../../../src/util/range"; - -describe("fillRange", () => { - test("Array", () => { - expect(fillRange(new Array(6))).toMatchObject([0, 1, 2, 3, 4, 5]); - expect(fillRange(new Array(4), 1)).toMatchObject([1, 2, 3, 4]); - expect(fillRange([])).toMatchObject([]); - }); - - test("Uint32Array", () => { - expect(fillRange(new Uint32Array(6))).toMatchObject( - new Uint32Array([0, 1, 2, 3, 4, 5]) - ); - expect(fillRange(new Array(4), 1)).toMatchObject( - new Uint32Array([1, 2, 3, 4]) - ); - expect(fillRange([])).toMatchObject(new Uint32Array([])); - }); -}); - -describe("sliceByIndex", () => { - test("Array", () => { - expect(sliceByIndex([0, 1, 2, 3, 4], [0, 1, 2])).toMatchObject([0, 1, 2]); - expect(sliceByIndex([0, 1, 2, 3, 4], [2, 1, 0])).toMatchObject([2, 1, 0]); - expect(sliceByIndex([0, 1, 2, 3, 4], [])).toMatchObject([]); - expect(sliceByIndex([], [])).toMatchObject([]); - }); - - test("Uint32Array", () => { - expect( - sliceByIndex([0, 1, 2, 3, 4], new Uint32Array([0, 1, 2])) - ).toMatchObject([0, 1, 2]); - expect( - sliceByIndex([0, 1, 2, 3, 4], new Uint32Array([2, 1, 0])) - ).toMatchObject([2, 1, 0]); - expect(sliceByIndex([0, 1, 2, 3, 4], new Uint32Array([]))).toMatchObject( - [] - ); - expect(sliceByIndex([], new Uint32Array([]))).toMatchObject([]); - - expect( - sliceByIndex(new Uint32Array([0, 1, 2, 3, 4]), new Uint32Array([0, 1, 2])) - ).toMatchObject(new Uint32Array([0, 1, 2])); - expect( - sliceByIndex(new Uint32Array([0, 1, 2, 3, 4]), new Uint32Array([2, 1, 0])) - ).toMatchObject(new Uint32Array([2, 1, 0])); - expect( - sliceByIndex( - new Uint32Array([0, 1, 2, 3, 4]), - new Uint32Array(new Uint32Array([])) - ) - ).toMatchObject(new Uint32Array([])); - expect( - sliceByIndex(new Uint32Array([]), new Uint32Array([])) - ).toMatchObject(new Uint32Array([])); - }); - - test("Float32Array", () => { - expect( - sliceByIndex(new Float32Array([0, 1, 2, 3, 4]), [0, 1, 2]) - ).toMatchObject(new Float32Array([0, 1, 2])); - expect( - sliceByIndex(new Float32Array([0, 1, 2, 3, 4]), [2, 1, 0]) - ).toMatchObject(new Float32Array([2, 1, 0])); - expect(sliceByIndex(new Float32Array([0, 1, 2, 3, 4]), [])).toMatchObject( - new Float32Array([]) - ); - expect(sliceByIndex(new Float32Array([]), [])).toMatchObject( - new Float32Array([]) - ); - }); -}); - -describe("makeSortIndex", () => { - test("Array", () => { - expect(makeSortIndex([3, 2, 1, 0])).toMatchObject( - new Uint32Array([3, 2, 1, 0]) - ); - expect(makeSortIndex([3, 2, 1, 0, 4])).toMatchObject( - new Uint32Array([3, 2, 1, 0, 4]) - ); - expect(makeSortIndex([])).toMatchObject(new Uint32Array([])); - }); - - test("Float32Array", () => { - expect(makeSortIndex(new Float32Array([3, 2, 1, 0]))).toMatchObject( - new Uint32Array([3, 2, 1, 0]) - ); - expect(makeSortIndex(new Float32Array([3, 2, 1, 0, 4]))).toMatchObject( - new Uint32Array([3, 2, 1, 0, 4]) - ); - expect(makeSortIndex(new Float32Array([]))).toMatchObject( - new Uint32Array([]) - ); - }); - - test("Int32Array", () => { - expect(makeSortIndex(new Int32Array([3, 2, 1, 0]))).toMatchObject( - new Uint32Array([3, 2, 1, 0]) - ); - expect(makeSortIndex(new Int32Array([3, 2, 1, 0, 4]))).toMatchObject( - new Uint32Array([3, 2, 1, 0, 4]) - ); - expect(makeSortIndex(new Int32Array([]))).toMatchObject( - new Uint32Array([]) - ); - }); -}); diff --git a/client/configuration/babel/babel.dev.js b/client/configuration/babel/babel.dev.js deleted file mode 100644 index 8309071b..00000000 --- a/client/configuration/babel/babel.dev.js +++ /dev/null @@ -1,16 +0,0 @@ -module.exports = { - babelrc: false, - cacheDirectory: true, - presets: [ - ["modern-browsers", { loose: true, modules: false }], - "@babel/preset-react" - ], - plugins: [ - "@babel/plugin-proposal-function-bind", - ["@babel/plugin-proposal-decorators", { legacy: true }], - ["@babel/plugin-proposal-class-properties", { loose: true }], - "@babel/plugin-proposal-export-namespace-from", - "@babel/plugin-proposal-optional-chaining", - "@babel/plugin-proposal-nullish-coalescing-operator" - ] -}; diff --git a/client/configuration/babel/babel.prod.js b/client/configuration/babel/babel.prod.js deleted file mode 100644 index 45e09551..00000000 --- a/client/configuration/babel/babel.prod.js +++ /dev/null @@ -1,17 +0,0 @@ -module.exports = { - babelrc: false, - presets: [ - ["modern-browsers", { loose: true, modules: false }], - "@babel/preset-react" - ], - plugins: [ - "@babel/plugin-proposal-function-bind", - ["@babel/plugin-proposal-decorators", { legacy: true }], - ["@babel/plugin-proposal-class-properties", { loose: true }], - "@babel/plugin-proposal-export-namespace-from", - "@babel/plugin-transform-react-constant-elements", - "@babel/plugin-transform-runtime", - "@babel/plugin-proposal-optional-chaining", - "@babel/plugin-proposal-nullish-coalescing-operator" - ] -}; diff --git a/client/configuration/eslint/eslint.js b/client/configuration/eslint/eslint.js deleted file mode 100644 index 9d2456fe..00000000 --- a/client/configuration/eslint/eslint.js +++ /dev/null @@ -1,58 +0,0 @@ -module.exports = { - root: true, - parser: "babel-eslint", - extends: ["airbnb", "prettier", "prettier/react"], - env: { browser: true, commonjs: true, es6: true }, - globals: { expect: true }, - parserOptions: { - ecmaVersion: 2017, - sourceType: "module", - ecmaFeatures: { - jsx: true, - generators: true - } - }, - rules: { - "no-magic-numbers": "off", - "no-nested-ternary": "off", - "func-style": "off", - "arrow-parens": "off", - "no-use-before-define": "off", - "react/jsx-filename-extension": "off", - "comma-dangle": "off", - "no-underscore-dangle": "off", - quotes: ["error", "double"], - "implicit-arrow-linebreak": "off", - "operator-linebreak": [ - "error", - "after", - { overrides: { "?": "before", ":": "before" } } - ], - "no-console": "off", - "spaced-comment": ["error", "always", { exceptions: ["*"] }], - "no-param-reassign": "off", - "object-curly-newline": ["error", { consistent: true }], - "react/prop-types": [0], - "space-before-function-paren": "off", - "function-paren-newline": "off", - "prefer-destructuring": ["error", { object: true, array: false }] - }, - overrides: [ - { - files: ["**/*.test.js"], - env: { - jest: true // now **/*.test.js files' env has both es6 *and* jest - }, - // Can't extend in overrides: https://github.com/eslint/eslint/issues/8813 - // "extends": ["plugin:jest/recommended"] - plugins: ["jest"], - rules: { - "jest/no-disabled-tests": "warn", - "jest/no-focused-tests": "error", - "jest/no-identical-title": "error", - "jest/prefer-to-have-length": "warn", - "jest/valid-expect": "error" - } - } - ] -}; diff --git a/client/configuration/webpack/webpack.config.dev.js b/client/configuration/webpack/webpack.config.dev.js deleted file mode 100644 index a128548c..00000000 --- a/client/configuration/webpack/webpack.config.dev.js +++ /dev/null @@ -1,85 +0,0 @@ -// jshint esversion: 6 -const path = require("path"); -const webpack = require("webpack"); -const HtmlWebpackPlugin = require("html-webpack-plugin"); - -const src = path.resolve("src"); -const nodeModules = path.resolve("node_modules"); - -const babelOptions = require("../babel/babel.dev"); - -module.exports = { - mode: "development", - devtool: "eval", - entry: ["./src/index"], - output: { - path: path.resolve("build"), - pathinfo: true, - filename: "static/js/bundle.js", - publicPath: "/" - }, - module: { - rules: [ - { - test: /\.js$/, - include: src, - loader: "babel-loader", - options: babelOptions - }, - { - test: /\.css$/, - include: src, - exclude: [path.resolve(src, "index.css")], - loader: [ - { - loader: "style-loader" - }, - { - loader: "css-loader", - options: { - modules: true, - localIdentName: "[name]__[local]___[hash:base64:5]" - } - } - ] - }, - { - test: /index\.css$/, - include: [path.resolve(src, "index.css")], - loader: [ - { - loader: "style-loader" - }, - { - loader: "css-loader" - } - ] - }, - - { test: /\.json$/, include: [src, nodeModules], loader: "json-loader" }, - { - test: /\.(jpg|png|gif|eot|svg|ttf|woff|woff2)(\?.*)?$/, - include: [src, nodeModules], - loader: "file-loader", - query: { name: "static/media/[name].[ext]" } - }, - { - test: /\.(mp4|webm)(\?.*)?$/, - include: [src, nodeModules], - loader: "url-loader", - query: { limit: 10000, name: "static/media/[name].[ext]" } - } - ] - }, - plugins: [ - new HtmlWebpackPlugin({ - inject: true, - template: path.resolve("index.html"), - favicon: path.resolve("favicon.png") - }), - new webpack.NoEmitOnErrorsPlugin(), - new webpack.DefinePlugin({ - __REACT_DEVTOOLS_GLOBAL_HOOK__: "({ isDisabled: true })" - }) - ] -}; diff --git a/client/configuration/webpack/webpack.config.prod.js b/client/configuration/webpack/webpack.config.prod.js deleted file mode 100644 index 6620962c..00000000 --- a/client/configuration/webpack/webpack.config.prod.js +++ /dev/null @@ -1,119 +0,0 @@ -// jshint esversion: 6 -const path = require("path"); -const HtmlWebpackPlugin = require("html-webpack-plugin"); -const MiniCssExtractPlugin = require("mini-css-extract-plugin"); -const SWPrecacheWebpackPlugin = require("sw-precache-webpack-plugin"); -const HtmlWebpackInlineSourcePlugin = require("html-webpack-inline-source-plugin"); - -const src = path.resolve("src"); -const nodeModules = path.resolve("node_modules"); - -const babelOptions = require("../babel/babel.prod"); - -const publicPath = ""; - -module.exports = { - mode: "production", - bail: true, - cache: false, - devtool: "cheap-source-map", - entry: ["./src/index.js"], - output: { - path: path.resolve("build"), - filename: "static/js/[name].[chunkhash:8].js", - publicPath - }, - module: { - rules: [ - { - test: /\.js$/, - include: src, - loader: "babel-loader", - options: babelOptions - }, - { - test: /\.css$/, - include: src, - exclude: [path.resolve(src, "index.css")], - use: [ - MiniCssExtractPlugin.loader, - { - loader: "css-loader", - options: { - modules: true, - importLoaders: 1, - localIdentName: "[name]__[local]___[hash:base64:5]" - } - } - ] - }, - { - test: /index\.css$/, - include: [path.resolve(src, "index.css")], - use: [ - MiniCssExtractPlugin.loader, - { - loader: "css-loader", - options: { - importLoaders: 1 - } - } - ] - }, - { - test: /\.json$/, - include: [src, nodeModules], - loader: "json-loader", - exclude: /manifest.json$/ - }, - { - test: /\.(jpg|png|gif|eot|svg|ttf|woff|woff2)(\?.*)?$/, - include: [src, nodeModules], - loader: "file-loader", - options: { - name: "[name].[hash:8].[ext]", - outputPath: "static/media/" - } - }, - { - test: /\.(mp4|webm)(\?.*)?$/, - include: [src, nodeModules], - loader: "url-loader", - query: { limit: 10000, name: "static/media/[name].[hash:8].[ext]" } - } - ] - }, - plugins: [ - new HtmlWebpackPlugin({ - inject: "body", - filename: "index.html", - template: path.resolve("index_template.html"), - favicon: path.resolve("favicon.png"), - inlineSource: ".(js|css)$", - minify: { - removeComments: true, - collapseWhitespace: true, - removeRedundantAttributes: true, - useShortDoctype: true, - removeEmptyAttributes: true, - removeStyleLinkTypeAttributes: true, - keepClosingSlash: true, - minifyJS: true, - minifyCSS: true, - minifyURLs: true - } - }), - new HtmlWebpackInlineSourcePlugin(), - new MiniCssExtractPlugin({ - filename: "static/css/[name].[contenthash:8].css" - }), - new SWPrecacheWebpackPlugin({ - cacheId: "cellxgene", - filename: "service-worker.js" - }) - ], - performance: { - maxEntrypointSize: 2000000, - maxAssetSize: 2000000 - } -}; diff --git a/client/favicon.png b/client/favicon.png deleted file mode 100644 index 58f43344..00000000 Binary files a/client/favicon.png and /dev/null differ diff --git a/client/index.html b/client/index.html deleted file mode 100644 index 98341fbe..00000000 --- a/client/index.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - cell×gene - - - - - - -
- - - diff --git a/client/index_template.html b/client/index_template.html deleted file mode 100644 index 85d2800f..00000000 --- a/client/index_template.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - cell×gene - - - - - - - - -
-{% for script in SCRIPTS %} - -{% endfor %} - - diff --git a/client/package-lock.json b/client/package-lock.json deleted file mode 100644 index 27ca60db..00000000 --- a/client/package-lock.json +++ /dev/null @@ -1,16961 +0,0 @@ -{ - "name": "cellxgene", - "version": "0.10.1", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@babel/code-frame": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", - "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - }, - "@babel/core": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.4.4.tgz", - "integrity": "sha512-lQgGX3FPRgbz2SKmhMtYgJvVzGZrmjaF4apZ2bLwofAKiSjxU0drPh4S/VasyYXwaTs+A1gvQ45BN8SQJzHsQQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.4", - "@babel/helpers": "^7.4.4", - "@babel/parser": "^7.4.4", - "@babel/template": "^7.4.4", - "@babel/traverse": "^7.4.4", - "@babel/types": "^7.4.4", - "convert-source-map": "^1.1.0", - "debug": "^4.1.0", - "json5": "^2.1.0", - "lodash": "^4.17.11", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - }, - "dependencies": { - "@babel/generator": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz", - "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.11", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", - "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4" - } - }, - "@babel/parser": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.4.tgz", - "integrity": "sha512-5pCS4mOsL+ANsFZGdvNLybx4wtqAZJ0MJjMHxvzI3bvIsz6sQvzW8XX92EYIkiPtIvcfG3Aj+Ir5VNyjnZhP7w==", - "dev": true - }, - "@babel/template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", - "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4" - } - }, - "@babel/traverse": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.4.tgz", - "integrity": "sha512-Gw6qqkw/e6AGzlyj9KnkabJX7VcubqPtkUQVAwkc0wUMldr3A/hezNB3Rc5eIvId95iSGkGIOe5hh1kMKf951A==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.4", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.11" - } - }, - "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "json5": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz", - "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.1.3.tgz", - "integrity": "sha512-ZoCZGcfIJFJuZBqxcY9OjC1KW2lWK64qrX1o4UYL3yshVhwKFYgzpWZ0vvtGMNJdTlvkw0W+HR1VnYN8q3QPFQ==", - "dev": true, - "requires": { - "@babel/types": "^7.1.3", - "jsesc": "^2.5.1", - "lodash": "^4.17.10", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz", - "integrity": "sha512-3UYcJUj9kvSLbLbUIfQTqzcy5VX7GRZ/CCDrnOaZorFFM01aXp1+GJwuFGV4NDDoAS+mOUyHcO6UD/RfqOks3Q==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.1.0.tgz", - "integrity": "sha512-qNSR4jrmJ8M1VMM9tibvyRAHXQs2PmaksQF7c1CGJNipfe3D8p+wgNwgso/P2A2r2mdgBWAXljNWR0QRZAMW8w==", - "dev": true, - "requires": { - "@babel/helper-explode-assignable-expression": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-builder-react-jsx": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.0.0.tgz", - "integrity": "sha512-ebJ2JM6NAKW0fQEqN8hOLxK84RbRz9OkUhGS/Xd5u56ejMfVbayJ4+LykERZCOUM6faa6Fp3SZNX3fcT16MKHw==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0", - "esutils": "^2.0.0" - } - }, - "@babel/helper-call-delegate": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.4.4.tgz", - "integrity": "sha512-l79boDFJ8S1c5hvQvG+rc+wHw6IuH7YldmRKsYtpbawsxURu/paVy57FZMomGK22/JckepaikOkY0MoAmdyOlQ==", - "dev": true, - "requires": { - "@babel/helper-hoist-variables": "^7.4.4", - "@babel/traverse": "^7.4.4", - "@babel/types": "^7.4.4" - }, - "dependencies": { - "@babel/generator": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz", - "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.11", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", - "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4" - } - }, - "@babel/parser": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.4.tgz", - "integrity": "sha512-5pCS4mOsL+ANsFZGdvNLybx4wtqAZJ0MJjMHxvzI3bvIsz6sQvzW8XX92EYIkiPtIvcfG3Aj+Ir5VNyjnZhP7w==", - "dev": true - }, - "@babel/traverse": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.4.tgz", - "integrity": "sha512-Gw6qqkw/e6AGzlyj9KnkabJX7VcubqPtkUQVAwkc0wUMldr3A/hezNB3Rc5eIvId95iSGkGIOe5hh1kMKf951A==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.4", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.11" - } - }, - "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.4.4.tgz", - "integrity": "sha512-UbBHIa2qeAGgyiNR9RszVF7bUHEdgS4JAUNT8SiqrAN6YJVxlOxeLr5pBzb5kan302dejJ9nla4RyKcR1XT6XA==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-member-expression-to-functions": "^7.0.0", - "@babel/helper-optimise-call-expression": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-replace-supers": "^7.4.4", - "@babel/helper-split-export-declaration": "^7.4.4" - }, - "dependencies": { - "@babel/generator": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz", - "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.11", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" - } - }, - "@babel/helper-replace-supers": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.4.4.tgz", - "integrity": "sha512-04xGEnd+s01nY1l15EuMS1rfKktNF+1CkKmHoErDppjAAZL+IUBZpzT748x262HF7fibaQPhbvWUl5HeSt1EXg==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.0.0", - "@babel/helper-optimise-call-expression": "^7.0.0", - "@babel/traverse": "^7.4.4", - "@babel/types": "^7.4.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", - "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4" - } - }, - "@babel/parser": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.4.tgz", - "integrity": "sha512-5pCS4mOsL+ANsFZGdvNLybx4wtqAZJ0MJjMHxvzI3bvIsz6sQvzW8XX92EYIkiPtIvcfG3Aj+Ir5VNyjnZhP7w==", - "dev": true - }, - "@babel/traverse": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.4.tgz", - "integrity": "sha512-Gw6qqkw/e6AGzlyj9KnkabJX7VcubqPtkUQVAwkc0wUMldr3A/hezNB3Rc5eIvId95iSGkGIOe5hh1kMKf951A==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.4", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.11" - } - }, - "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "@babel/helper-define-map": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.4.4.tgz", - "integrity": "sha512-IX3Ln8gLhZpSuqHJSnTNBWGDE9kdkTEWl21A/K7PQ00tseBwbqCHTvNLHSBd9M0R5rER4h5Rsvj9vw0R5SieBg==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.1.0", - "@babel/types": "^7.4.4", - "lodash": "^4.17.11" - }, - "dependencies": { - "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - } - } - } - }, - "@babel/helper-explode-assignable-expression": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.1.0.tgz", - "integrity": "sha512-NRQpfHrJ1msCHtKjbzs9YcMmJZOg6mQMmGRB+hbamEdG5PNpaSm95275VD92DvJKuyl0s2sFiDmMZ+EnnvufqA==", - "dev": true, - "requires": { - "@babel/traverse": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-function-name": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", - "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.0.0", - "@babel/template": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", - "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.4.4.tgz", - "integrity": "sha512-VYk2/H/BnYbZDDg39hr3t2kKyifAm1W6zHRfhx8jGjIHpQEBv9dry7oQ2f3+J703TLu69nYdxsovl0XYfcnK4w==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4" - }, - "dependencies": { - "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - } - } - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.0.0.tgz", - "integrity": "sha512-avo+lm/QmZlv27Zsi0xEor2fKcqWG56D5ae9dzklpIaY7cQMK5N8VSpaNVPPagiqmy7LrEjK1IWdGMOqPu5csg==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-module-imports": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz", - "integrity": "sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-module-transforms": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.1.0.tgz", - "integrity": "sha512-0JZRd2yhawo79Rcm4w0LwSMILFmFXjugG3yqf+P/UsKsRS1mJCmMwwlHDlMg7Avr9LrvSpp4ZSULO9r8jpCzcw==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/helper-simple-access": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.0.0", - "@babel/template": "^7.1.0", - "@babel/types": "^7.0.0", - "lodash": "^4.17.10" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0.tgz", - "integrity": "sha512-u8nd9NQePYNQV8iPWu/pLLYBqZBa4ZaY1YWRFMuxrid94wKI1QNt67NEZ7GAe5Kc/0LLScbim05xZFWkAdrj9g==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", - "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==", - "dev": true - }, - "@babel/helper-regex": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.0.0.tgz", - "integrity": "sha512-TR0/N0NDCcUIUEbqV6dCO+LptmmSQFQ7q70lfcEB4URsjD0E1HzicrwUH+ap6BAQ2jhCX9Q4UqZy4wilujWlkg==", - "dev": true, - "requires": { - "lodash": "^4.17.10" - } - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.1.0.tgz", - "integrity": "sha512-3fOK0L+Fdlg8S5al8u/hWE6vhufGSn0bN09xm2LXMy//REAF8kDCrYoOBKYmA8m5Nom+sV9LyLCwrFynA8/slg==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.0.0", - "@babel/helper-wrap-function": "^7.1.0", - "@babel/template": "^7.1.0", - "@babel/traverse": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-replace-supers": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.4.4.tgz", - "integrity": "sha512-04xGEnd+s01nY1l15EuMS1rfKktNF+1CkKmHoErDppjAAZL+IUBZpzT748x262HF7fibaQPhbvWUl5HeSt1EXg==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.0.0", - "@babel/helper-optimise-call-expression": "^7.0.0", - "@babel/traverse": "^7.4.4", - "@babel/types": "^7.4.4" - }, - "dependencies": { - "@babel/generator": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz", - "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.11", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", - "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4" - } - }, - "@babel/parser": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.4.tgz", - "integrity": "sha512-5pCS4mOsL+ANsFZGdvNLybx4wtqAZJ0MJjMHxvzI3bvIsz6sQvzW8XX92EYIkiPtIvcfG3Aj+Ir5VNyjnZhP7w==", - "dev": true - }, - "@babel/traverse": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.4.tgz", - "integrity": "sha512-Gw6qqkw/e6AGzlyj9KnkabJX7VcubqPtkUQVAwkc0wUMldr3A/hezNB3Rc5eIvId95iSGkGIOe5hh1kMKf951A==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.4", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.11" - } - }, - "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "@babel/helper-simple-access": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.1.0.tgz", - "integrity": "sha512-Vk+78hNjRbsiu49zAPALxTb+JUQCz1aolpd8osOF16BGnLtseD21nbHgLPGUwrXEurZgiCOUmvs3ExTu4F5x6w==", - "dev": true, - "requires": { - "@babel/template": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz", - "integrity": "sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-wrap-function": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.1.0.tgz", - "integrity": "sha512-R6HU3dete+rwsdAfrOzTlE9Mcpk4RjU3aX3gi9grtmugQY0u79X7eogUvfXA5sI81Mfq1cn6AgxihfN33STjJA==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.1.0", - "@babel/template": "^7.1.0", - "@babel/traverse": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@babel/helpers": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.4.4.tgz", - "integrity": "sha512-igczbR/0SeuPR8RFfC7tGrbdTbFL3QTvH6D+Z6zNxnTe//GyqmtHmDkzrqDmyZ3eSwPqB/LhyKoU5DXsp+Vp2A==", - "dev": true, - "requires": { - "@babel/template": "^7.4.4", - "@babel/traverse": "^7.4.4", - "@babel/types": "^7.4.4" - }, - "dependencies": { - "@babel/generator": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz", - "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.11", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", - "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4" - } - }, - "@babel/parser": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.4.tgz", - "integrity": "sha512-5pCS4mOsL+ANsFZGdvNLybx4wtqAZJ0MJjMHxvzI3bvIsz6sQvzW8XX92EYIkiPtIvcfG3Aj+Ir5VNyjnZhP7w==", - "dev": true - }, - "@babel/template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", - "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4" - } - }, - "@babel/traverse": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.4.tgz", - "integrity": "sha512-Gw6qqkw/e6AGzlyj9KnkabJX7VcubqPtkUQVAwkc0wUMldr3A/hezNB3Rc5eIvId95iSGkGIOe5hh1kMKf951A==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.4", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.11" - } - }, - "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "@babel/highlight": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", - "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.1.3.tgz", - "integrity": "sha512-gqmspPZOMW3MIRb9HlrnbZHXI1/KHTOroBwN1NcLL6pWxzqzEKGvRTq0W/PxS45OtQGbaFikSQpkS5zbnsQm2w==", - "dev": true - }, - "@babel/plugin-proposal-async-generator-functions": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.2.0.tgz", - "integrity": "sha512-+Dfo/SCQqrwx48ptLVGLdE39YtWRuKc/Y9I5Fy0P1DDBB9lsAHpjcEJQt+4IifuSOSTLBKJObJqMvaO1pIE8LQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-remap-async-to-generator": "^7.1.0", - "@babel/plugin-syntax-async-generators": "^7.2.0" - } - }, - "@babel/plugin-proposal-class-properties": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.4.4.tgz", - "integrity": "sha512-WjKTI8g8d5w1Bc9zgwSz2nfrsNQsXcCf9J9cdCvrJV6RF56yztwm4TmJC0MgJ9tvwO9gUA/mcYe89bLdGfiXFg==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.4.4", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-proposal-decorators": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.4.4.tgz", - "integrity": "sha512-z7MpQz3XC/iQJWXH9y+MaWcLPNSMY9RQSthrLzak8R8hCj0fuyNk+Dzi9kfNe/JxxlWQ2g7wkABbgWjW36MTcw==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.4.4", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-decorators": "^7.2.0" - } - }, - "@babel/plugin-proposal-export-namespace-from": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.2.0.tgz", - "integrity": "sha512-DZUxbHYxQ5fUFIkMEnh75ogEdBLPfL+mQUqrO2hNY2LGm+tqFnxE924+mhAcCOh/8za8AaZsWHbq6bBoS3TAzA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-export-namespace-from": "^7.2.0" - } - }, - "@babel/plugin-proposal-function-bind": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-function-bind/-/plugin-proposal-function-bind-7.2.0.tgz", - "integrity": "sha512-qOFJ/eX1Is78sywwTxDcsntLOdb5ZlHVVqUz5xznq8ldAfOVIyZzp1JE2rzHnaksZIhrqMrwIpQL/qcEprnVbw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-function-bind": "^7.2.0" - } - }, - "@babel/plugin-proposal-json-strings": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz", - "integrity": "sha512-MAFV1CA/YVmYwZG0fBQyXhmj0BHCB5egZHCKWIFVv/XCxAeVGIHfos3SwDck4LvCllENIAg7xMKOG5kH0dzyUg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-json-strings": "^7.2.0" - } - }, - "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.4.4.tgz", - "integrity": "sha512-Amph7Epui1Dh/xxUxS2+K22/MUi6+6JVTvy3P58tja3B6yKTSjwwx0/d83rF7551D6PVSSoplQb8GCwqec7HRw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.2.0" - } - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.4.4.tgz", - "integrity": "sha512-dMBG6cSPBbHeEBdFXeQ2QLc5gUpg4Vkaz8octD4aoW/ISO+jBOcsuxYL7bsb5WSu8RLP6boxrBIALEHgoHtO9g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-object-rest-spread": "^7.2.0" - }, - "dependencies": { - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz", - "integrity": "sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - } - } - }, - "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz", - "integrity": "sha512-mgYj3jCcxug6KUcX4OBoOJz3CMrwRfQELPQ5560F70YQUBZB7uac9fqaWamKR1iWUzGiK2t0ygzjTScZnVz75g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.2.0" - }, - "dependencies": { - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz", - "integrity": "sha512-bDe4xKNhb0LI7IvZHiA13kff0KEfaGX/Hv4lMA9+7TEc63hMNvfKo6ZFpXhKuEp+II/q35Gc4NoMeDZyaUbj9w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - } - } - }, - "@babel/plugin-proposal-optional-chaining": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.2.0.tgz", - "integrity": "sha512-ea3Q6edZC/55wEBVZAEz42v528VulyO0eir+7uky/sT4XRcdkWJcFi1aPtitTlwUzGnECWJNExWww1SStt+yWw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-optional-chaining": "^7.2.0" - } - }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.4.4.tgz", - "integrity": "sha512-j1NwnOqMG9mFUOH58JTFsA/+ZYzQLUZ/drqWUqxCYLGeu2JFZL8YrNC9hBxKmWtAuOCHPcRpgv7fhap09Fb4kA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-regex": "^7.4.4", - "regexpu-core": "^4.5.4" - }, - "dependencies": { - "@babel/helper-regex": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.4.4.tgz", - "integrity": "sha512-Y5nuB/kESmR3tKjU8Nkn1wMGEx1tjJX076HBMeL3XLQCu6vA/YRzuTW0bbb+qRnXvQGn+d6Rx953yffl8vEy7Q==", - "dev": true, - "requires": { - "lodash": "^4.17.11" - } - } - } - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.2.0.tgz", - "integrity": "sha512-1ZrIRBv2t0GSlcwVoQ6VgSLpLgiN/FVQUzt9znxo7v2Ov4jJrs8RY8tv0wvDmFN3qIdMKWrmMMW6yZ0G19MfGg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-syntax-decorators": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.2.0.tgz", - "integrity": "sha512-38QdqVoXdHUQfTpZo3rQwqQdWtCn5tMv4uV6r2RMfTqNBuv4ZBhz79SfaQWKTVmxHjeFv/DnXVC/+agHCklYWA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-syntax-export-namespace-from": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.2.0.tgz", - "integrity": "sha512-1zGA3UNch6A+A11nIzBVEaE3DDJbjfB+eLIcf0GGOh/BJr/8NxL3546MGhV/r0RhH4xADFIEso39TKCfEMlsGA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-syntax-function-bind": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-function-bind/-/plugin-syntax-function-bind-7.2.0.tgz", - "integrity": "sha512-/WzU1lLU2l0wDfB42Wkg6tahrmtBbiD8C4H6EGSX0M4GAjzN6JiOpq/Uh8G6GSoR6lPMvhjM0MNiV6znj6y/zg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz", - "integrity": "sha512-5UGYnMSLRE1dqqZwug+1LISpA403HzlSfsg6P9VXU6TBjcSHeNlw4DxDx7LgpF+iKZoOG/+uzqoRHTdcUpiZNg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.0.0.tgz", - "integrity": "sha512-PdmL2AoPsCLWxhIr3kG2+F9v4WH06Q3z+NoGVpQgnUNGcagXHq5sB3OXxkSahKq9TLdNMN/AJzFYSOo8UKDMHg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.2.0.tgz", - "integrity": "sha512-lRCEaKE+LTxDQtgbYajI04ddt6WW0WJq57xqkAZ+s11h4YgfRHhVA/Y2VhfPzzFD4qeLHWg32DMp9HooY4Kqlg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.0.0.tgz", - "integrity": "sha512-5A0n4p6bIiVe5OvQPxBnesezsgFJdHhSs3uFSvaPdMqtsovajLZ+G2vZyvNe10EzJBWWo3AcHGKhAFUxqwp2dw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz", - "integrity": "sha512-bDe4xKNhb0LI7IvZHiA13kff0KEfaGX/Hv4lMA9+7TEc63hMNvfKo6ZFpXhKuEp+II/q35Gc4NoMeDZyaUbj9w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.2.0.tgz", - "integrity": "sha512-HtGCtvp5Uq/jH/WNUPkK6b7rufnCPLLlDAFN7cmACoIjaOOiXxUt3SswU5loHqrhtqTsa/WoLQ1OQ1AGuZqaWA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz", - "integrity": "sha512-ER77Cax1+8/8jCB9fo4Ud161OZzWN5qawi4GusDuRLcDbDG+bIGYY20zb2dfAFdTRGzrfq2xZPvF0R64EHnimg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.4.4.tgz", - "integrity": "sha512-YiqW2Li8TXmzgbXw+STsSqPBPFnGviiaSp6CYOq55X8GQ2SGVLrXB6pNid8HkqkZAzOH6knbai3snhP7v0fNwA==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-remap-async-to-generator": "^7.1.0" - } - }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.2.0.tgz", - "integrity": "sha512-ntQPR6q1/NKuphly49+QiQiTN0O63uOwjdD6dhIjSWBI5xlrbUFh720TIpzBhpnrLfv2tNH/BXvLIab1+BAI0w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.4.4.tgz", - "integrity": "sha512-jkTUyWZcTrwxu5DD4rWz6rDB5Cjdmgz6z7M7RLXOJyCUkFBawssDGcGh8M/0FTSB87avyJI1HsTwUXp9nKA1PA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "lodash": "^4.17.11" - } - }, - "@babel/plugin-transform-classes": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.4.4.tgz", - "integrity": "sha512-/e44eFLImEGIpL9qPxSRat13I5QNRgBLu2hOQJCF7VLy/otSM/sypV1+XaIw5+502RX/+6YaSAPmldk+nhHDPw==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.0.0", - "@babel/helper-define-map": "^7.4.4", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-optimise-call-expression": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-replace-supers": "^7.4.4", - "@babel/helper-split-export-declaration": "^7.4.4", - "globals": "^11.1.0" - }, - "dependencies": { - "@babel/helper-split-export-declaration": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", - "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4" - } - }, - "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - } - } - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.2.0.tgz", - "integrity": "sha512-kP/drqTxY6Xt3NNpKiMomfgkNn4o7+vKxK2DDKcBG9sHj51vHqMBGy8wbDS/J4lMxnqs153/T3+DmCEAkC5cpA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.4.4.tgz", - "integrity": "sha512-/aOx+nW0w8eHiEHm+BTERB2oJn5D127iye/SUQl7NjHy0lf+j7h4MKMMSOwdazGq9OxgiNADncE+SRJkCxjZpQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.4.4.tgz", - "integrity": "sha512-P05YEhRc2h53lZDjRPk/OektxCVevFzZs2Gfjd545Wde3k+yFDbXORgl2e0xpbq8mLcKJ7Idss4fAg0zORN/zg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-regex": "^7.4.4", - "regexpu-core": "^4.5.4" - }, - "dependencies": { - "@babel/helper-regex": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.4.4.tgz", - "integrity": "sha512-Y5nuB/kESmR3tKjU8Nkn1wMGEx1tjJX076HBMeL3XLQCu6vA/YRzuTW0bbb+qRnXvQGn+d6Rx953yffl8vEy7Q==", - "dev": true, - "requires": { - "lodash": "^4.17.11" - } - }, - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - }, - "regenerate-unicode-properties": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz", - "integrity": "sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA==", - "dev": true, - "requires": { - "regenerate": "^1.4.0" - } - }, - "regexpu-core": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.5.4.tgz", - "integrity": "sha512-BtizvGtFQKGPUcTy56o3nk1bGRp4SZOTYrDtGNlqCQufptV5IkkLN6Emw+yunAJjzf+C9FQFtvq7IoA3+oMYHQ==", - "dev": true, - "requires": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.0.2", - "regjsgen": "^0.5.0", - "regjsparser": "^0.6.0", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.1.0" - } - }, - "regjsgen": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.0.tgz", - "integrity": "sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA==", - "dev": true - }, - "regjsparser": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.0.tgz", - "integrity": "sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ==", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz", - "integrity": "sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g==", - "dev": true - } - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.2.0.tgz", - "integrity": "sha512-q+yuxW4DsTjNceUiTzK0L+AfQ0zD9rWaTLiUqHA8p0gxx7lu1EylenfzjeIWNkPy6e/0VG/Wjw9uf9LueQwLOw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.2.0.tgz", - "integrity": "sha512-umh4hR6N7mu4Elq9GG8TOu9M0bakvlsREEC+ialrQN6ABS4oDQ69qJv1VtR3uxlKMCQMCvzk7vr17RHKcjx68A==", - "dev": true, - "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.1.0", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-for-of": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.4.4.tgz", - "integrity": "sha512-9T/5Dlr14Z9TIEXLXkt8T1DU7F24cbhwhMNUziN3hB1AXoZcdzPcTiKGRn/6iOymDqtTKWnr/BtRKN9JwbKtdQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.4.4.tgz", - "integrity": "sha512-iU9pv7U+2jC9ANQkKeNF6DrPy4GBa4NWQtl6dHB4Pb3izX2JOEvDTFarlNsBj/63ZEzNNIAMs3Qw4fNCcSOXJA==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-literals": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.2.0.tgz", - "integrity": "sha512-2ThDhm4lI4oV7fVQ6pNNK+sx+c/GM5/SaML0w/r4ZB7sAneD/piDJtwdKlNckXeyGK7wlwg2E2w33C/Hh+VFCg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.2.0.tgz", - "integrity": "sha512-HiU3zKkSU6scTidmnFJ0bMX8hz5ixC93b4MHMiYebmk2lUVNGOboPsqQvx5LzooihijUoLR/v7Nc1rbBtnc7FA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.2.0.tgz", - "integrity": "sha512-mK2A8ucqz1qhrdqjS9VMIDfIvvT2thrEsIQzbaTdc5QFzhDjQv2CkJJ5f6BXIkgbmaoax3zBr2RyvV/8zeoUZw==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.1.0", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.4.4.tgz", - "integrity": "sha512-4sfBOJt58sEo9a2BQXnZq+Q3ZTSAUXyK3E30o36BOGnJ+tvJ6YSxF0PG6kERvbeISgProodWuI9UVG3/FMY6iw==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.4.4", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-simple-access": "^7.1.0" - }, - "dependencies": { - "@babel/helper-module-transforms": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.4.4.tgz", - "integrity": "sha512-3Z1yp8TVQf+B4ynN7WoHPKS8EkdTbgAEy0nU0rs/1Kw4pDgmvYH3rz3aI11KgxKCba2cn7N+tqzV1mY2HMN96w==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/helper-simple-access": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/template": "^7.4.4", - "@babel/types": "^7.4.4", - "lodash": "^4.17.11" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", - "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4" - } - }, - "@babel/parser": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.4.tgz", - "integrity": "sha512-5pCS4mOsL+ANsFZGdvNLybx4wtqAZJ0MJjMHxvzI3bvIsz6sQvzW8XX92EYIkiPtIvcfG3Aj+Ir5VNyjnZhP7w==", - "dev": true - }, - "@babel/template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", - "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4" - } - }, - "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - } - } - } - }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.4.4.tgz", - "integrity": "sha512-MSiModfILQc3/oqnG7NrP1jHaSPryO6tA2kOMmAQApz5dayPxWiHqmq4sWH2xF5LcQK56LlbKByCd8Aah/OIkQ==", - "dev": true, - "requires": { - "@babel/helper-hoist-variables": "^7.4.4", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.2.0.tgz", - "integrity": "sha512-BV3bw6MyUH1iIsGhXlOK6sXhmSarZjtJ/vMiD9dNmpY8QXFFQTj+6v92pcfy1iqa8DeAfJFwoxcrS/TUZda6sw==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.1.0", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.4.4.tgz", - "integrity": "sha512-Ki+Y9nXBlKfhD+LXaRS7v95TtTGYRAf9Y1rTDiE75zf8YQz4GDaWRXosMfJBXxnk88mGFjWdCRIeqDbon7spYA==", - "dev": true, - "requires": { - "regexp-tree": "^0.1.0" - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.4.4.tgz", - "integrity": "sha512-r1z3T2DNGQwwe2vPGZMBNjioT2scgWzK9BCnDEh+46z8EEwXBq24uRzd65I7pjtugzPSj921aM15RpESgzsSuA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.2.0.tgz", - "integrity": "sha512-VMyhPYZISFZAqAPVkiYb7dUe2AsVi2/wCT5+wZdsNO31FojQJa9ns40hzZ6U9f50Jlq4w6qwzdBB2uwqZ00ebg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-replace-supers": "^7.1.0" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.4.4.tgz", - "integrity": "sha512-oMh5DUO1V63nZcu/ZVLQFqiihBGo4OpxJxR1otF50GMeCLiRx5nUdtokd+u9SuVJrvvuIh9OosRFPP4pIPnwmw==", - "dev": true, - "requires": { - "@babel/helper-call-delegate": "^7.4.4", - "@babel/helper-get-function-arity": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-property-literals": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.2.0.tgz", - "integrity": "sha512-9q7Dbk4RhgcLp8ebduOpCbtjh7C0itoLYHXd9ueASKAG/is5PQtMR5VJGka9NKqGhYEGn5ITahd4h9QeBMylWQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-react-constant-elements": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.2.0.tgz", - "integrity": "sha512-YYQFg6giRFMsZPKUM9v+VcHOdfSQdz9jHCx3akAi3UYgyjndmdYGSXylQ/V+HswQt4fL8IklchD9HTsaOCrWQQ==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-react-display-name": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.0.0.tgz", - "integrity": "sha512-BX8xKuQTO0HzINxT6j/GiCwoJB0AOMs0HmLbEnAvcte8U8rSkNa/eSCAY+l1OA4JnCVq2jw2p6U8QQryy2fTPg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-react-jsx": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.0.0.tgz", - "integrity": "sha512-0TMP21hXsSUjIQJmu/r7RiVxeFrXRcMUigbKu0BLegJK9PkYodHstaszcig7zxXfaBji2LYUdtqIkHs+hgYkJQ==", - "dev": true, - "requires": { - "@babel/helper-builder-react-jsx": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-jsx": "^7.0.0" - } - }, - "@babel/plugin-transform-react-jsx-self": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.0.0.tgz", - "integrity": "sha512-pymy+AK12WO4safW1HmBpwagUQRl9cevNX+82AIAtU1pIdugqcH+nuYP03Ja6B+N4gliAaKWAegIBL/ymALPHA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-jsx": "^7.0.0" - } - }, - "@babel/plugin-transform-react-jsx-source": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.0.0.tgz", - "integrity": "sha512-OSeEpFJEH5dw/TtxTg4nijl4nHBbhqbKL94Xo/Y17WKIf2qJWeIk/QeXACF19lG1vMezkxqruwnTjVizaW7u7w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-jsx": "^7.0.0" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.4.4.tgz", - "integrity": "sha512-Zz3w+pX1SI0KMIiqshFZkwnVGUhDZzpX2vtPzfJBKQQq8WsP/Xy9DNdELWivxcKOCX/Pywge4SiEaPaLtoDT4g==", - "dev": true, - "requires": { - "regenerator-transform": "^0.13.4" - } - }, - "@babel/plugin-transform-reserved-words": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.2.0.tgz", - "integrity": "sha512-fz43fqW8E1tAB3DKF19/vxbpib1fuyCwSPE418ge5ZxILnBhWyhtPgz8eh1RCGGJlwvksHkyxMxh0eenFi+kFw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-runtime": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.4.4.tgz", - "integrity": "sha512-aMVojEjPszvau3NRg+TIH14ynZLvPewH4xhlCW1w6A3rkxTS1m4uwzRclYR9oS+rl/dr+kT+pzbfHuAWP/lc7Q==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0", - "resolve": "^1.8.1", - "semver": "^5.5.1" - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz", - "integrity": "sha512-QP4eUM83ha9zmYtpbnyjTLAGKQritA5XW/iG9cjtuOI8s1RuL/3V6a3DeSHfKutJQ+ayUfeZJPcnCYEQzaPQqg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-spread": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.2.2.tgz", - "integrity": "sha512-KWfky/58vubwtS0hLqEnrWJjsMGaOeSBn90Ezn5Jeg9Z8KKHmELbP1yGylMlm5N6TPKeY9A2+UaSYLdxahg01w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.2.0.tgz", - "integrity": "sha512-KKYCoGaRAf+ckH8gEL3JHUaFVyNHKe3ASNsZ+AlktgHevvxGigoIttrEJb8iKN03Q7Eazlv1s6cx2B2cQ3Jabw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-regex": "^7.0.0" - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.4.4.tgz", - "integrity": "sha512-mQrEC4TWkhLN0z8ygIvEL9ZEToPhG5K7KDW3pzGqOfIGZ28Jb0POUkeWcoz8HnHvhFy6dwAT1j8OzqN8s804+g==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.2.0.tgz", - "integrity": "sha512-2LNhETWYxiYysBtrBTqL8+La0jIoQQnIScUJc74OYvUGRmkskNY4EzLCnjHBzdmb38wqtTaixpo1NctEcvMDZw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.4.4.tgz", - "integrity": "sha512-il+/XdNw01i93+M9J9u4T7/e/Ue/vWfNZE4IRUQjplu2Mqb/AFTDimkw2tdEdSH50wuQXZAbXSql0UphQke+vA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-regex": "^7.4.4", - "regexpu-core": "^4.5.4" - }, - "dependencies": { - "@babel/helper-regex": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.4.4.tgz", - "integrity": "sha512-Y5nuB/kESmR3tKjU8Nkn1wMGEx1tjJX076HBMeL3XLQCu6vA/YRzuTW0bbb+qRnXvQGn+d6Rx953yffl8vEy7Q==", - "dev": true, - "requires": { - "lodash": "^4.17.11" - } - }, - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - }, - "regenerate-unicode-properties": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz", - "integrity": "sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA==", - "dev": true, - "requires": { - "regenerate": "^1.4.0" - } - }, - "regexpu-core": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.5.4.tgz", - "integrity": "sha512-BtizvGtFQKGPUcTy56o3nk1bGRp4SZOTYrDtGNlqCQufptV5IkkLN6Emw+yunAJjzf+C9FQFtvq7IoA3+oMYHQ==", - "dev": true, - "requires": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.0.2", - "regjsgen": "^0.5.0", - "regjsparser": "^0.6.0", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.1.0" - } - }, - "regjsgen": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.0.tgz", - "integrity": "sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA==", - "dev": true - }, - "regjsparser": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.0.tgz", - "integrity": "sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ==", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz", - "integrity": "sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g==", - "dev": true - } - } - }, - "@babel/preset-env": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.4.4.tgz", - "integrity": "sha512-FU1H+ACWqZZqfw1x2G1tgtSSYSfxJLkpaUQL37CenULFARDo+h4xJoVHzRoHbK+85ViLciuI7ME4WTIhFRBBlw==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-async-generator-functions": "^7.2.0", - "@babel/plugin-proposal-json-strings": "^7.2.0", - "@babel/plugin-proposal-object-rest-spread": "^7.4.4", - "@babel/plugin-proposal-optional-catch-binding": "^7.2.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-syntax-async-generators": "^7.2.0", - "@babel/plugin-syntax-json-strings": "^7.2.0", - "@babel/plugin-syntax-object-rest-spread": "^7.2.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.2.0", - "@babel/plugin-transform-arrow-functions": "^7.2.0", - "@babel/plugin-transform-async-to-generator": "^7.4.4", - "@babel/plugin-transform-block-scoped-functions": "^7.2.0", - "@babel/plugin-transform-block-scoping": "^7.4.4", - "@babel/plugin-transform-classes": "^7.4.4", - "@babel/plugin-transform-computed-properties": "^7.2.0", - "@babel/plugin-transform-destructuring": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/plugin-transform-duplicate-keys": "^7.2.0", - "@babel/plugin-transform-exponentiation-operator": "^7.2.0", - "@babel/plugin-transform-for-of": "^7.4.4", - "@babel/plugin-transform-function-name": "^7.4.4", - "@babel/plugin-transform-literals": "^7.2.0", - "@babel/plugin-transform-member-expression-literals": "^7.2.0", - "@babel/plugin-transform-modules-amd": "^7.2.0", - "@babel/plugin-transform-modules-commonjs": "^7.4.4", - "@babel/plugin-transform-modules-systemjs": "^7.4.4", - "@babel/plugin-transform-modules-umd": "^7.2.0", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.4.4", - "@babel/plugin-transform-new-target": "^7.4.4", - "@babel/plugin-transform-object-super": "^7.2.0", - "@babel/plugin-transform-parameters": "^7.4.4", - "@babel/plugin-transform-property-literals": "^7.2.0", - "@babel/plugin-transform-regenerator": "^7.4.4", - "@babel/plugin-transform-reserved-words": "^7.2.0", - "@babel/plugin-transform-shorthand-properties": "^7.2.0", - "@babel/plugin-transform-spread": "^7.2.0", - "@babel/plugin-transform-sticky-regex": "^7.2.0", - "@babel/plugin-transform-template-literals": "^7.4.4", - "@babel/plugin-transform-typeof-symbol": "^7.2.0", - "@babel/plugin-transform-unicode-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "browserslist": "^4.5.2", - "core-js-compat": "^3.0.0", - "invariant": "^2.2.2", - "js-levenshtein": "^1.1.3", - "semver": "^5.5.0" - }, - "dependencies": { - "@babel/helper-module-transforms": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.4.4.tgz", - "integrity": "sha512-3Z1yp8TVQf+B4ynN7WoHPKS8EkdTbgAEy0nU0rs/1Kw4pDgmvYH3rz3aI11KgxKCba2cn7N+tqzV1mY2HMN96w==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/helper-simple-access": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/template": "^7.4.4", - "@babel/types": "^7.4.4", - "lodash": "^4.17.11" - } - }, - "@babel/helper-regex": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.4.4.tgz", - "integrity": "sha512-Y5nuB/kESmR3tKjU8Nkn1wMGEx1tjJX076HBMeL3XLQCu6vA/YRzuTW0bbb+qRnXvQGn+d6Rx953yffl8vEy7Q==", - "dev": true, - "requires": { - "lodash": "^4.17.11" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", - "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4" - } - }, - "@babel/parser": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.4.tgz", - "integrity": "sha512-5pCS4mOsL+ANsFZGdvNLybx4wtqAZJ0MJjMHxvzI3bvIsz6sQvzW8XX92EYIkiPtIvcfG3Aj+Ir5VNyjnZhP7w==", - "dev": true - }, - "@babel/plugin-proposal-async-generator-functions": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.2.0.tgz", - "integrity": "sha512-+Dfo/SCQqrwx48ptLVGLdE39YtWRuKc/Y9I5Fy0P1DDBB9lsAHpjcEJQt+4IifuSOSTLBKJObJqMvaO1pIE8LQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-remap-async-to-generator": "^7.1.0", - "@babel/plugin-syntax-async-generators": "^7.2.0" - } - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.4.4.tgz", - "integrity": "sha512-dMBG6cSPBbHeEBdFXeQ2QLc5gUpg4Vkaz8octD4aoW/ISO+jBOcsuxYL7bsb5WSu8RLP6boxrBIALEHgoHtO9g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-object-rest-spread": "^7.2.0" - } - }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.4.4.tgz", - "integrity": "sha512-j1NwnOqMG9mFUOH58JTFsA/+ZYzQLUZ/drqWUqxCYLGeu2JFZL8YrNC9hBxKmWtAuOCHPcRpgv7fhap09Fb4kA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-regex": "^7.4.4", - "regexpu-core": "^4.5.4" - } - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.2.0.tgz", - "integrity": "sha512-1ZrIRBv2t0GSlcwVoQ6VgSLpLgiN/FVQUzt9znxo7v2Ov4jJrs8RY8tv0wvDmFN3qIdMKWrmMMW6yZ0G19MfGg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz", - "integrity": "sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz", - "integrity": "sha512-bDe4xKNhb0LI7IvZHiA13kff0KEfaGX/Hv4lMA9+7TEc63hMNvfKo6ZFpXhKuEp+II/q35Gc4NoMeDZyaUbj9w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz", - "integrity": "sha512-ER77Cax1+8/8jCB9fo4Ud161OZzWN5qawi4GusDuRLcDbDG+bIGYY20zb2dfAFdTRGzrfq2xZPvF0R64EHnimg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.4.4.tgz", - "integrity": "sha512-iU9pv7U+2jC9ANQkKeNF6DrPy4GBa4NWQtl6dHB4Pb3izX2JOEvDTFarlNsBj/63ZEzNNIAMs3Qw4fNCcSOXJA==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.4.4.tgz", - "integrity": "sha512-4sfBOJt58sEo9a2BQXnZq+Q3ZTSAUXyK3E30o36BOGnJ+tvJ6YSxF0PG6kERvbeISgProodWuI9UVG3/FMY6iw==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.4.4", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-simple-access": "^7.1.0" - } - }, - "@babel/template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", - "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4" - } - }, - "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - } - }, - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - }, - "regenerate-unicode-properties": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz", - "integrity": "sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA==", - "dev": true, - "requires": { - "regenerate": "^1.4.0" - } - }, - "regexpu-core": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.5.4.tgz", - "integrity": "sha512-BtizvGtFQKGPUcTy56o3nk1bGRp4SZOTYrDtGNlqCQufptV5IkkLN6Emw+yunAJjzf+C9FQFtvq7IoA3+oMYHQ==", - "dev": true, - "requires": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.0.2", - "regjsgen": "^0.5.0", - "regjsparser": "^0.6.0", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.1.0" - } - }, - "regjsgen": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.0.tgz", - "integrity": "sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA==", - "dev": true - }, - "regjsparser": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.0.tgz", - "integrity": "sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ==", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz", - "integrity": "sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g==", - "dev": true - } - } - }, - "@babel/preset-react": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.0.0.tgz", - "integrity": "sha512-oayxyPS4Zj+hF6Et11BwuBkmpgT/zMxyuZgFrMeZID6Hdh3dGlk4sHCAhdBCpuCKW2ppBfl2uCCetlrUIJRY3w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-transform-react-display-name": "^7.0.0", - "@babel/plugin-transform-react-jsx": "^7.0.0", - "@babel/plugin-transform-react-jsx-self": "^7.0.0", - "@babel/plugin-transform-react-jsx-source": "^7.0.0" - } - }, - "@babel/register": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.4.4.tgz", - "integrity": "sha512-sn51H88GRa00+ZoMqCVgOphmswG4b7mhf9VOB0LUBAieykq2GnRFerlN+JQkO/ntT7wz4jaHNSRPg9IdMPEUkA==", - "dev": true, - "requires": { - "core-js": "^3.0.0", - "find-cache-dir": "^2.0.0", - "lodash": "^4.17.11", - "mkdirp": "^0.5.1", - "pirates": "^4.0.0", - "source-map-support": "^0.5.9" - }, - "dependencies": { - "core-js": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.0.1.tgz", - "integrity": "sha512-sco40rF+2KlE0ROMvydjkrVMMG1vYilP2ALoRXcYR4obqbYIuV3Bg+51GEDW+HF8n7NRA+iaA4qD0nD9lo9mew==", - "dev": true - }, - "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - } - } - } - }, - "@babel/runtime": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.4.tgz", - "integrity": "sha512-w0+uT71b6Yi7i5SE0co4NioIpSYS6lLiXvCzWzGSKvpK5vdQtCbICHMj+gbAKAOtxiV6HsVh/MBdaF9EQ6faSg==", - "requires": { - "regenerator-runtime": "^0.13.2" - } - }, - "@babel/template": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.1.2.tgz", - "integrity": "sha512-SY1MmplssORfFiLDcOETrW7fCLl+PavlwMh92rrGcikQaRq4iWPVH0MpwPpY3etVMx6RnDjXtr6VZYr/IbP/Ag==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.1.2", - "@babel/types": "^7.1.2" - } - }, - "@babel/traverse": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.1.4.tgz", - "integrity": "sha512-my9mdrAIGdDiSVBuMjpn/oXYpva0/EZwWL3sm3Wcy/AVWO2eXnsoZruOT9jOGNRXU8KbCIu5zsKnXcAJ6PcV6Q==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.1.3", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.0.0", - "@babel/parser": "^7.1.3", - "@babel/types": "^7.1.3", - "debug": "^3.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.10" - } - }, - "@babel/types": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.1.3.tgz", - "integrity": "sha512-RpPOVfK+yatXyn8n4PB1NW6k9qjinrXrRR8ugBN8fD6hCy5RXI6PSbVqpOJBO9oSaY7Nom4ohj35feb0UR9hSA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.10", - "to-fast-properties": "^2.0.0" - } - }, - "@blueprintjs/core": { - "version": "3.15.1", - "resolved": "https://registry.npmjs.org/@blueprintjs/core/-/core-3.15.1.tgz", - "integrity": "sha512-M8ltbqqlMZuZ6SEuqo/3Fr59ZcUfd8Er7ocbm7EACVfRW7dRhOCd/TKkf2kfICNtCDwznwXk0iAePLXZhUGtQg==", - "requires": { - "@blueprintjs/icons": "^3.8.0", - "@types/dom4": "^2.0.1", - "classnames": "^2.2", - "dom4": "^2.0.1", - "normalize.css": "^8.0.0", - "popper.js": "^1.14.1", - "react-popper": "^1.0.0", - "react-transition-group": "^2.2.1", - "resize-observer-polyfill": "^1.5.0", - "tslib": "^1.9.0" - }, - "dependencies": { - "@blueprintjs/icons": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@blueprintjs/icons/-/icons-3.8.0.tgz", - "integrity": "sha512-yHaRQ3vfV9Gf3foZ4ONtxddz+u5ufkHqHj8Ia5VhPbFgG4el+cPdmsGGIIM72rgKS1KQa5Ay+ggjpByUlXvrKg==", - "requires": { - "classnames": "^2.2", - "tslib": "^1.9.0" - } - } - } - }, - "@blueprintjs/icons": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@blueprintjs/icons/-/icons-3.8.0.tgz", - "integrity": "sha512-yHaRQ3vfV9Gf3foZ4ONtxddz+u5ufkHqHj8Ia5VhPbFgG4el+cPdmsGGIIM72rgKS1KQa5Ay+ggjpByUlXvrKg==", - "requires": { - "classnames": "^2.2", - "tslib": "^1.9.0" - } - }, - "@blueprintjs/select": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@blueprintjs/select/-/select-3.8.0.tgz", - "integrity": "sha512-sWWGZx/nARtxKfoFODlEQxA7HDpddGDC4uKPNMd1ZQgKazEtMkw0rFVm550K6Aoj0wp82TqqmcxHrb0VL6Epiw==", - "requires": { - "@blueprintjs/core": "^3.15.0", - "classnames": "^2.2", - "tslib": "^1.9.0" - }, - "dependencies": { - "@blueprintjs/core": { - "version": "3.15.1", - "resolved": "https://registry.npmjs.org/@blueprintjs/core/-/core-3.15.1.tgz", - "integrity": "sha512-M8ltbqqlMZuZ6SEuqo/3Fr59ZcUfd8Er7ocbm7EACVfRW7dRhOCd/TKkf2kfICNtCDwznwXk0iAePLXZhUGtQg==", - "requires": { - "@blueprintjs/icons": "^3.8.0", - "@types/dom4": "^2.0.1", - "classnames": "^2.2", - "dom4": "^2.0.1", - "normalize.css": "^8.0.0", - "popper.js": "^1.14.1", - "react-popper": "^1.0.0", - "react-transition-group": "^2.2.1", - "resize-observer-polyfill": "^1.5.0", - "tslib": "^1.9.0" - } - }, - "@blueprintjs/icons": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@blueprintjs/icons/-/icons-3.8.0.tgz", - "integrity": "sha512-yHaRQ3vfV9Gf3foZ4ONtxddz+u5ufkHqHj8Ia5VhPbFgG4el+cPdmsGGIIM72rgKS1KQa5Ay+ggjpByUlXvrKg==", - "requires": { - "classnames": "^2.2", - "tslib": "^1.9.0" - } - } - } - }, - "@cnakazawa/watch": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.3.tgz", - "integrity": "sha512-r5160ogAvGyHsal38Kux7YYtodEKOj89RGb28ht1jh3SJb08VwRwAKKJL0bGb04Zd/3r9FL3BFIc3bBidYffCA==", - "dev": true, - "requires": { - "exec-sh": "^0.3.2", - "minimist": "^1.2.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "@jest/console": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.7.1.tgz", - "integrity": "sha512-iNhtIy2M8bXlAOULWVTUxmnelTLFneTNEkHCgPmgd+zNwy9zVddJ6oS5rZ9iwoscNdT5mMwUd0C51v/fSlzItg==", - "dev": true, - "requires": { - "@jest/source-map": "^24.3.0", - "chalk": "^2.0.1", - "slash": "^2.0.0" - }, - "dependencies": { - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - } - } - }, - "@jest/core": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-24.8.0.tgz", - "integrity": "sha512-R9rhAJwCBQzaRnrRgAdVfnglUuATXdwTRsYqs6NMdVcAl5euG8LtWDe+fVkN27YfKVBW61IojVsXKaOmSnqd/A==", - "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/reporters": "^24.8.0", - "@jest/test-result": "^24.8.0", - "@jest/transform": "^24.8.0", - "@jest/types": "^24.8.0", - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "graceful-fs": "^4.1.15", - "jest-changed-files": "^24.8.0", - "jest-config": "^24.8.0", - "jest-haste-map": "^24.8.0", - "jest-message-util": "^24.8.0", - "jest-regex-util": "^24.3.0", - "jest-resolve-dependencies": "^24.8.0", - "jest-runner": "^24.8.0", - "jest-runtime": "^24.8.0", - "jest-snapshot": "^24.8.0", - "jest-util": "^24.8.0", - "jest-validate": "^24.8.0", - "jest-watcher": "^24.8.0", - "micromatch": "^3.1.10", - "p-each-series": "^1.0.0", - "pirates": "^4.0.1", - "realpath-native": "^1.1.0", - "rimraf": "^2.5.4", - "strip-ansi": "^5.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "pirates": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", - "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", - "dev": true, - "requires": { - "node-modules-regexp": "^1.0.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "@jest/environment": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.8.0.tgz", - "integrity": "sha512-vlGt2HLg7qM+vtBrSkjDxk9K0YtRBi7HfRFaDxoRtyi+DyVChzhF20duvpdAnKVBV6W5tym8jm0U9EfXbDk1tw==", - "dev": true, - "requires": { - "@jest/fake-timers": "^24.8.0", - "@jest/transform": "^24.8.0", - "@jest/types": "^24.8.0", - "jest-mock": "^24.8.0" - } - }, - "@jest/fake-timers": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.8.0.tgz", - "integrity": "sha512-2M4d5MufVXwi6VzZhJ9f5S/wU4ud2ck0kxPof1Iz3zWx6Y+V2eJrES9jEktB6O3o/oEyk+il/uNu9PvASjWXQw==", - "dev": true, - "requires": { - "@jest/types": "^24.8.0", - "jest-message-util": "^24.8.0", - "jest-mock": "^24.8.0" - } - }, - "@jest/reporters": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-24.8.0.tgz", - "integrity": "sha512-eZ9TyUYpyIIXfYCrw0UHUWUvE35vx5I92HGMgS93Pv7du+GHIzl+/vh8Qj9MCWFK/4TqyttVBPakWMOfZRIfxw==", - "dev": true, - "requires": { - "@jest/environment": "^24.8.0", - "@jest/test-result": "^24.8.0", - "@jest/transform": "^24.8.0", - "@jest/types": "^24.8.0", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "glob": "^7.1.2", - "istanbul-lib-coverage": "^2.0.2", - "istanbul-lib-instrument": "^3.0.1", - "istanbul-lib-report": "^2.0.4", - "istanbul-lib-source-maps": "^3.0.1", - "istanbul-reports": "^2.1.1", - "jest-haste-map": "^24.8.0", - "jest-resolve": "^24.8.0", - "jest-runtime": "^24.8.0", - "jest-util": "^24.8.0", - "jest-worker": "^24.6.0", - "node-notifier": "^5.2.1", - "slash": "^2.0.0", - "source-map": "^0.6.0", - "string-length": "^2.0.0" - }, - "dependencies": { - "@babel/generator": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz", - "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.11", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", - "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4" - } - }, - "@babel/parser": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.4.tgz", - "integrity": "sha512-5pCS4mOsL+ANsFZGdvNLybx4wtqAZJ0MJjMHxvzI3bvIsz6sQvzW8XX92EYIkiPtIvcfG3Aj+Ir5VNyjnZhP7w==", - "dev": true - }, - "@babel/template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", - "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4" - } - }, - "@babel/traverse": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.4.tgz", - "integrity": "sha512-Gw6qqkw/e6AGzlyj9KnkabJX7VcubqPtkUQVAwkc0wUMldr3A/hezNB3Rc5eIvId95iSGkGIOe5hh1kMKf951A==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.4", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.11" - } - }, - "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", - "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", - "dev": true, - "requires": { - "@babel/generator": "^7.4.0", - "@babel/parser": "^7.4.3", - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0", - "istanbul-lib-coverage": "^2.0.5", - "semver": "^6.0.0" - } - }, - "semver": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.0.0.tgz", - "integrity": "sha512-0UewU+9rFapKFnlbirLi3byoOuhrSsli/z/ihNnvM24vgF+8sNBiI1LZPBSH9wJKUwaUbw+s3hToDLCXkrghrQ==", - "dev": true - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "@jest/source-map": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.3.0.tgz", - "integrity": "sha512-zALZt1t2ou8le/crCeeiRYzvdnTzaIlpOWaet45lNSqNJUnXbppUUFR4ZUAlzgDmKee4Q5P/tKXypI1RiHwgag==", - "dev": true, - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.1.15", - "source-map": "^0.6.0" - }, - "dependencies": { - "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "@jest/test-result": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.8.0.tgz", - "integrity": "sha512-+YdLlxwizlfqkFDh7Mc7ONPQAhA4YylU1s529vVM1rsf67vGZH/2GGm5uO8QzPeVyaVMobCQ7FTxl38QrKRlng==", - "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/types": "^24.8.0", - "@types/istanbul-lib-coverage": "^2.0.0" - } - }, - "@jest/test-sequencer": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-24.8.0.tgz", - "integrity": "sha512-OzL/2yHyPdCHXEzhoBuq37CE99nkme15eHkAzXRVqthreWZamEMA0WoetwstsQBCXABhczpK03JNbc4L01vvLg==", - "dev": true, - "requires": { - "@jest/test-result": "^24.8.0", - "jest-haste-map": "^24.8.0", - "jest-runner": "^24.8.0", - "jest-runtime": "^24.8.0" - } - }, - "@jest/transform": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.8.0.tgz", - "integrity": "sha512-xBMfFUP7TortCs0O+Xtez2W7Zu1PLH9bvJgtraN1CDST6LBM/eTOZ9SfwS/lvV8yOfcDpFmwf9bq5cYbXvqsvA==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/types": "^24.8.0", - "babel-plugin-istanbul": "^5.1.0", - "chalk": "^2.0.1", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.1.15", - "jest-haste-map": "^24.8.0", - "jest-regex-util": "^24.3.0", - "jest-util": "^24.8.0", - "micromatch": "^3.1.10", - "realpath-native": "^1.1.0", - "slash": "^2.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "2.4.1" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "write-file-atomic": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.1.tgz", - "integrity": "sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } - } - } - }, - "@jest/types": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.8.0.tgz", - "integrity": "sha512-g17UxVr2YfBtaMUxn9u/4+siG1ptg9IGYAYwvpwn61nBg779RXnjE/m7CxYcIzEt0AbHZZAHSEZNhkE2WxURVg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^12.0.9" - } - }, - "@types/babel__core": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.1.tgz", - "integrity": "sha512-+hjBtgcFPYyCTo0A15+nxrCVJL7aC6Acg87TXd5OW3QhHswdrOLoles+ldL2Uk8q++7yIfl4tURtztccdeeyOw==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@types/babel__generator": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.0.2.tgz", - "integrity": "sha512-NHcOfab3Zw4q5sEE2COkpfXjoE7o+PmqD9DQW4koUT3roNxwziUdXGnRndMat/LJNUtePwn1TlP4do3uoe3KZQ==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.2.tgz", - "integrity": "sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.6.tgz", - "integrity": "sha512-XYVgHF2sQ0YblLRMLNPB3CkFMewzFmlDsH/TneZFHUXDlABQgh88uOxuez7ZcXxayLFrqLwtDH1t+FmlFwNZxw==", - "dev": true, - "requires": { - "@babel/types": "^7.3.0" - }, - "dependencies": { - "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - } - } - } - }, - "@types/dom4": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/dom4/-/dom4-2.0.1.tgz", - "integrity": "sha512-kSkVAvWmMZiCYtvqjqQEwOmvKwcH+V4uiv3qPQ8pAh1Xl39xggGEo8gHUqV4waYGHezdFw0rKBR8Jt0CrQSDZA==" - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz", - "integrity": "sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg==", - "dev": true - }, - "@types/istanbul-lib-report": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz", - "integrity": "sha512-3BUTyMzbZa2DtDI2BkERNC6jJw2Mr2Y0oGI7mRxYNBPxppbtEK1F66u3bKwU2g+wxwWI7PAoRpJnOY1grJqzHg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*" - } - }, - "@types/istanbul-reports": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.1.tgz", - "integrity": "sha512-UpYjBi8xefVChsCoBpKShdxTllC9pwISirfoZsUa2AAdQg/Jd2KQGtSbw+ya7GPo7x/wAPlH6JBhKhAsXUEZNA==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*", - "@types/istanbul-lib-report": "*" - } - }, - "@types/stack-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", - "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", - "dev": true - }, - "@types/yargs": { - "version": "12.0.12", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-12.0.12.tgz", - "integrity": "sha512-SOhuU4wNBxhhTHxYaiG5NY4HBhDIDnJF60GU+2LqHAdKKer86//e4yg69aENCtQ04n0ovz+tq2YPME5t5yp4pw==", - "dev": true - }, - "@webassemblyjs/ast": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", - "integrity": "sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ==", - "dev": true, - "requires": { - "@webassemblyjs/helper-module-context": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/wast-parser": "1.8.5" - } - }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz", - "integrity": "sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ==", - "dev": true - }, - "@webassemblyjs/helper-api-error": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz", - "integrity": "sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA==", - "dev": true - }, - "@webassemblyjs/helper-buffer": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz", - "integrity": "sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q==", - "dev": true - }, - "@webassemblyjs/helper-code-frame": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz", - "integrity": "sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ==", - "dev": true, - "requires": { - "@webassemblyjs/wast-printer": "1.8.5" - } - }, - "@webassemblyjs/helper-fsm": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz", - "integrity": "sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow==", - "dev": true - }, - "@webassemblyjs/helper-module-context": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz", - "integrity": "sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "mamacro": "^0.0.3" - } - }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz", - "integrity": "sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ==", - "dev": true - }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz", - "integrity": "sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-buffer": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/wasm-gen": "1.8.5" - } - }, - "@webassemblyjs/ieee754": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz", - "integrity": "sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==", - "dev": true, - "requires": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "@webassemblyjs/leb128": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.8.5.tgz", - "integrity": "sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A==", - "dev": true, - "requires": { - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/utf8": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.8.5.tgz", - "integrity": "sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw==", - "dev": true - }, - "@webassemblyjs/wasm-edit": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz", - "integrity": "sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-buffer": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/helper-wasm-section": "1.8.5", - "@webassemblyjs/wasm-gen": "1.8.5", - "@webassemblyjs/wasm-opt": "1.8.5", - "@webassemblyjs/wasm-parser": "1.8.5", - "@webassemblyjs/wast-printer": "1.8.5" - } - }, - "@webassemblyjs/wasm-gen": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz", - "integrity": "sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/ieee754": "1.8.5", - "@webassemblyjs/leb128": "1.8.5", - "@webassemblyjs/utf8": "1.8.5" - } - }, - "@webassemblyjs/wasm-opt": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz", - "integrity": "sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-buffer": "1.8.5", - "@webassemblyjs/wasm-gen": "1.8.5", - "@webassemblyjs/wasm-parser": "1.8.5" - } - }, - "@webassemblyjs/wasm-parser": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz", - "integrity": "sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-api-error": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/ieee754": "1.8.5", - "@webassemblyjs/leb128": "1.8.5", - "@webassemblyjs/utf8": "1.8.5" - } - }, - "@webassemblyjs/wast-parser": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz", - "integrity": "sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/floating-point-hex-parser": "1.8.5", - "@webassemblyjs/helper-api-error": "1.8.5", - "@webassemblyjs/helper-code-frame": "1.8.5", - "@webassemblyjs/helper-fsm": "1.8.5", - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/wast-printer": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz", - "integrity": "sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/wast-parser": "1.8.5", - "@xtuc/long": "4.2.2" - } - }, - "@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true - }, - "abab": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz", - "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==", - "dev": true - }, - "accepts": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", - "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", - "dev": true, - "requires": { - "mime-types": "~2.1.18", - "negotiator": "0.6.1" - } - }, - "acorn": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", - "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", - "dev": true - }, - "acorn-dynamic-import": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz", - "integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==", - "dev": true - }, - "acorn-globals": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.2.tgz", - "integrity": "sha512-BbzvZhVtZP+Bs1J1HcwrQe8ycfO0wStkSGxuul3He3GkHOIZ6eTqOkPuw9IP1X3+IkOo4wiJmwkobzXYz4wewQ==", - "dev": true, - "requires": { - "acorn": "^6.0.1", - "acorn-walk": "^6.0.1" - }, - "dependencies": { - "acorn": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", - "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", - "dev": true - } - } - }, - "acorn-jsx": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz", - "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==", - "dev": true - }, - "acorn-walk": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz", - "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==", - "dev": true - }, - "agent-base": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", - "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } - }, - "ajv": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.4.tgz", - "integrity": "sha512-4Wyjt8+t6YszqaXnLDfMmG/8AlO5Zbcsy3ATHncCzjW/NoPzAId8AK6749Ybjmdt+kUY1gP60fCu46oDxPv/mg==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-errors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.0.tgz", - "integrity": "sha1-7PAh+hCP0X37Xms4Py3SM+Mf/Fk=", - "dev": true - }, - "ajv-keywords": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", - "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", - "dev": true - }, - "ansi-align": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", - "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", - "dev": true, - "requires": { - "string-width": "^2.0.0" - } - }, - "ansi-colors": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", - "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", - "dev": true - }, - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "aria-query": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz", - "integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=", - "dev": true, - "requires": { - "ast-types-flow": "0.0.7", - "commander": "^2.11.0" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true - }, - "array-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", - "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", - "dev": true - }, - "array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", - "dev": true - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", - "dev": true - }, - "array-includes": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", - "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.7.0" - } - }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, - "requires": { - "array-uniq": "^1.0.1" - } - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" - }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", - "dev": true, - "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "assert": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", - "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", - "dev": true, - "requires": { - "object-assign": "^4.1.1", - "util": "0.10.3" - }, - "dependencies": { - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", - "dev": true - }, - "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "dev": true, - "requires": { - "inherits": "2.0.1" - } - } - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true - }, - "ast-types-flow": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", - "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", - "dev": true - }, - "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", - "dev": true - }, - "async-each": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", - "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", - "dev": true - }, - "async-limiter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", - "dev": true - }, - "axobject-query": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz", - "integrity": "sha512-MCeek8ZH7hKyO1rWUbKNQBbl4l2eY0ntk7OGi+q0RlafrCnfPxC06WZA+uebCfmYp4mNU9jRBP1AhGyf8+W3ww==", - "dev": true, - "requires": { - "ast-types-flow": "0.0.7" - } - }, - "babel-eslint": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.0.1.tgz", - "integrity": "sha512-z7OT1iNV+TjOwHNLLyJk+HN+YVWX+CLE6fPD2SymJZOZQBs+QIexFjhm4keGTm8MW9xr4EC9Q0PbaLB24V5GoQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.0.0", - "@babel/traverse": "^7.0.0", - "@babel/types": "^7.0.0", - "eslint-scope": "3.7.1", - "eslint-visitor-keys": "^1.0.0" - } - }, - "babel-jest": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.8.0.tgz", - "integrity": "sha512-+5/kaZt4I9efoXzPlZASyK/lN9qdRKmmUav9smVc0ruPQD7IsfucQ87gpOE8mn2jbDuS6M/YOW6n3v9ZoIfgnw==", - "dev": true, - "requires": { - "@jest/transform": "^24.8.0", - "@jest/types": "^24.8.0", - "@types/babel__core": "^7.1.0", - "babel-plugin-istanbul": "^5.1.0", - "babel-preset-jest": "^24.6.0", - "chalk": "^2.4.2", - "slash": "^2.0.0" - }, - "dependencies": { - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - } - } - }, - "babel-loader": { - "version": "8.0.6", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.6.tgz", - "integrity": "sha512-4BmWKtBOBm13uoUwd08UwjZlaw3O9GWf456R9j+5YykFZ6LUIjIKLc0zEZf+hauxPOJs96C8k6FvYD09vWzhYw==", - "dev": true, - "requires": { - "find-cache-dir": "^2.0.0", - "loader-utils": "^1.0.2", - "mkdirp": "^0.5.1", - "pify": "^4.0.1" - }, - "dependencies": { - "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - } - } - } - }, - "babel-plugin-istanbul": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.1.4.tgz", - "integrity": "sha512-dySz4VJMH+dpndj0wjJ8JPs/7i1TdSPb1nRrn56/92pKOF9VKC1FMFJmMXjzlGGusnCAqujP6PBCiKq0sVA+YQ==", - "dev": true, - "requires": { - "find-up": "^3.0.0", - "istanbul-lib-instrument": "^3.3.0", - "test-exclude": "^5.2.3" - }, - "dependencies": { - "@babel/generator": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz", - "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.11", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", - "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4" - } - }, - "@babel/parser": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.4.tgz", - "integrity": "sha512-5pCS4mOsL+ANsFZGdvNLybx4wtqAZJ0MJjMHxvzI3bvIsz6sQvzW8XX92EYIkiPtIvcfG3Aj+Ir5VNyjnZhP7w==", - "dev": true - }, - "@babel/template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", - "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4" - } - }, - "@babel/traverse": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.4.tgz", - "integrity": "sha512-Gw6qqkw/e6AGzlyj9KnkabJX7VcubqPtkUQVAwkc0wUMldr3A/hezNB3Rc5eIvId95iSGkGIOe5hh1kMKf951A==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.4", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.11" - } - }, - "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", - "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", - "dev": true, - "requires": { - "@babel/generator": "^7.4.0", - "@babel/parser": "^7.4.3", - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0", - "istanbul-lib-coverage": "^2.0.5", - "semver": "^6.0.0" - } - }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - }, - "read-pkg-up": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", - "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", - "dev": true, - "requires": { - "find-up": "^3.0.0", - "read-pkg": "^3.0.0" - } - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "semver": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.0.0.tgz", - "integrity": "sha512-0UewU+9rFapKFnlbirLi3byoOuhrSsli/z/ihNnvM24vgF+8sNBiI1LZPBSH9wJKUwaUbw+s3hToDLCXkrghrQ==", - "dev": true - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "test-exclude": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", - "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", - "dev": true, - "requires": { - "glob": "^7.1.3", - "minimatch": "^3.0.4", - "read-pkg-up": "^4.0.0", - "require-main-filename": "^2.0.0" - } - } - } - }, - "babel-plugin-jest-hoist": { - "version": "24.6.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.6.0.tgz", - "integrity": "sha512-3pKNH6hMt9SbOv0F3WVmy5CWQ4uogS3k0GY5XLyQHJ9EGpAT9XWkFd2ZiXXtkwFHdAHa5j7w7kfxSP5lAIwu7w==", - "dev": true, - "requires": { - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-preset-jest": { - "version": "24.6.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.6.0.tgz", - "integrity": "sha512-pdZqLEdmy1ZK5kyRUfvBb2IfTPb2BUvIJczlPspS8fWmBQslNNDBqVfh7BW5leOVJMDZKzjD8XEyABTk6gQ5yw==", - "dev": true, - "requires": { - "@babel/plugin-syntax-object-rest-spread": "^7.0.0", - "babel-plugin-jest-hoist": "^24.6.0" - } - }, - "babel-preset-modern-browsers": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/babel-preset-modern-browsers/-/babel-preset-modern-browsers-14.0.0.tgz", - "integrity": "sha512-StlM59GnTowme4QO/rLeuko5BRyTq9FM/K/4Xquyd0H6pdjPQ3JbLg9O8ID0V4WDNV8YKyPFlkKn531hSn5Ifw==", - "dev": true, - "requires": { - "@babel/plugin-proposal-async-generator-functions": "^7.2.0", - "@babel/plugin-proposal-json-strings": "^7.2.0", - "@babel/plugin-proposal-object-rest-spread": "^7.3.4", - "@babel/plugin-proposal-optional-catch-binding": "^7.2.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.2.0", - "@babel/plugin-syntax-async-generators": "^7.2.0", - "@babel/plugin-syntax-json-strings": "^7.2.0", - "@babel/plugin-syntax-object-rest-spread": "^7.2.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.2.0", - "@babel/plugin-transform-arrow-functions": "^7.2.0", - "@babel/plugin-transform-function-name": "^7.2.0", - "@babel/plugin-transform-modules-commonjs": "^7.2.0" - }, - "dependencies": { - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz", - "integrity": "sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - } - } - }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" - } - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - } - } - }, - "base64-js": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", - "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "big.js": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", - "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", - "dev": true - }, - "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true - }, - "bluebird": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.4.tgz", - "integrity": "sha512-FG+nFEZChJrbQ9tIccIfZJBz3J7mLrAhxakAbnrJWn8d7aKOC+LWifa0G+p4ZqKp4y13T7juYvdhq9NzKdsrjw==", - "dev": true - }, - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true - }, - "body-parser": { - "version": "1.18.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", - "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", - "dev": true, - "requires": { - "bytes": "3.0.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "~1.6.3", - "iconv-lite": "0.4.23", - "on-finished": "~2.3.0", - "qs": "6.5.2", - "raw-body": "2.3.3", - "type-is": "~1.6.16" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", - "dev": true - }, - "boxen": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", - "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", - "dev": true, - "requires": { - "ansi-align": "^2.0.0", - "camelcase": "^4.0.0", - "chalk": "^2.0.1", - "cli-boxes": "^1.0.0", - "string-width": "^2.0.0", - "term-size": "^1.2.0", - "widest-line": "^2.0.0" - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true - }, - "browser-process-hrtime": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz", - "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==", - "dev": true - }, - "browser-resolve": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", - "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", - "dev": true, - "requires": { - "resolve": "1.1.7" - }, - "dependencies": { - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - } - } - }, - "browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "dev": true, - "requires": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "browserify-cipher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", - "dev": true, - "requires": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" - } - }, - "browserify-des": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", - "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "browserify-rsa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "randombytes": "^2.0.1" - } - }, - "browserify-sign": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", - "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", - "dev": true, - "requires": { - "bn.js": "^4.1.1", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.2", - "elliptic": "^6.0.0", - "inherits": "^2.0.1", - "parse-asn1": "^5.0.0" - } - }, - "browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", - "dev": true, - "requires": { - "pako": "~1.0.5" - } - }, - "browserslist": { - "version": "4.5.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.5.6.tgz", - "integrity": "sha512-o/hPOtbU9oX507lIqon+UvPYqpx3mHc8cV3QemSBTXwkG8gSQSK6UKvXcE/DcleU3+A59XTUHyCvZ5qGy8xVAg==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30000963", - "electron-to-chromium": "^1.3.127", - "node-releases": "^1.1.17" - } - }, - "bser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz", - "integrity": "sha1-mseNPtXZFYBP2HrLFYvHlxR6Fxk=", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, - "buffer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", - "dev": true, - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", - "dev": true - }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true - }, - "builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", - "dev": true - }, - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", - "dev": true - }, - "cacache": { - "version": "11.3.2", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz", - "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==", - "dev": true, - "requires": { - "bluebird": "^3.5.3", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.3", - "graceful-fs": "^4.1.15", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.2", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" - }, - "dependencies": { - "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "yallist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", - "dev": true - } - } - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camel-case": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", - "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", - "dev": true, - "requires": { - "no-case": "^2.2.0", - "upper-case": "^1.1.1" - } - }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, - "camelcase-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", - "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", - "dev": true, - "requires": { - "camelcase": "^2.0.0", - "map-obj": "^1.0.0" - }, - "dependencies": { - "camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", - "dev": true - } - } - }, - "caniuse-lite": { - "version": "1.0.30000967", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000967.tgz", - "integrity": "sha512-rUBIbap+VJfxTzrM4akJ00lkvVb5/n5v3EGXfWzSH5zT8aJmGzjA8HWhJ4U6kCpzxozUSnB+yvAYDRPY6mRpgQ==", - "dev": true - }, - "canvas-fit": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/canvas-fit/-/canvas-fit-1.5.0.tgz", - "integrity": "sha1-rhO+Zq3kL1vg5IfjRfzjCl5bXl8=", - "requires": { - "element-size": "^1.1.1" - } - }, - "capture-exit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", - "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", - "dev": true, - "requires": { - "rsvp": "^4.8.4" - } - }, - "capture-stack-trace": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", - "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "check-more-types": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", - "integrity": "sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA=", - "dev": true - }, - "chokidar": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.5.tgz", - "integrity": "sha512-i0TprVWp+Kj4WRPtInjexJ8Q+BqTE909VpH8xVhXrJkoc5QC8VO9TryGOqTr+2hljzc1sC62t22h5tZePodM/A==", - "dev": true, - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - } - } - }, - "chownr": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", - "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", - "dev": true - }, - "chrome-trace-event": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz", - "integrity": "sha512-xDbVgyfDTT2piup/h8dK/y4QZfJRSa73bw1WZ8b4XM1o7fsFubUVGYcE+1ANtOzJJELGpYoG2961z0Z6OAld9A==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "ci-info": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", - "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", - "dev": true - }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "classnames": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", - "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==" - }, - "clean-css": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", - "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", - "dev": true, - "requires": { - "source-map": "~0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "cli-boxes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", - "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", - "dev": true - }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "dev": true, - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", - "dev": true - }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "dev": true, - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "clone-deep": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-0.2.4.tgz", - "integrity": "sha1-TnPdCen7lxzDhnDF3O2cGJZIHMY=", - "dev": true, - "requires": { - "for-own": "^0.1.3", - "is-plain-object": "^2.0.1", - "kind-of": "^3.0.2", - "lazy-cache": "^1.0.3", - "shallow-clone": "^0.1.2" - } - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "combined-stream": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", - "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", - "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==" - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "configstore": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz", - "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==", - "dev": true, - "requires": { - "dot-prop": "^4.1.0", - "graceful-fs": "^4.1.2", - "make-dir": "^1.0.0", - "unique-string": "^1.0.0", - "write-file-atomic": "^2.0.0", - "xdg-basedir": "^3.0.0" - } - }, - "connect-history-api-fallback": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", - "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", - "dev": true - }, - "console-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", - "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", - "dev": true, - "requires": { - "date-now": "^0.1.4" - } - }, - "constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", - "dev": true - }, - "contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", - "dev": true - }, - "content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", - "dev": true - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "dev": true - }, - "convert-source-map": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", - "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", - "dev": true - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", - "dev": true - }, - "copy-concurrently": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", - "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", - "dev": true, - "requires": { - "aproba": "^1.1.1", - "fs-write-stream-atomic": "^1.0.8", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.0" - } - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true - }, - "copy-webpack-plugin": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.0.3.tgz", - "integrity": "sha512-PlZRs9CUMnAVylZq+vg2Juew662jWtwOXOqH4lbQD9ZFhRG9R7tVStOgHt21CBGVq7k5yIJaz8TXDLSjV+Lj8Q==", - "dev": true, - "requires": { - "cacache": "^11.3.2", - "find-cache-dir": "^2.1.0", - "glob-parent": "^3.1.0", - "globby": "^7.1.1", - "is-glob": "^4.0.1", - "loader-utils": "^1.2.3", - "minimatch": "^3.0.4", - "normalize-path": "^3.0.0", - "p-limit": "^2.2.0", - "schema-utils": "^1.0.0", - "serialize-javascript": "^1.7.0", - "webpack-log": "^2.0.0" - }, - "dependencies": { - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true - }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", - "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^2.0.0", - "json5": "^1.0.1" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "serialize-javascript": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.7.0.tgz", - "integrity": "sha512-ke8UG8ulpFOxO8f8gRYabHQe/ZntKlcig2Mp+8+URDP1D8vJZ0KUt7LYo07q25Z/+JVSgpr/cui9PIp5H6/+nA==", - "dev": true - } - } - }, - "core-js": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", - "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==" - }, - "core-js-compat": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.0.1.tgz", - "integrity": "sha512-2pC3e+Ht/1/gD7Sim/sqzvRplMiRnFQVlPpDVaHtY9l7zZP7knamr3VRD6NyGfHd84MrDC0tAM9ulNxYMW0T3g==", - "dev": true, - "requires": { - "browserslist": "^4.5.4", - "core-js": "3.0.1", - "core-js-pure": "3.0.1", - "semver": "^6.0.0" - }, - "dependencies": { - "core-js": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.0.1.tgz", - "integrity": "sha512-sco40rF+2KlE0ROMvydjkrVMMG1vYilP2ALoRXcYR4obqbYIuV3Bg+51GEDW+HF8n7NRA+iaA4qD0nD9lo9mew==", - "dev": true - }, - "semver": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.0.0.tgz", - "integrity": "sha512-0UewU+9rFapKFnlbirLi3byoOuhrSsli/z/ihNnvM24vgF+8sNBiI1LZPBSH9wJKUwaUbw+s3hToDLCXkrghrQ==", - "dev": true - } - } - }, - "core-js-pure": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.0.1.tgz", - "integrity": "sha512-mSxeQ6IghKW3MoyF4cz19GJ1cMm7761ON+WObSyLfTu/Jn3x7w4NwNFnrZxgl4MTSvYYepVLNuRtlB4loMwJ5g==", - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "create-ecdh": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", - "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "elliptic": "^6.0.0" - } - }, - "create-error-class": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", - "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", - "dev": true, - "requires": { - "capture-stack-trace": "^1.0.0" - } - }, - "create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } - }, - "create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "dev": true, - "requires": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "create-react-context": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.2.3.tgz", - "integrity": "sha512-CQBmD0+QGgTaxDL3OX1IDXYqjkp2It4RIbcb99jS6AEg27Ga+a9G3JtK6SIu0HBwPLZlmwt9F7UwWA4Bn92Rag==", - "requires": { - "fbjs": "^0.8.0", - "gud": "^1.0.0" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "dev": true, - "requires": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" - } - }, - "crypto-random-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", - "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=", - "dev": true - }, - "css-loader": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-2.1.1.tgz", - "integrity": "sha512-OcKJU/lt232vl1P9EEDamhoO9iKY3tIjY5GU+XDLblAykTdgs6Ux9P1hTHve8nFKy5KPpOXOsVI/hIwi3841+w==", - "dev": true, - "requires": { - "camelcase": "^5.2.0", - "icss-utils": "^4.1.0", - "loader-utils": "^1.2.3", - "normalize-path": "^3.0.0", - "postcss": "^7.0.14", - "postcss-modules-extract-imports": "^2.0.0", - "postcss-modules-local-by-default": "^2.0.6", - "postcss-modules-scope": "^2.1.0", - "postcss-modules-values": "^2.0.0", - "postcss-value-parser": "^3.3.0", - "schema-utils": "^1.0.0" - }, - "dependencies": { - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", - "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^2.0.0", - "json5": "^1.0.1" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - } - } - }, - "css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", - "dev": true, - "requires": { - "boolbase": "~1.0.0", - "css-what": "2.1", - "domutils": "1.5.1", - "nth-check": "~1.0.1" - } - }, - "css-what": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz", - "integrity": "sha1-lGfQMsOM+u+58teVASUwYvh/ob0=", - "dev": true - }, - "cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true - }, - "cssom": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.6.tgz", - "integrity": "sha512-DtUeseGk9/GBW0hl0vVPpU22iHL6YB5BUX7ml1hB+GMpo0NX5G4voX3kdWiMSEguFtcW3Vh3djqNF4aIe6ne0A==", - "dev": true - }, - "cssstyle": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.2.2.tgz", - "integrity": "sha512-43wY3kl1CVQSvL7wUY1qXkxVGkStjpkDmVjiIKX8R97uhajy8Bybay78uOtqvh7Q5GK75dNPfW0geWjE6qQQow==", - "dev": true, - "requires": { - "cssom": "0.3.x" - } - }, - "currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", - "dev": true, - "requires": { - "array-find-index": "^1.0.1" - } - }, - "cwd": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/cwd/-/cwd-0.10.0.tgz", - "integrity": "sha1-FyQAaUBXwioTsM8WFix+S3p/5Wc=", - "dev": true, - "requires": { - "find-pkg": "^0.1.2", - "fs-exists-sync": "^0.1.0" - } - }, - "cyclist": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", - "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=", - "dev": true - }, - "d3": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/d3/-/d3-4.13.0.tgz", - "integrity": "sha512-l8c4+0SldjVKLaE2WG++EQlqD7mh/dmQjvi2L2lKPadAVC+TbJC4ci7Uk9bRi+To0+ansgsS0iWfPjD7DBy+FQ==", - "requires": { - "d3-array": "1.2.1", - "d3-axis": "1.0.8", - "d3-brush": "1.0.4", - "d3-chord": "1.0.4", - "d3-collection": "1.0.4", - "d3-color": "1.0.3", - "d3-dispatch": "1.0.3", - "d3-drag": "1.2.1", - "d3-dsv": "1.0.8", - "d3-ease": "1.0.3", - "d3-force": "1.1.0", - "d3-format": "1.2.2", - "d3-geo": "1.9.1", - "d3-hierarchy": "1.1.5", - "d3-interpolate": "1.1.6", - "d3-path": "1.0.5", - "d3-polygon": "1.0.3", - "d3-quadtree": "1.0.3", - "d3-queue": "3.0.7", - "d3-random": "1.1.0", - "d3-request": "1.0.6", - "d3-scale": "1.0.7", - "d3-selection": "1.3.0", - "d3-shape": "1.2.0", - "d3-time": "1.0.8", - "d3-time-format": "2.1.1", - "d3-timer": "1.0.7", - "d3-transition": "1.1.1", - "d3-voronoi": "1.1.2", - "d3-zoom": "1.7.1" - } - }, - "d3-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.1.tgz", - "integrity": "sha512-CyINJQ0SOUHojDdFDH4JEM0552vCR1utGyLHegJHyYH0JyCpSeTPxi4OBqHMA2jJZq4NH782LtaJWBImqI/HBw==" - }, - "d3-axis": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.8.tgz", - "integrity": "sha1-MacFoLU15ldZ3hQXOjGTMTfxjvo=" - }, - "d3-brush": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.0.4.tgz", - "integrity": "sha1-AMLyOAGfJPbAoZSibUGhUw/+e8Q=", - "requires": { - "d3-dispatch": "1", - "d3-drag": "1", - "d3-interpolate": "1", - "d3-selection": "1", - "d3-transition": "1" - } - }, - "d3-chord": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.4.tgz", - "integrity": "sha1-fexPC6iG9xP+ERxF92NBT290yiw=", - "requires": { - "d3-array": "1", - "d3-path": "1" - } - }, - "d3-collection": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.4.tgz", - "integrity": "sha1-NC39EoN8kJdPM/HMCnha6lcNzcI=" - }, - "d3-color": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.0.3.tgz", - "integrity": "sha1-vHZD/KjlOoNH4vva/6I2eWtYUJs=" - }, - "d3-dispatch": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.3.tgz", - "integrity": "sha1-RuFJHqqbWMNY/OW+TovtYm54cfg=" - }, - "d3-drag": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.1.tgz", - "integrity": "sha512-Cg8/K2rTtzxzrb0fmnYOUeZHvwa4PHzwXOLZZPwtEs2SKLLKLXeYwZKBB+DlOxUvFmarOnmt//cU4+3US2lyyQ==", - "requires": { - "d3-dispatch": "1", - "d3-selection": "1" - } - }, - "d3-dsv": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.0.8.tgz", - "integrity": "sha512-IVCJpQ+YGe3qu6odkPQI0KPqfxkhbP/oM1XhhE/DFiYmcXKfCRub4KXyiuehV1d4drjWVXHUWx4gHqhdZb6n/A==", - "requires": { - "commander": "2", - "iconv-lite": "0.4", - "rw": "1" - } - }, - "d3-ease": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.3.tgz", - "integrity": "sha1-aL+8NJM4o4DETYrMT7wzBKotjA4=" - }, - "d3-force": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.1.0.tgz", - "integrity": "sha512-2HVQz3/VCQs0QeRNZTYb7GxoUCeb6bOzMp/cGcLa87awY9ZsPvXOGeZm0iaGBjXic6I1ysKwMn+g+5jSAdzwcg==", - "requires": { - "d3-collection": "1", - "d3-dispatch": "1", - "d3-quadtree": "1", - "d3-timer": "1" - } - }, - "d3-format": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.2.2.tgz", - "integrity": "sha512-zH9CfF/3C8zUI47nsiKfD0+AGDEuM8LwBIP7pBVpyR4l/sKkZqITmMtxRp04rwBrlshIZ17XeFAaovN3++wzkw==" - }, - "d3-geo": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.9.1.tgz", - "integrity": "sha512-l9wL/cEQkyZQYXw3xbmLsH3eQ5ij+icNfo4r0GrLa5rOCZR/e/3am45IQ0FvQ5uMsv+77zBRunLc9ufTWSQYFA==", - "requires": { - "d3-array": "1" - } - }, - "d3-hierarchy": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.5.tgz", - "integrity": "sha1-ochFxC+Eoga88cAcAQmOpN2qeiY=" - }, - "d3-interpolate": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.1.6.tgz", - "integrity": "sha512-mOnv5a+pZzkNIHtw/V6I+w9Lqm9L5bG3OTXPM5A+QO0yyVMQ4W1uZhR+VOJmazaOZXri2ppbiZ5BUNWT0pFM9A==", - "requires": { - "d3-color": "1" - } - }, - "d3-path": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.5.tgz", - "integrity": "sha1-JB6xhJvZ6egCHA0KeZ+KDo5EF2Q=" - }, - "d3-polygon": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.3.tgz", - "integrity": "sha1-FoiOkCZGCTPysXllKtN4Ik04LGI=" - }, - "d3-quadtree": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.3.tgz", - "integrity": "sha1-rHmH4+I/6AWpkPKOG1DTj8uCJDg=" - }, - "d3-queue": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/d3-queue/-/d3-queue-3.0.7.tgz", - "integrity": "sha1-yTouVLQXwJWRKdfXP2z31Ckudhg=" - }, - "d3-random": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.1.0.tgz", - "integrity": "sha1-ZkLlBsb6OmSFldKyRpeIqNElKdM=" - }, - "d3-request": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/d3-request/-/d3-request-1.0.6.tgz", - "integrity": "sha512-FJj8ySY6GYuAJHZMaCQ83xEYE4KbkPkmxZ3Hu6zA1xxG2GD+z6P+Lyp+zjdsHf0xEbp2xcluDI50rCS855EQ6w==", - "requires": { - "d3-collection": "1", - "d3-dispatch": "1", - "d3-dsv": "1", - "xmlhttprequest": "1" - } - }, - "d3-scale": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-1.0.7.tgz", - "integrity": "sha512-KvU92czp2/qse5tUfGms6Kjig0AhHOwkzXG0+PqIJB3ke0WUv088AHMZI0OssO9NCkXt4RP8yju9rpH8aGB7Lw==", - "requires": { - "d3-array": "^1.2.0", - "d3-collection": "1", - "d3-color": "1", - "d3-format": "1", - "d3-interpolate": "1", - "d3-time": "1", - "d3-time-format": "2" - } - }, - "d3-scale-chromatic": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-1.3.3.tgz", - "integrity": "sha512-BWTipif1CimXcYfT02LKjAyItX5gKiwxuPRgr4xM58JwlLocWbjPLI7aMEjkcoOQXMkYsmNsvv3d2yl/OKuHHw==", - "requires": { - "d3-color": "1", - "d3-interpolate": "1" - } - }, - "d3-selection": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.3.0.tgz", - "integrity": "sha512-qgpUOg9tl5CirdqESUAu0t9MU/t3O9klYfGfyKsXEmhyxyzLpzpeh08gaxBUTQw1uXIOkr/30Ut2YRjSSxlmHA==" - }, - "d3-shape": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.2.0.tgz", - "integrity": "sha1-RdAVOPBkuv0F6j1tLLdI/YxB93c=", - "requires": { - "d3-path": "1" - } - }, - "d3-time": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.0.8.tgz", - "integrity": "sha512-YRZkNhphZh3KcnBfitvF3c6E0JOFGikHZ4YqD+Lzv83ZHn1/u6yGenRU1m+KAk9J1GnZMnKcrtfvSktlA1DXNQ==" - }, - "d3-time-format": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.1.1.tgz", - "integrity": "sha512-8kAkymq2WMfzW7e+s/IUNAtN/y3gZXGRrdGfo6R8NKPAA85UBTxZg5E61bR6nLwjPjj4d3zywSQe1CkYLPFyrw==", - "requires": { - "d3-time": "1" - } - }, - "d3-timer": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.7.tgz", - "integrity": "sha512-vMZXR88XujmG/L5oB96NNKH5lCWwiLM/S2HyyAQLcjWJCloK5shxta4CwOFYLZoY3AWX73v8Lgv4cCAdWtRmOA==" - }, - "d3-transition": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.1.1.tgz", - "integrity": "sha512-xeg8oggyQ+y5eb4J13iDgKIjUcEfIOZs2BqV/eEmXm2twx80wTzJ4tB4vaZ5BKfz7XsI/DFmQL5me6O27/5ykQ==", - "requires": { - "d3-color": "1", - "d3-dispatch": "1", - "d3-ease": "1", - "d3-interpolate": "1", - "d3-selection": "^1.1.0", - "d3-timer": "1" - } - }, - "d3-voronoi": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.2.tgz", - "integrity": "sha1-Fodmfo8TotFYyAwUgMWinLDYlzw=" - }, - "d3-zoom": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.7.1.tgz", - "integrity": "sha512-sZHQ55DGq5BZBFGnRshUT8tm2sfhPHFnOlmPbbwTkAoPeVdRTkB4Xsf9GCY0TSHrTD8PeJPZGmP/TpGicwJDJQ==", - "requires": { - "d3-dispatch": "1", - "d3-drag": "1", - "d3-interpolate": "1", - "d3-selection": "1", - "d3-transition": "1" - } - }, - "damerau-levenshtein": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz", - "integrity": "sha1-AxkcQyy27qFou3fzpV/9zLiXhRQ=", - "dev": true - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "data-urls": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", - "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", - "dev": true, - "requires": { - "abab": "^2.0.0", - "whatwg-mimetype": "^2.2.0", - "whatwg-url": "^7.0.0" - }, - "dependencies": { - "whatwg-url": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", - "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", - "dev": true, - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - } - } - }, - "date-now": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", - "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", - "dev": true - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - } - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true - }, - "des.js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", - "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", - "dev": true - }, - "detect-file": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", - "dev": true - }, - "detect-newline": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", - "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", - "dev": true - }, - "diff-sequences": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.3.0.tgz", - "integrity": "sha512-xLqpez+Zj9GKSnPWS0WZw1igGocZ+uua8+y+5dDNTT934N3QuY1sp2LkHzwiaYQGz60hMq0pjAshdeXm5VUOEw==", - "dev": true - }, - "diffie-hellman": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" - } - }, - "dir-glob": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", - "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", - "dev": true, - "requires": { - "path-type": "^3.0.0" - }, - "dependencies": { - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - } - } - }, - "doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" - } - }, - "dom-converter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", - "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", - "dev": true, - "requires": { - "utila": "~0.4" - } - }, - "dom-helpers": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", - "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", - "requires": { - "@babel/runtime": "^7.1.2" - } - }, - "dom-scroll-into-view": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dom-scroll-into-view/-/dom-scroll-into-view-1.0.1.tgz", - "integrity": "sha1-Mqu5Lw2P7KYhUWKu9D5LRJq42Zw=" - }, - "dom-serializer": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", - "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", - "dev": true, - "requires": { - "domelementtype": "~1.1.1", - "entities": "~1.1.1" - }, - "dependencies": { - "domelementtype": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", - "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", - "dev": true - } - } - }, - "dom-urls": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/dom-urls/-/dom-urls-1.1.0.tgz", - "integrity": "sha1-AB3fgWKM0ecGElxxdvU8zsVdkY4=", - "dev": true, - "requires": { - "urijs": "^1.16.1" - } - }, - "dom4": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/dom4/-/dom4-2.1.3.tgz", - "integrity": "sha512-begvh4z5GV0kyxx+YgJZ7sIo/jsELx/v7MQxoLZpOvT5yFo18X8dfgtUmKAwdGuyMeugncylarLHlO4gIK6YNw==" - }, - "domain-browser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", - "dev": true - }, - "domelementtype": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", - "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=", - "dev": true - }, - "domexception": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", - "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", - "dev": true, - "requires": { - "webidl-conversions": "^4.0.2" - } - }, - "domhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.1.0.tgz", - "integrity": "sha1-0mRvXlf2w7qxHPbLBdPArPdBJZQ=", - "dev": true, - "requires": { - "domelementtype": "1" - } - }, - "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", - "dev": true, - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "dot-prop": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", - "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", - "dev": true, - "requires": { - "is-obj": "^1.0.0" - } - }, - "duplexer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", - "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", - "dev": true - }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", - "dev": true - }, - "duplexify": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.1.tgz", - "integrity": "sha512-vM58DwdnKmty+FSPzT14K9JXb90H+j5emaR4KYbr2KTIz00WHGbWOe5ghQTx233ZCLZtrGDALzKwcjEtSt35mA==", - "dev": true, - "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" - } - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", - "dev": true - }, - "electron-to-chromium": { - "version": "1.3.133", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.133.tgz", - "integrity": "sha512-lyoC8aoqbbDqsprb6aPdt9n3DpOZZzdz/T4IZKsR0/dkZIxnJVUjjcpOSwA66jPRIOyDAamCTAUqweU05kKNSg==", - "dev": true - }, - "element-size": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/element-size/-/element-size-1.1.1.tgz", - "integrity": "sha1-ZOXxWdlxIWMYRby67K8nnDm1404=" - }, - "elliptic": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", - "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", - "dev": true, - "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" - } - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", - "dev": true - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "dev": true - }, - "encoding": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", - "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", - "requires": { - "iconv-lite": "~0.4.13" - } - }, - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "enhanced-resolve": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", - "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.4.0", - "tapable": "^1.0.0" - } - }, - "entities": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", - "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=", - "dev": true - }, - "errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", - "dev": true, - "requires": { - "prr": "~1.0.1" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-abstract": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz", - "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", - "dev": true, - "requires": { - "es-to-primitive": "^1.1.1", - "function-bind": "^1.1.1", - "has": "^1.0.1", - "is-callable": "^1.1.3", - "is-regex": "^1.0.4" - } - }, - "es-to-primitive": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", - "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "es6-promise": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz", - "integrity": "sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg==", - "dev": true - }, - "es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", - "dev": true, - "requires": { - "es6-promise": "^4.0.3" - } - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "escodegen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.1.tgz", - "integrity": "sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw==", - "dev": true, - "requires": { - "esprima": "^3.1.3", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - }, - "dependencies": { - "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true - } - } - }, - "eslint": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz", - "integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "ajv": "^6.9.1", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "eslint-scope": "^4.0.3", - "eslint-utils": "^1.3.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^5.0.1", - "esquery": "^1.0.1", - "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", - "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", - "globals": "^11.7.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "inquirer": "^6.2.2", - "js-yaml": "^3.13.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.11", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", - "progress": "^2.0.0", - "regexpp": "^2.0.1", - "semver": "^5.5.1", - "strip-ansi": "^4.0.0", - "strip-json-comments": "^2.0.1", - "table": "^5.2.3", - "text-table": "^0.2.0" - }, - "dependencies": { - "ajv": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", - "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "eslint-config-airbnb": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-17.1.0.tgz", - "integrity": "sha512-R9jw28hFfEQnpPau01NO5K/JWMGLi6aymiF6RsnMURjTk+MqZKllCqGK/0tOvHkPi/NWSSOU2Ced/GX++YxLnw==", - "dev": true, - "requires": { - "eslint-config-airbnb-base": "^13.1.0", - "object.assign": "^4.1.0", - "object.entries": "^1.0.4" - } - }, - "eslint-config-airbnb-base": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-13.1.0.tgz", - "integrity": "sha512-XWwQtf3U3zIoKO1BbHh6aUhJZQweOwSt4c2JrPDg9FP3Ltv3+YfEv7jIDB8275tVnO/qOHbfuYg3kzw6Je7uWw==", - "dev": true, - "requires": { - "eslint-restricted-globals": "^0.1.1", - "object.assign": "^4.1.0", - "object.entries": "^1.0.4" - } - }, - "eslint-config-prettier": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-4.2.0.tgz", - "integrity": "sha512-y0uWc/FRfrHhpPZCYflWC8aE0KRJRY04rdZVfl8cL3sEZmOYyaBdhdlQPjKZBnuRMyLVK+JUZr7HaZFClQiH4w==", - "dev": true, - "requires": { - "get-stdin": "^6.0.0" - }, - "dependencies": { - "get-stdin": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", - "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", - "dev": true - } - } - }, - "eslint-import-resolver-node": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz", - "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==", - "dev": true, - "requires": { - "debug": "^2.6.9", - "resolve": "^1.5.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "eslint-loader": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-2.1.2.tgz", - "integrity": "sha512-rA9XiXEOilLYPOIInvVH5S/hYfyTPyxag6DZhoQOduM+3TkghAEQ3VcFO8VnX4J4qg/UIBzp72aOf/xvYmpmsg==", - "dev": true, - "requires": { - "loader-fs-cache": "^1.0.0", - "loader-utils": "^1.0.2", - "object-assign": "^4.0.1", - "object-hash": "^1.1.4", - "rimraf": "^2.6.1" - } - }, - "eslint-module-utils": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.4.0.tgz", - "integrity": "sha512-14tltLm38Eu3zS+mt0KvILC3q8jyIAH518MlG+HO0p+yK885Lb1UHTY/UgR91eOyGdmxAPb+OLoW4znqIT6Ndw==", - "dev": true, - "requires": { - "debug": "^2.6.8", - "pkg-dir": "^2.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "eslint-plugin-filenames": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-filenames/-/eslint-plugin-filenames-1.3.2.tgz", - "integrity": "sha512-tqxJTiEM5a0JmRCUYQmxw23vtTxrb2+a3Q2mMOPhFxvt7ZQQJmdiuMby9B/vUAuVMghyP7oET+nIf6EO6CBd/w==", - "dev": true, - "requires": { - "lodash.camelcase": "4.3.0", - "lodash.kebabcase": "4.1.1", - "lodash.snakecase": "4.1.1", - "lodash.upperfirst": "4.3.1" - } - }, - "eslint-plugin-import": { - "version": "2.17.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.17.2.tgz", - "integrity": "sha512-m+cSVxM7oLsIpmwNn2WXTJoReOF9f/CtLMo7qOVmKd1KntBy0hEcuNZ3erTmWjx+DxRO0Zcrm5KwAvI9wHcV5g==", - "dev": true, - "requires": { - "array-includes": "^3.0.3", - "contains-path": "^0.1.0", - "debug": "^2.6.9", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.2", - "eslint-module-utils": "^2.4.0", - "has": "^1.0.3", - "lodash": "^4.17.11", - "minimatch": "^3.0.4", - "read-pkg-up": "^2.0.0", - "resolve": "^1.10.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "requires": { - "pify": "^2.0.0" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - } - }, - "resolve": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.1.tgz", - "integrity": "sha512-KuIe4mf++td/eFb6wkaPbMDnP6kObCaEtIDuHOUED6MNUo4K670KZUHuuvYPZDxNF0WVLw49n06M2m2dXphEzA==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } - } - }, - "eslint-plugin-jest": { - "version": "22.5.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-22.5.1.tgz", - "integrity": "sha512-c3WjZR/HBoi4GedJRwo2OGHa8Pzo1EbSVwQ2HFzJ+4t2OoYM7Alx646EH/aaxZ+9eGcPiq0FT0UGkRuFFx2FHg==", - "dev": true - }, - "eslint-plugin-jsx-a11y": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.1.tgz", - "integrity": "sha512-cjN2ObWrRz0TTw7vEcGQrx+YltMvZoOEx4hWU8eEERDnBIU00OTq7Vr+jA7DFKxiwLNv4tTh5Pq2GUNEa8b6+w==", - "dev": true, - "requires": { - "aria-query": "^3.0.0", - "array-includes": "^3.0.3", - "ast-types-flow": "^0.0.7", - "axobject-query": "^2.0.2", - "damerau-levenshtein": "^1.0.4", - "emoji-regex": "^7.0.2", - "has": "^1.0.3", - "jsx-ast-utils": "^2.0.1" - } - }, - "eslint-plugin-react": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.13.0.tgz", - "integrity": "sha512-uA5LrHylu8lW/eAH3bEQe9YdzpPaFd9yAJTwTi/i/BKTD7j6aQMKVAdGM/ML72zD6womuSK7EiGtMKuK06lWjQ==", - "dev": true, - "requires": { - "array-includes": "^3.0.3", - "doctrine": "^2.1.0", - "has": "^1.0.3", - "jsx-ast-utils": "^2.1.0", - "object.fromentries": "^2.0.0", - "prop-types": "^15.7.2", - "resolve": "^1.10.1" - }, - "dependencies": { - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "jsx-ast-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.1.0.tgz", - "integrity": "sha512-yDGDG2DS4JcqhA6blsuYbtsT09xL8AoLuUR2Gb5exrw7UEM19sBcOTq+YBBhrNbl0PUC4R4LnFu+dHg2HKeVvA==", - "dev": true, - "requires": { - "array-includes": "^3.0.3" - } - }, - "prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", - "dev": true, - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.8.1" - } - }, - "react-is": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", - "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==", - "dev": true - }, - "resolve": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.1.tgz", - "integrity": "sha512-KuIe4mf++td/eFb6wkaPbMDnP6kObCaEtIDuHOUED6MNUo4K670KZUHuuvYPZDxNF0WVLw49n06M2m2dXphEzA==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - } - } - }, - "eslint-restricted-globals": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz", - "integrity": "sha1-NfDVy8ZMLj7WLpO0saevBbp+1Nc=", - "dev": true - }, - "eslint-scope": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", - "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", - "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==", - "dev": true - }, - "eslint-visitor-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", - "dev": true - }, - "espree": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz", - "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==", - "dev": true, - "requires": { - "acorn": "^6.0.7", - "acorn-jsx": "^5.0.0", - "eslint-visitor-keys": "^1.0.0" - }, - "dependencies": { - "acorn": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", - "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", - "dev": true - } - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", - "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", - "dev": true, - "requires": { - "estraverse": "^4.0.0" - } - }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "dev": true, - "requires": { - "estraverse": "^4.1.0" - } - }, - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", - "dev": true - }, - "event-stream": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", - "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", - "dev": true, - "requires": { - "duplexer": "~0.1.1", - "from": "~0", - "map-stream": "~0.1.0", - "pause-stream": "0.0.11", - "split": "0.3", - "stream-combiner": "~0.0.4", - "through": "~2.3.1" - } - }, - "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" - }, - "evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, - "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" - } - }, - "exec-sh": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.2.tgz", - "integrity": "sha512-9sLAvzhI5nc8TpuQUh4ahMdCrWT00wPWz7j47/emR5+2qEfoZP5zzUXvx+vdx+H6ohhnsYC31iX04QLYJK8zTg==", - "dev": true - }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "dev": true, - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - } - } - }, - "exenv": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz", - "integrity": "sha1-KueOhdmJQVhnCwPUe+wfA72Ru50=" - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "expand-tilde": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-1.2.2.tgz", - "integrity": "sha1-C4HrqJflo9MdHD0QL48BRB5VlEk=", - "dev": true, - "requires": { - "os-homedir": "^1.0.1" - } - }, - "expect": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-24.8.0.tgz", - "integrity": "sha512-/zYvP8iMDrzaaxHVa724eJBCKqSHmO0FA7EDkBiRHxg6OipmMn1fN+C8T9L9K8yr7UONkOifu6+LLH+z76CnaA==", - "dev": true, - "requires": { - "@jest/types": "^24.8.0", - "ansi-styles": "^3.2.0", - "jest-get-type": "^24.8.0", - "jest-matcher-utils": "^24.8.0", - "jest-message-util": "^24.8.0", - "jest-regex-util": "^24.3.0" - } - }, - "expect-puppeteer": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/expect-puppeteer/-/expect-puppeteer-4.1.1.tgz", - "integrity": "sha512-xNpu6uYJL9Qrrp4Z31MOpDWK68zAi+2qg5aMQlyOTVZNy7cAgBZiPvKCN0C1JmP3jgPZfcxhetVjZLaw/KcJOQ==", - "dev": true - }, - "express": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", - "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", - "dev": true, - "requires": { - "accepts": "~1.3.5", - "array-flatten": "1.1.1", - "body-parser": "1.18.3", - "content-disposition": "0.5.2", - "content-type": "~1.0.4", - "cookie": "0.3.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.1.1", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.4", - "qs": "6.5.2", - "range-parser": "~1.2.0", - "safe-buffer": "5.1.2", - "send": "0.16.2", - "serve-static": "1.13.2", - "setprototypeof": "1.1.0", - "statuses": "~1.4.0", - "type-is": "~1.6.16", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "external-editor": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", - "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==", - "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - } - } - }, - "extract-zip": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz", - "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=", - "dev": true, - "requires": { - "concat-stream": "1.6.2", - "debug": "2.6.9", - "mkdirp": "0.5.1", - "yauzl": "2.4.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "fb-watchman": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.0.tgz", - "integrity": "sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg=", - "dev": true, - "requires": { - "bser": "^2.0.0" - } - }, - "fbjs": { - "version": "0.8.17", - "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", - "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", - "requires": { - "core-js": "^1.0.0", - "isomorphic-fetch": "^2.1.1", - "loose-envify": "^1.0.0", - "object-assign": "^4.1.0", - "promise": "^7.1.1", - "setimmediate": "^1.0.5", - "ua-parser-js": "^0.7.18" - }, - "dependencies": { - "core-js": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", - "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" - } - } - }, - "fd-slicer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", - "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", - "dev": true, - "requires": { - "pend": "~1.2.0" - } - }, - "figgy-pudding": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", - "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==", - "dev": true - }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-entry-cache": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", - "dev": true, - "requires": { - "flat-cache": "^2.0.1" - } - }, - "file-loader": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-3.0.1.tgz", - "integrity": "sha512-4sNIOXgtH/9WZq4NvlfU3Opn5ynUsqBwSLyM+I7UOwdGigTBYfVVQEwe/msZNX/j4pCJTIM14Fsw66Svo1oVrw==", - "dev": true, - "requires": { - "loader-utils": "^1.0.2", - "schema-utils": "^1.0.0" - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - } - } - }, - "finalhandler": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", - "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", - "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "statuses": "~1.4.0", - "unpipe": "~1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - } - } - } - }, - "find-file-up": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/find-file-up/-/find-file-up-0.1.3.tgz", - "integrity": "sha1-z2gJG8+fMApA2kEbN9pczlovvqA=", - "dev": true, - "requires": { - "fs-exists-sync": "^0.1.0", - "resolve-dir": "^0.1.0" - } - }, - "find-pkg": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/find-pkg/-/find-pkg-0.1.2.tgz", - "integrity": "sha1-G9wiwG42NlUy4qJIBGhUuXiNpVc=", - "dev": true, - "requires": { - "find-file-up": "^0.1.2" - } - }, - "find-process": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/find-process/-/find-process-1.4.1.tgz", - "integrity": "sha512-RkYWDeukxEoDKUyocqMGKAYuwhSwq77zL99gCqhX9czWon3otdlzihJ0MSZ6YWNKHyvS/MN2YR4+RGYOuIEANg==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "commander": "^2.11.0", - "debug": "^2.6.8" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "findup-sync": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", - "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", - "dev": true, - "requires": { - "detect-file": "^1.0.0", - "is-glob": "^3.1.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" - }, - "dependencies": { - "expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", - "dev": true, - "requires": { - "homedir-polyfill": "^1.0.1" - } - }, - "global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", - "dev": true, - "requires": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" - } - }, - "global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", - "dev": true, - "requires": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" - } - }, - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - }, - "resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", - "dev": true, - "requires": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" - } - } - } - }, - "flat-cache": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", - "dev": true, - "requires": { - "flatted": "^2.0.0", - "rimraf": "2.6.3", - "write": "1.0.3" - } - }, - "flatbuffers": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-1.10.2.tgz", - "integrity": "sha512-VK7lHZF/corkykjXZ0+dqViI8Wk1YpwPCFN2wrnTs+PMCMG5+uHRvkRW14fuA7Smkhkgx+Dj5UdS3YXktJL+qw==" - }, - "flatted": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.0.tgz", - "integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==", - "dev": true - }, - "flush-write-stream": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz", - "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.4" - } - }, - "font-color-contrast": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/font-color-contrast/-/font-color-contrast-1.0.3.tgz", - "integrity": "sha1-d9iP+Xxr0JXPqd/rRGJLqK1xe2w=" - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true - }, - "for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", - "dev": true, - "requires": { - "for-in": "^1.0.1" - } - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", - "dev": true - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "requires": { - "map-cache": "^0.2.2" - } - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", - "dev": true - }, - "from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", - "dev": true - }, - "from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - } - }, - "fs-exists-sync": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz", - "integrity": "sha1-mC1ok6+RjnLQjeyehnP/K1qNat0=", - "dev": true - }, - "fs-write-stream-atomic": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", - "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "iferr": "^0.1.5", - "imurmurhash": "^0.1.4", - "readable-stream": "1 || 2" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", - "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", - "dev": true, - "optional": true, - "requires": { - "nan": "^2.12.1", - "node-pre-gyp": "^0.12.0" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "debug": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ms": "^2.1.1" - } - }, - "deep-extend": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "fs-minipass": { - "version": "1.2.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.24", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "ini": { - "version": "1.3.5", - "bundled": true, - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true, - "optional": true - }, - "minipass": { - "version": "2.3.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "needle": { - "version": "2.3.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "debug": "^4.1.0", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.12.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "optional": true - }, - "npm-packlist": { - "version": "1.4.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "rimraf": { - "version": "2.6.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "sax": { - "version": "1.2.4", - "bundled": true, - "dev": true, - "optional": true - }, - "semver": { - "version": "5.7.0", - "bundled": true, - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "tar": { - "version": "4.4.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.3.4", - "minizlib": "^1.1.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.2" - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "wide-align": { - "version": "1.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "yallist": { - "version": "3.0.3", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "fuzzysort": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/fuzzysort/-/fuzzysort-1.1.4.tgz", - "integrity": "sha512-JzK/lHjVZ6joAg3OnCjylwYXYVjRiwTY6Yb25LvfpJHK8bjisfnZJ5bY8aVWwTwCXgxPNgLAtmHL+Hs5q1ddLQ==" - }, - "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", - "dev": true - }, - "get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "gl-mat4": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gl-mat4/-/gl-mat4-1.2.0.tgz", - "integrity": "sha512-sT5C0pwB1/e9G9AvAoLsoaJtbMGjfd/jfxo8jMCKqYYEnjZuFvqV5rehqar0538EmssjdDeiEWnKyBSTw7quoA==" - }, - "gl-matrix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.0.0.tgz", - "integrity": "sha512-PD4mVH/C/Zs64kOozeFnKY8ybhgwxXXQYGWdB4h68krAHknWJgk9uKOn6z8YElh5//vs++90pb6csrTIDWnexA==" - }, - "gl-vec3": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/gl-vec3/-/gl-vec3-1.1.3.tgz", - "integrity": "sha512-jduKUqT0SGH02l8Yl+mV1yVsDfYgQAJyXGxkJQGyxPLHRiW25DwVIRPt6uvhrEMHftJfqhqKthRcyZqNEl9Xdw==" - }, - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "global-dirs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", - "dev": true, - "requires": { - "ini": "^1.3.4" - } - }, - "global-modules": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-0.2.3.tgz", - "integrity": "sha1-6lo77ULG1s6ZWk+KEmm12uIjgo0=", - "dev": true, - "requires": { - "global-prefix": "^0.1.4", - "is-windows": "^0.2.0" - }, - "dependencies": { - "is-windows": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz", - "integrity": "sha1-3hqm1j6indJIc3tp8f+LgALSEIw=", - "dev": true - } - } - }, - "global-prefix": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-0.1.5.tgz", - "integrity": "sha1-jTvGuNo8qBEqFg2NSW/wRiv+948=", - "dev": true, - "requires": { - "homedir-polyfill": "^1.0.0", - "ini": "^1.3.4", - "is-windows": "^0.2.0", - "which": "^1.2.12" - }, - "dependencies": { - "is-windows": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz", - "integrity": "sha1-3hqm1j6indJIc3tp8f+LgALSEIw=", - "dev": true - } - } - }, - "globals": { - "version": "11.8.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.8.0.tgz", - "integrity": "sha512-io6LkyPVuzCHBSQV9fmOwxZkUk6nIaGmxheLDgmuFv89j0fm2aqDbIXKAGfzCMHqz3HLF2Zf8WSG6VqMh2qFmA==", - "dev": true - }, - "globby": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", - "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "dir-glob": "^2.0.0", - "glob": "^7.1.2", - "ignore": "^3.3.5", - "pify": "^3.0.0", - "slash": "^1.0.0" - } - }, - "got": { - "version": "6.7.1", - "resolved": "http://registry.npmjs.org/got/-/got-6.7.1.tgz", - "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", - "dev": true, - "requires": { - "create-error-class": "^3.0.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-redirect": "^1.0.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "lowercase-keys": "^1.0.0", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "unzip-response": "^2.0.1", - "url-parse-lax": "^1.0.0" - } - }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - }, - "growly": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", - "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", - "dev": true - }, - "gud": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz", - "integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==" - }, - "handlebars": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz", - "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==", - "dev": true, - "requires": { - "neo-async": "^2.6.0", - "optimist": "^0.6.1", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true - }, - "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "dev": true, - "requires": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" - }, - "dependencies": { - "ajv": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.8.1.tgz", - "integrity": "sha512-eqxCp82P+JfqL683wwsL73XmFs1eG6qjw+RD3YHx+Jll1r0jNd4dh8QG9NYAeNGA/hnZjeEDgtTskgJULbxpWQ==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - } - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", - "dev": true - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", - "dev": true - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "hoek": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.4.tgz", - "integrity": "sha512-Alr4ZQgoMlnere5FZJsIyfIjORBqZll5POhDsF4q64dPuJR6rNxXdDxtHSQq8OXRurhmx+PWYEE8bXRROY8h0w==", - "dev": true - }, - "hoist-non-react-statics": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz", - "integrity": "sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==", - "requires": { - "react-is": "^16.7.0" - } - }, - "homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", - "dev": true, - "requires": { - "parse-passwd": "^1.0.0" - } - }, - "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", - "dev": true - }, - "html-encoding-sniffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", - "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", - "dev": true, - "requires": { - "whatwg-encoding": "^1.0.1" - } - }, - "html-minifier": { - "version": "3.5.20", - "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.20.tgz", - "integrity": "sha512-ZmgNLaTp54+HFKkONyLFEfs5dd/ZOtlquKaTnqIWFmx3Av5zG6ZPcV2d0o9XM2fXOTxxIf6eDcwzFFotke/5zA==", - "dev": true, - "requires": { - "camel-case": "3.0.x", - "clean-css": "4.2.x", - "commander": "2.17.x", - "he": "1.1.x", - "param-case": "2.1.x", - "relateurl": "0.2.x", - "uglify-js": "3.4.x" - }, - "dependencies": { - "commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", - "dev": true - } - } - }, - "html-webpack-inline-source-plugin": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/html-webpack-inline-source-plugin/-/html-webpack-inline-source-plugin-0.0.10.tgz", - "integrity": "sha512-0ZNU57u7283vrXSF5a4VDnVOMWiSwypKIp1z/XfXWoVHLA1r3Xmyxx5+Lz+mnthz/UvxL1OAf41w5UIF68Jngw==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5", - "slash": "^1.0.0", - "source-map-url": "^0.4.0" - } - }, - "html-webpack-plugin": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", - "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=", - "dev": true, - "requires": { - "html-minifier": "^3.2.3", - "loader-utils": "^0.2.16", - "lodash": "^4.17.3", - "pretty-error": "^2.0.2", - "tapable": "^1.0.0", - "toposort": "^1.0.0", - "util.promisify": "1.0.0" - }, - "dependencies": { - "loader-utils": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", - "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", - "dev": true, - "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0", - "object-assign": "^4.0.1" - } - } - } - }, - "htmlparser2": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", - "integrity": "sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4=", - "dev": true, - "requires": { - "domelementtype": "1", - "domhandler": "2.1", - "domutils": "1.1", - "readable-stream": "1.0" - }, - "dependencies": { - "domutils": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.1.6.tgz", - "integrity": "sha1-vdw94Jm5ou+sxRxiPyj0FuzFdIU=", - "dev": true, - "requires": { - "domelementtype": "1" - } - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "readable-stream": { - "version": "1.0.34", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } - } - }, - "http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", - "dev": true - }, - "https-proxy-agent": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", - "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", - "dev": true, - "requires": { - "agent-base": "^4.1.0", - "debug": "^3.1.0" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "icss-replace-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", - "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", - "dev": true - }, - "icss-utils": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.0.tgz", - "integrity": "sha512-3DEun4VOeMvSczifM3F2cKQrDQ5Pj6WKhkOq6HD4QTnDUAq8MQRxy5TX6Sy1iY6WPBe4gQ3p5vTECjbIkglkkQ==", - "dev": true, - "requires": { - "postcss": "^7.0.14" - } - }, - "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "dev": true - }, - "iferr": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", - "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", - "dev": true - }, - "ignore": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", - "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", - "dev": true - }, - "import-fresh": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.0.0.tgz", - "integrity": "sha512-pOnA9tfM3Uwics+SaBLCNyZZZbK+4PTu0OPZtLlMIrv17EdBoC15S9Kn8ckJ9TZTyKb3ywNE5y1yeDxxGA7nTQ==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", - "dev": true - }, - "import-local": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", - "dev": true, - "requires": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - } - } - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "indent-string": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", - "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", - "dev": true, - "requires": { - "repeating": "^2.0.0" - } - }, - "indexes-of": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", - "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", - "dev": true - }, - "indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true - }, - "inquirer": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.2.tgz", - "integrity": "sha512-Z2rREiXA6cHRR9KBOarR3WuLlFzlIfAEIiB45ll5SSadMg7WqOh1MKEjjndfuH5ewXdixWCxqnVfGOQzPeiztA==", - "dev": true, - "requires": { - "ansi-escapes": "^3.2.0", - "chalk": "^2.4.2", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.3", - "figures": "^2.0.0", - "lodash": "^4.17.11", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^6.4.0", - "string-width": "^2.1.0", - "strip-ansi": "^5.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true - }, - "ansi-regex": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz", - "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==", - "dev": true - }, - "strip-ansi": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.0.0.tgz", - "integrity": "sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow==", - "dev": true, - "requires": { - "ansi-regex": "^4.0.0" - } - } - } - }, - "interpret": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", - "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", - "dev": true - }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "requires": { - "loose-envify": "^1.0.0" - } - }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true - }, - "ipaddr.js": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", - "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=", - "dev": true - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dev": true, - "requires": { - "binary-extensions": "^1.0.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-builtin-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", - "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", - "dev": true, - "requires": { - "builtin-modules": "^1.0.0" - } - }, - "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", - "dev": true - }, - "is-ci": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", - "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", - "dev": true, - "requires": { - "ci-info": "^1.5.0" - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", - "dev": true - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-finite": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", - "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-installed-globally": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", - "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", - "dev": true, - "requires": { - "global-dirs": "^0.1.0", - "is-path-inside": "^1.0.0" - } - }, - "is-npm": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", - "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" - }, - "is-obj": { - "version": "1.0.1", - "resolved": "http://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", - "dev": true - }, - "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", - "dev": true - }, - "is-path-in-cwd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", - "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", - "dev": true, - "requires": { - "is-path-inside": "^1.0.0" - } - }, - "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "dev": true, - "requires": { - "path-is-inside": "^1.0.1" - } - }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", - "dev": true - }, - "is-redirect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", - "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", - "dev": true - }, - "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "dev": true, - "requires": { - "has": "^1.0.1" - } - }, - "is-retry-allowed": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", - "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=", - "dev": true - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, - "is-symbol": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", - "dev": true, - "requires": { - "has-symbols": "^1.0.0" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isemail": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.2.0.tgz", - "integrity": "sha512-zKqkK+O+dGqevc93KNsbZ/TqTUFd46MwWjYOoMrjIMZ51eU7DtQG3Wmd9SQQT7i7RVnuTPEiYEWHU3MSbxC1Tg==", - "dev": true, - "requires": { - "punycode": "2.x.x" - } - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "isomorphic-fetch": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", - "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", - "requires": { - "node-fetch": "^1.0.1", - "whatwg-fetch": ">=0.10.0" - } - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, - "istanbul-lib-report": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", - "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "supports-color": "^6.1.0" - }, - "dependencies": { - "istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "dev": true - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "istanbul-lib-source-maps": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", - "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "rimraf": "^2.6.3", - "source-map": "^0.6.1" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "dev": true - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "istanbul-reports": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.4.tgz", - "integrity": "sha512-QCHGyZEK0bfi9GR215QSm+NJwFKEShbtc7tfbUdLAEzn3kKhLDDZqvljn8rPZM9v8CEOhzL1nlYoO4r1ryl67w==", - "dev": true, - "requires": { - "handlebars": "^4.1.2" - } - }, - "jest": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-24.8.0.tgz", - "integrity": "sha512-o0HM90RKFRNWmAWvlyV8i5jGZ97pFwkeVoGvPW1EtLTgJc2+jcuqcbbqcSZLE/3f2S5pt0y2ZBETuhpWNl1Reg==", - "dev": true, - "requires": { - "import-local": "^2.0.0", - "jest-cli": "^24.8.0" - }, - "dependencies": { - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "requires": { - "ci-info": "^2.0.0" - } - }, - "jest-cli": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.8.0.tgz", - "integrity": "sha512-+p6J00jSMPQ116ZLlHJJvdf8wbjNbZdeSX9ptfHX06/MSNaXmKihQzx5vQcw0q2G6JsdVkUIdWbOWtSnaYs3yA==", - "dev": true, - "requires": { - "@jest/core": "^24.8.0", - "@jest/test-result": "^24.8.0", - "@jest/types": "^24.8.0", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "import-local": "^2.0.0", - "is-ci": "^2.0.0", - "jest-config": "^24.8.0", - "jest-util": "^24.8.0", - "jest-validate": "^24.8.0", - "prompts": "^2.0.1", - "realpath-native": "^1.1.0", - "yargs": "^12.0.2" - } - } - } - }, - "jest-changed-files": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.8.0.tgz", - "integrity": "sha512-qgANC1Yrivsq+UrLXsvJefBKVoCsKB0Hv+mBb6NMjjZ90wwxCDmU3hsCXBya30cH+LnPYjwgcU65i6yJ5Nfuug==", - "dev": true, - "requires": { - "@jest/types": "^24.8.0", - "execa": "^1.0.0", - "throat": "^4.0.0" - }, - "dependencies": { - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } - } - }, - "jest-config": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.8.0.tgz", - "integrity": "sha512-Czl3Nn2uEzVGsOeaewGWoDPD8GStxCpAe0zOYs2x2l0fZAgPbCr3uwUkgNKV3LwE13VXythM946cd5rdGkkBZw==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^24.8.0", - "@jest/types": "^24.8.0", - "babel-jest": "^24.8.0", - "chalk": "^2.0.1", - "glob": "^7.1.1", - "jest-environment-jsdom": "^24.8.0", - "jest-environment-node": "^24.8.0", - "jest-get-type": "^24.8.0", - "jest-jasmine2": "^24.8.0", - "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.8.0", - "jest-util": "^24.8.0", - "jest-validate": "^24.8.0", - "micromatch": "^3.1.10", - "pretty-format": "^24.8.0", - "realpath-native": "^1.1.0" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "babel-jest": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.8.0.tgz", - "integrity": "sha512-+5/kaZt4I9efoXzPlZASyK/lN9qdRKmmUav9smVc0ruPQD7IsfucQ87gpOE8mn2jbDuS6M/YOW6n3v9ZoIfgnw==", - "dev": true, - "requires": { - "@jest/transform": "^24.8.0", - "@jest/types": "^24.8.0", - "@types/babel__core": "^7.1.0", - "babel-plugin-istanbul": "^5.1.0", - "babel-preset-jest": "^24.6.0", - "chalk": "^2.4.2", - "slash": "^2.0.0" - } - }, - "babel-plugin-jest-hoist": { - "version": "24.6.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.6.0.tgz", - "integrity": "sha512-3pKNH6hMt9SbOv0F3WVmy5CWQ4uogS3k0GY5XLyQHJ9EGpAT9XWkFd2ZiXXtkwFHdAHa5j7w7kfxSP5lAIwu7w==", - "dev": true, - "requires": { - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-preset-jest": { - "version": "24.6.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.6.0.tgz", - "integrity": "sha512-pdZqLEdmy1ZK5kyRUfvBb2IfTPb2BUvIJczlPspS8fWmBQslNNDBqVfh7BW5leOVJMDZKzjD8XEyABTk6gQ5yw==", - "dev": true, - "requires": { - "@babel/plugin-syntax-object-rest-spread": "^7.0.0", - "babel-plugin-jest-hoist": "^24.6.0" - } - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - } - } - }, - "jest-dev-server": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/jest-dev-server/-/jest-dev-server-4.1.1.tgz", - "integrity": "sha512-VB/myFWW1q271FUWWbuTy/tWFTw1bQ8Gs5U/5I0h6p8vNJSs+nBivOJQNiOrc745ijh1ND0WMs1rDSmCCnh5Cg==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "cwd": "^0.10.0", - "find-process": "^1.4.1", - "prompts": "^2.0.4", - "spawnd": "^4.0.0", - "tree-kill": "^1.2.1", - "wait-on": "^3.2.0" - } - }, - "jest-diff": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.8.0.tgz", - "integrity": "sha512-wxetCEl49zUpJ/bvUmIFjd/o52J+yWcoc5ZyPq4/W1LUKGEhRYDIbP1KcF6t+PvqNrGAFk4/JhtxDq/Nnzs66g==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "diff-sequences": "^24.3.0", - "jest-get-type": "^24.8.0", - "pretty-format": "^24.8.0" - } - }, - "jest-docblock": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.3.0.tgz", - "integrity": "sha512-nlANmF9Yq1dufhFlKG9rasfQlrY7wINJbo3q01tu56Jv5eBU5jirylhF2O5ZBnLxzOVBGRDz/9NAwNyBtG4Nyg==", - "dev": true, - "requires": { - "detect-newline": "^2.1.0" - } - }, - "jest-each": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.8.0.tgz", - "integrity": "sha512-NrwK9gaL5+XgrgoCsd9svsoWdVkK4gnvyhcpzd6m487tXHqIdYeykgq3MKI1u4I+5Zf0tofr70at9dWJDeb+BA==", - "dev": true, - "requires": { - "@jest/types": "^24.8.0", - "chalk": "^2.0.1", - "jest-get-type": "^24.8.0", - "jest-util": "^24.8.0", - "pretty-format": "^24.8.0" - } - }, - "jest-environment-jsdom": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.8.0.tgz", - "integrity": "sha512-qbvgLmR7PpwjoFjM/sbuqHJt/NCkviuq9vus9NBn/76hhSidO+Z6Bn9tU8friecegbJL8gzZQEMZBQlFWDCwAQ==", - "dev": true, - "requires": { - "@jest/environment": "^24.8.0", - "@jest/fake-timers": "^24.8.0", - "@jest/types": "^24.8.0", - "jest-mock": "^24.8.0", - "jest-util": "^24.8.0", - "jsdom": "^11.5.1" - } - }, - "jest-environment-node": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.8.0.tgz", - "integrity": "sha512-vIGUEScd1cdDgR6sqn2M08sJTRLQp6Dk/eIkCeO4PFHxZMOgy+uYLPMC4ix3PEfM5Au/x3uQ/5Tl0DpXXZsJ/Q==", - "dev": true, - "requires": { - "@jest/environment": "^24.8.0", - "@jest/fake-timers": "^24.8.0", - "@jest/types": "^24.8.0", - "jest-mock": "^24.8.0", - "jest-util": "^24.8.0" - } - }, - "jest-environment-puppeteer": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/jest-environment-puppeteer/-/jest-environment-puppeteer-4.1.1.tgz", - "integrity": "sha512-H+Wqxq4w9mFb0sBRGcbFIU02T+0YZbf/HKfQhwHvbam4UQqq7/n3kRkF0iYYlf76Jmys/H8rThtdh+HHxFgA5Q==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "cwd": "^0.10.0", - "jest-dev-server": "^4.1.1", - "merge-deep": "^3.0.2" - } - }, - "jest-get-type": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.8.0.tgz", - "integrity": "sha512-RR4fo8jEmMD9zSz2nLbs2j0zvPpk/KCEz3a62jJWbd2ayNo0cb+KFRxPHVhE4ZmgGJEQp0fosmNz84IfqM8cMQ==", - "dev": true - }, - "jest-haste-map": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.8.0.tgz", - "integrity": "sha512-ZBPRGHdPt1rHajWelXdqygIDpJx8u3xOoLyUBWRW28r3tagrgoepPrzAozW7kW9HrQfhvmiv1tncsxqHJO1onQ==", - "dev": true, - "requires": { - "@jest/types": "^24.8.0", - "anymatch": "^2.0.0", - "fb-watchman": "^2.0.0", - "fsevents": "^1.2.7", - "graceful-fs": "^4.1.15", - "invariant": "^2.2.4", - "jest-serializer": "^24.4.0", - "jest-util": "^24.8.0", - "jest-worker": "^24.6.0", - "micromatch": "^3.1.10", - "sane": "^4.0.3", - "walker": "^1.0.7" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fsevents": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", - "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", - "dev": true, - "optional": true, - "requires": { - "nan": "^2.12.1", - "node-pre-gyp": "^0.12.0" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "debug": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ms": "^2.1.1" - } - }, - "deep-extend": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "fs-minipass": { - "version": "1.2.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.24", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "ini": { - "version": "1.3.5", - "bundled": true, - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true, - "optional": true - }, - "minipass": { - "version": "2.3.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "needle": { - "version": "2.3.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "debug": "^4.1.0", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.12.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "optional": true - }, - "npm-packlist": { - "version": "1.4.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "rimraf": { - "version": "2.6.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "sax": { - "version": "1.2.4", - "bundled": true, - "dev": true, - "optional": true - }, - "semver": { - "version": "5.7.0", - "bundled": true, - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "tar": { - "version": "4.4.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.3.4", - "minizlib": "^1.1.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.2" - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "wide-align": { - "version": "1.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "yallist": { - "version": "3.0.3", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "nan": { - "version": "2.13.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz", - "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==", - "dev": true, - "optional": true - } - } - }, - "jest-jasmine2": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.8.0.tgz", - "integrity": "sha512-cEky88npEE5LKd5jPpTdDCLvKkdyklnaRycBXL6GNmpxe41F0WN44+i7lpQKa/hcbXaQ+rc9RMaM4dsebrYong==", - "dev": true, - "requires": { - "@babel/traverse": "^7.1.0", - "@jest/environment": "^24.8.0", - "@jest/test-result": "^24.8.0", - "@jest/types": "^24.8.0", - "chalk": "^2.0.1", - "co": "^4.6.0", - "expect": "^24.8.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^24.8.0", - "jest-matcher-utils": "^24.8.0", - "jest-message-util": "^24.8.0", - "jest-runtime": "^24.8.0", - "jest-snapshot": "^24.8.0", - "jest-util": "^24.8.0", - "pretty-format": "^24.8.0", - "throat": "^4.0.0" - } - }, - "jest-leak-detector": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.8.0.tgz", - "integrity": "sha512-cG0yRSK8A831LN8lIHxI3AblB40uhv0z+SsQdW3GoMMVcK+sJwrIIyax5tu3eHHNJ8Fu6IMDpnLda2jhn2pD/g==", - "dev": true, - "requires": { - "pretty-format": "^24.8.0" - } - }, - "jest-matcher-utils": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.8.0.tgz", - "integrity": "sha512-lex1yASY51FvUuHgm0GOVj7DCYEouWSlIYmCW7APSqB9v8mXmKSn5+sWVF0MhuASG0bnYY106/49JU1FZNl5hw==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "jest-diff": "^24.8.0", - "jest-get-type": "^24.8.0", - "pretty-format": "^24.8.0" - } - }, - "jest-message-util": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.8.0.tgz", - "integrity": "sha512-p2k71rf/b6ns8btdB0uVdljWo9h0ovpnEe05ZKWceQGfXYr4KkzgKo3PBi8wdnd9OtNh46VpNIJynUn/3MKm1g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.8.0", - "@jest/types": "^24.8.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", - "stack-utils": "^1.0.1" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - } - } - }, - "jest-mock": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.8.0.tgz", - "integrity": "sha512-6kWugwjGjJw+ZkK4mDa0Df3sDlUTsV47MSrT0nGQ0RBWJbpODDQ8MHDVtGtUYBne3IwZUhtB7elxHspU79WH3A==", - "dev": true, - "requires": { - "@jest/types": "^24.8.0" - } - }, - "jest-pnp-resolver": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz", - "integrity": "sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ==", - "dev": true - }, - "jest-puppeteer": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/jest-puppeteer/-/jest-puppeteer-4.1.1.tgz", - "integrity": "sha512-nMZ8oUrmzp67UfPKXKmoEU1wLsZfPAhlOlLYHZSCb78eIkHmeP8tqSibhAMxH8BIQUHTsnAfel5z5AsGv2SzGQ==", - "dev": true, - "requires": { - "expect-puppeteer": "^4.1.1", - "jest-environment-puppeteer": "^4.1.1" - } - }, - "jest-regex-util": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.3.0.tgz", - "integrity": "sha512-tXQR1NEOyGlfylyEjg1ImtScwMq8Oh3iJbGTjN7p0J23EuVX1MA8rwU69K4sLbCmwzgCUbVkm0FkSF9TdzOhtg==", - "dev": true - }, - "jest-resolve": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.8.0.tgz", - "integrity": "sha512-+hjSzi1PoRvnuOICoYd5V/KpIQmkAsfjFO71458hQ2Whi/yf1GDeBOFj8Gxw4LrApHsVJvn5fmjcPdmoUHaVKw==", - "dev": true, - "requires": { - "@jest/types": "^24.8.0", - "browser-resolve": "^1.11.3", - "chalk": "^2.0.1", - "jest-pnp-resolver": "^1.2.1", - "realpath-native": "^1.1.0" - } - }, - "jest-resolve-dependencies": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.8.0.tgz", - "integrity": "sha512-hyK1qfIf/krV+fSNyhyJeq3elVMhK9Eijlwy+j5jqmZ9QsxwKBiP6qukQxaHtK8k6zql/KYWwCTQ+fDGTIJauw==", - "dev": true, - "requires": { - "@jest/types": "^24.8.0", - "jest-regex-util": "^24.3.0", - "jest-snapshot": "^24.8.0" - } - }, - "jest-runner": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.8.0.tgz", - "integrity": "sha512-utFqC5BaA3JmznbissSs95X1ZF+d+4WuOWwpM9+Ak356YtMhHE/GXUondZdcyAAOTBEsRGAgH/0TwLzfI9h7ow==", - "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/environment": "^24.8.0", - "@jest/test-result": "^24.8.0", - "@jest/types": "^24.8.0", - "chalk": "^2.4.2", - "exit": "^0.1.2", - "graceful-fs": "^4.1.15", - "jest-config": "^24.8.0", - "jest-docblock": "^24.3.0", - "jest-haste-map": "^24.8.0", - "jest-jasmine2": "^24.8.0", - "jest-leak-detector": "^24.8.0", - "jest-message-util": "^24.8.0", - "jest-resolve": "^24.8.0", - "jest-runtime": "^24.8.0", - "jest-util": "^24.8.0", - "jest-worker": "^24.6.0", - "source-map-support": "^0.5.6", - "throat": "^4.0.0" - }, - "dependencies": { - "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true - } - } - }, - "jest-runtime": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.8.0.tgz", - "integrity": "sha512-Mq0aIXhvO/3bX44ccT+czU1/57IgOMyy80oM0XR/nyD5zgBcesF84BPabZi39pJVA6UXw+fY2Q1N+4BiVUBWOA==", - "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/environment": "^24.8.0", - "@jest/source-map": "^24.3.0", - "@jest/transform": "^24.8.0", - "@jest/types": "^24.8.0", - "@types/yargs": "^12.0.2", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.1.15", - "jest-config": "^24.8.0", - "jest-haste-map": "^24.8.0", - "jest-message-util": "^24.8.0", - "jest-mock": "^24.8.0", - "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.8.0", - "jest-snapshot": "^24.8.0", - "jest-util": "^24.8.0", - "jest-validate": "^24.8.0", - "realpath-native": "^1.1.0", - "slash": "^2.0.0", - "strip-bom": "^3.0.0", - "yargs": "^12.0.2" - }, - "dependencies": { - "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } - } - }, - "jest-serializer": { - "version": "24.4.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.4.0.tgz", - "integrity": "sha512-k//0DtglVstc1fv+GY/VHDIjrtNjdYvYjMlbLUed4kxrE92sIUewOi5Hj3vrpB8CXfkJntRPDRjCrCvUhBdL8Q==", - "dev": true - }, - "jest-snapshot": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.8.0.tgz", - "integrity": "sha512-5ehtWoc8oU9/cAPe6fez6QofVJLBKyqkY2+TlKTOf0VllBB/mqUNdARdcjlZrs9F1Cv+/HKoCS/BknT0+tmfPg==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0", - "@jest/types": "^24.8.0", - "chalk": "^2.0.1", - "expect": "^24.8.0", - "jest-diff": "^24.8.0", - "jest-matcher-utils": "^24.8.0", - "jest-message-util": "^24.8.0", - "jest-resolve": "^24.8.0", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "pretty-format": "^24.8.0", - "semver": "^5.5.0" - } - }, - "jest-util": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.8.0.tgz", - "integrity": "sha512-DYZeE+XyAnbNt0BG1OQqKy/4GVLPtzwGx5tsnDrFcax36rVE3lTA5fbvgmbVPUZf9w77AJ8otqR4VBbfFJkUZA==", - "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/fake-timers": "^24.8.0", - "@jest/source-map": "^24.3.0", - "@jest/test-result": "^24.8.0", - "@jest/types": "^24.8.0", - "callsites": "^3.0.0", - "chalk": "^2.0.1", - "graceful-fs": "^4.1.15", - "is-ci": "^2.0.0", - "mkdirp": "^0.5.1", - "slash": "^2.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true - }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "requires": { - "ci-info": "^2.0.0" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "jest-validate": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.8.0.tgz", - "integrity": "sha512-+/N7VOEMW1Vzsrk3UWBDYTExTPwf68tavEPKDnJzrC6UlHtUDU/fuEdXqFoHzv9XnQ+zW6X3qMZhJ3YexfeLDA==", - "dev": true, - "requires": { - "@jest/types": "^24.8.0", - "camelcase": "^5.0.0", - "chalk": "^2.0.1", - "jest-get-type": "^24.8.0", - "leven": "^2.1.0", - "pretty-format": "^24.8.0" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - } - } - }, - "jest-watcher": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.8.0.tgz", - "integrity": "sha512-SBjwHt5NedQoVu54M5GEx7cl7IGEFFznvd/HNT8ier7cCAx/Qgu9ZMlaTQkvK22G1YOpcWBLQPFSImmxdn3DAw==", - "dev": true, - "requires": { - "@jest/test-result": "^24.8.0", - "@jest/types": "^24.8.0", - "@types/yargs": "^12.0.9", - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.1", - "jest-util": "^24.8.0", - "string-length": "^2.0.0" - } - }, - "jest-worker": { - "version": "24.6.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.6.0.tgz", - "integrity": "sha512-jDwgW5W9qGNvpI1tNnvajh0a5IE/PuGLFmHk6aR/BZFz8tSgGw17GsDPXAJ6p91IvYDjOw8GpFbvvZGAK+DPQQ==", - "dev": true, - "requires": { - "merge-stream": "^1.0.1", - "supports-color": "^6.1.0" - }, - "dependencies": { - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "joi": { - "version": "13.7.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-13.7.0.tgz", - "integrity": "sha512-xuY5VkHfeOYK3Hdi91ulocfuFopwgbSORmIwzcwHKESQhC7w1kD5jaVSPnqDxS2I8t3RZ9omCKAxNwXN5zG1/Q==", - "dev": true, - "requires": { - "hoek": "5.x.x", - "isemail": "3.x.x", - "topo": "3.x.x" - } - }, - "js-levenshtein": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", - "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", - "dev": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, - "jsdom": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", - "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", - "dev": true, - "requires": { - "abab": "^2.0.0", - "acorn": "^5.5.3", - "acorn-globals": "^4.1.0", - "array-equal": "^1.0.0", - "cssom": ">= 0.3.2 < 0.4.0", - "cssstyle": "^1.0.0", - "data-urls": "^1.0.0", - "domexception": "^1.0.1", - "escodegen": "^1.9.1", - "html-encoding-sniffer": "^1.0.2", - "left-pad": "^1.3.0", - "nwsapi": "^2.0.7", - "parse5": "4.0.0", - "pn": "^1.1.0", - "request": "^2.87.0", - "request-promise-native": "^1.0.5", - "sax": "^1.2.4", - "symbol-tree": "^3.2.2", - "tough-cookie": "^2.3.4", - "w3c-hr-time": "^1.0.1", - "webidl-conversions": "^4.0.2", - "whatwg-encoding": "^1.0.3", - "whatwg-mimetype": "^2.1.0", - "whatwg-url": "^6.4.1", - "ws": "^5.2.0", - "xml-name-validator": "^3.0.0" - } - }, - "jsesc": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.1.tgz", - "integrity": "sha1-5CGiqOINawgZ3yiQj3glJrlt0f4=", - "dev": true - }, - "json-loader": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz", - "integrity": "sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==", - "dev": true - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", - "dev": true - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "jsx-ast-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz", - "integrity": "sha1-6AGxs5mF4g//yHtA43SAgOLcrH8=", - "dev": true, - "requires": { - "array-includes": "^3.0.3" - } - }, - "key-pressed": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/key-pressed/-/key-pressed-0.0.1.tgz", - "integrity": "sha1-1oL7THwQQpKaW8iGNnKMpxIg3rs=", - "requires": { - "vkey": "0.0.3" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - }, - "kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true - }, - "latest-version": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", - "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", - "dev": true, - "requires": { - "package-json": "^4.0.0" - } - }, - "lazy-ass": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", - "integrity": "sha1-eZllXoZGwX8In90YfRUNMyTVRRM=", - "dev": true - }, - "lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", - "dev": true - }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.0.0" - } - }, - "left-pad": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", - "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", - "dev": true - }, - "leven": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", - "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=", - "dev": true - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, - "loader-fs-cache": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.1.tgz", - "integrity": "sha1-VuC/CL2XCLJqdltoUJhAyN7J/bw=", - "dev": true, - "requires": { - "find-cache-dir": "^0.1.1", - "mkdirp": "0.5.1" - }, - "dependencies": { - "find-cache-dir": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz", - "integrity": "sha1-yN765XyKUqinhPnjHFfHQumToLk=", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "mkdirp": "^0.5.1", - "pkg-dir": "^1.0.0" - } - }, - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "^2.0.0" - } - }, - "pkg-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", - "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", - "dev": true, - "requires": { - "find-up": "^1.0.0" - } - } - } - }, - "loader-runner": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", - "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", - "dev": true - }, - "loader-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", - "dev": true, - "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" - }, - "lodash._reinterpolate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", - "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", - "dev": true - }, - "lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", - "dev": true - }, - "lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=", - "dev": true - }, - "lodash.kebabcase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", - "integrity": "sha1-hImxyw0p/4gZXM7KRI/21swpXDY=", - "dev": true - }, - "lodash.snakecase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", - "integrity": "sha1-OdcUo1NXFHg3rv1ktdy7Fr7Nj40=", - "dev": true - }, - "lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", - "dev": true - }, - "lodash.template": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.4.0.tgz", - "integrity": "sha1-5zoDhcg1VZF0bgILmWecaQ5o+6A=", - "dev": true, - "requires": { - "lodash._reinterpolate": "~3.0.0", - "lodash.templatesettings": "^4.0.0" - } - }, - "lodash.templatesettings": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz", - "integrity": "sha1-K01OlbpEDZFf8IvImeRVNmZxMxY=", - "dev": true, - "requires": { - "lodash._reinterpolate": "~3.0.0" - } - }, - "lodash.upperfirst": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", - "integrity": "sha1-E2Xt9DFIBIHvDRxolXpe2Z1J984=", - "dev": true - }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "loud-rejection": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", - "dev": true, - "requires": { - "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.0" - } - }, - "lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", - "dev": true - }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true - }, - "lru-cache": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", - "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "make-dir": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "makeerror": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", - "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", - "dev": true, - "requires": { - "tmpl": "1.0.x" - } - }, - "mamacro": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", - "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==", - "dev": true - }, - "map-age-cleaner": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.2.tgz", - "integrity": "sha512-UN1dNocxQq44IhJyMI4TU8phc2m9BddacHRPRjKGLYaF0jqd3xLz0jS0skpAU9WgYyoR4gHtUpzytNBS385FWQ==", - "dev": true, - "requires": { - "p-defer": "^1.0.0" - } - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true - }, - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true - }, - "map-stream": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", - "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", - "dev": true - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "requires": { - "object-visit": "^1.0.0" - } - }, - "md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "dev": true - }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - }, - "dependencies": { - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "p-is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", - "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", - "dev": true - } - } - }, - "memoize-one": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.0.4.tgz", - "integrity": "sha512-P0z5IeAH6qHHGkJIXWw0xC2HNEgkx/9uWWBQw64FJj3/ol14VYdfVGWWr0fXfjhhv3TKVIqUq65os6O4GUNksA==" - }, - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "dev": true, - "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - }, - "meow": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", - "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", - "dev": true, - "requires": { - "camelcase-keys": "^2.0.0", - "decamelize": "^1.1.2", - "loud-rejection": "^1.0.0", - "map-obj": "^1.0.1", - "minimist": "^1.1.3", - "normalize-package-data": "^2.3.4", - "object-assign": "^4.0.1", - "read-pkg-up": "^1.0.1", - "redent": "^1.0.0", - "trim-newlines": "^1.0.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "merge-deep": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/merge-deep/-/merge-deep-3.0.2.tgz", - "integrity": "sha512-T7qC8kg4Zoti1cFd8Cr0M+qaZfOwjlPDEdZIIPPB2JZctjaPM4fX+i7HOId69tAti2fvO6X5ldfYUONDODsrkA==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "clone-deep": "^0.2.4", - "kind-of": "^3.0.2" - } - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", - "dev": true - }, - "merge-stream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", - "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", - "dev": true, - "requires": { - "readable-stream": "^2.0.1" - } - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - } - } - }, - "miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "dev": true, - "requires": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" - } - }, - "mime": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", - "dev": true - }, - "mime-db": { - "version": "1.36.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.36.0.tgz", - "integrity": "sha512-L+xvyD9MkoYMXb1jAmzI/lWYAxAMCPvIBSWur0PZ5nOf5euahRLVqH//FKW9mWp2lkqUgYiXPgkzfMUFi4zVDw==", - "dev": true - }, - "mime-types": { - "version": "2.1.20", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.20.tgz", - "integrity": "sha512-HrkrPaP9vGuWbLK1B1FfgAkbqNjIuy4eHlIYnFi7kamZyLLrGlo2mpcx0bBmNpKqBtYtAfGbodDddIgddSJC2A==", - "dev": true, - "requires": { - "mime-db": "~1.36.0" - } - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true - }, - "mini-css-extract-plugin": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.6.0.tgz", - "integrity": "sha512-79q5P7YGI6rdnVyIAV4NXpBQJFWdkzJxCim3Kog4078fM0piAaFlwocqbejdWtLW1cEzCexPrh6EdyFsPgVdAw==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0", - "normalize-url": "^2.0.1", - "schema-utils": "^1.0.0", - "webpack-sources": "^1.1.0" - } - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, - "mississippi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", - "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", - "dev": true, - "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - }, - "dependencies": { - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } - } - }, - "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", - "dev": true, - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "mixin-object": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz", - "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=", - "dev": true, - "requires": { - "for-in": "^0.1.3", - "is-extendable": "^0.1.1" - }, - "dependencies": { - "for-in": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz", - "integrity": "sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=", - "dev": true - } - } - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "mouse-position": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mouse-position/-/mouse-position-2.1.0.tgz", - "integrity": "sha512-Ra8/RFn9e9/D2v9syhF5bLo4yiWvqZzcC/5bHOvkixDAkvO3WhlWN2xQRHSirMoYk2bagIae2upXXwxTxzI3BQ==", - "requires": { - "events": "^1.0.1" - } - }, - "mouse-pressed": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mouse-pressed/-/mouse-pressed-1.0.0.tgz", - "integrity": "sha1-wB4UGA+NoSBQc5lZOeT5b+8ifjI=", - "requires": { - "events": "^1.0.1" - } - }, - "move-concurrently": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", - "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", - "dev": true, - "requires": { - "aproba": "^1.1.1", - "copy-concurrently": "^1.0.0", - "fs-write-stream-atomic": "^1.0.8", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.3" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", - "dev": true - }, - "nan": { - "version": "2.13.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz", - "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==", - "dev": true, - "optional": true - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - } - } - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "negotiator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", - "dev": true - }, - "neo-async": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz", - "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==", - "dev": true - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "no-case": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", - "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", - "dev": true, - "requires": { - "lower-case": "^1.1.1" - } - }, - "node-fetch": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", - "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", - "requires": { - "encoding": "^0.1.11", - "is-stream": "^1.0.1" - } - }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", - "dev": true - }, - "node-libs-browser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.0.tgz", - "integrity": "sha512-5MQunG/oyOaBdttrL40dA7bUfPORLRWMUJLQtMg7nluxUvk5XwnLdL9twQHFAjRx/y7mIMkLKT9++qPbbk6BZA==", - "dev": true, - "requires": { - "assert": "^1.1.1", - "browserify-zlib": "^0.2.0", - "buffer": "^4.3.0", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", - "crypto-browserify": "^3.11.0", - "domain-browser": "^1.1.1", - "events": "^3.0.0", - "https-browserify": "^1.0.0", - "os-browserify": "^0.3.0", - "path-browserify": "0.0.0", - "process": "^0.11.10", - "punycode": "^1.2.4", - "querystring-es3": "^0.2.0", - "readable-stream": "^2.3.3", - "stream-browserify": "^2.0.1", - "stream-http": "^2.7.2", - "string_decoder": "^1.0.0", - "timers-browserify": "^2.0.4", - "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.11.0", - "vm-browserify": "0.0.4" - }, - "dependencies": { - "events": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", - "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==", - "dev": true - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - } - } - }, - "node-modules-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", - "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", - "dev": true - }, - "node-notifier": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.0.tgz", - "integrity": "sha512-SUDEb+o71XR5lXSTyivXd9J7fCloE3SyP4lSgt3lU2oSANiox+SxlNRGPjDKrwU1YN3ix2KN/VGGCg0t01rttQ==", - "dev": true, - "requires": { - "growly": "^1.3.0", - "is-wsl": "^1.1.0", - "semver": "^5.5.0", - "shellwords": "^0.1.1", - "which": "^1.3.0" - } - }, - "node-releases": { - "version": "1.1.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.19.tgz", - "integrity": "sha512-SH/B4WwovHbulIALsQllAVwqZZD1kPmKCqrhGfR29dXjLAVZMHvBjD3S6nL9D/J9QkmZ1R92/0wCMDKXUUvyyA==", - "dev": true, - "requires": { - "semver": "^5.3.0" - } - }, - "normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "is-builtin-module": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - }, - "normalize-url": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz", - "integrity": "sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==", - "dev": true, - "requires": { - "prepend-http": "^2.0.0", - "query-string": "^5.0.1", - "sort-keys": "^2.0.0" - }, - "dependencies": { - "prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", - "dev": true - }, - "query-string": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", - "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", - "dev": true, - "requires": { - "decode-uri-component": "^0.2.0", - "object-assign": "^4.1.0", - "strict-uri-encode": "^1.0.0" - } - }, - "strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", - "dev": true - } - } - }, - "normalize.css": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.0.tgz", - "integrity": "sha512-iXcbM3NWr0XkNyfiSBsoPezi+0V92P9nj84yVV1/UZxRUrGczgX/X91KMAGM0omWLY2+2Q1gKD/XRn4gQRDB2A==" - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.0" - } - }, - "nth-check": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz", - "integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=", - "dev": true, - "requires": { - "boolbase": "~1.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, - "nwsapi": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.1.4.tgz", - "integrity": "sha512-iGfd9Y6SFdTNldEy2L0GUhcarIutFmk+MPWIn9dmj8NMIup03G08uUF2KGbbmv/Ux4RT0VZJoP/sVbWA6d/VIw==", - "dev": true - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "object-hash": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.1.tgz", - "integrity": "sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==", - "dev": true - }, - "object-keys": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", - "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==", - "dev": true - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "requires": { - "isobject": "^3.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, - "object.entries": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.0.4.tgz", - "integrity": "sha1-G/mk3SKI9bM/Opk9JXZh8F0WGl8=", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.6.1", - "function-bind": "^1.1.0", - "has": "^1.0.1" - } - }, - "object.fromentries": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.0.tgz", - "integrity": "sha512-9iLiI6H083uiqUuvzyY6qrlmc/Gz8hLQFOcb/Ri/0xXFkSNS3ctV+CbE6yM2+AnkYfOB3dGjdzC0wrMLIhQICA==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.11.0", - "function-bind": "^1.1.1", - "has": "^1.0.1" - } - }, - "object.getownpropertydescriptors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", - "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "requires": { - "isobject": "^3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dev": true, - "requires": { - "ee-first": "1.1.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "dev": true, - "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" - }, - "dependencies": { - "wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", - "dev": true - } - } - }, - "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "wordwrap": "~1.0.0" - } - }, - "orbit-camera": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/orbit-camera/-/orbit-camera-1.0.0.tgz", - "integrity": "sha1-jaFhWrXB53eiv2W5XlzR9Rfx/DE=", - "requires": { - "gl-matrix": "^2.2.0" - }, - "dependencies": { - "gl-matrix": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-2.8.1.tgz", - "integrity": "sha512-0YCjVpE3pS5XWlN3J4X7AiAx65+nqAI54LndtVFnQZB6G/FVLkZH8y8V6R3cIoOQR4pUdfwQGd1iwyoXHJ4Qfw==" - } - } - }, - "os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", - "dev": true - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true - }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - }, - "dependencies": { - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", - "dev": true - }, - "p-each-series": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", - "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=", - "dev": true, - "requires": { - "p-reduce": "^1.0.0" - } - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-map": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", - "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", - "dev": true - }, - "p-reduce": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", - "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=", - "dev": true - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "package-json": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", - "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", - "dev": true, - "requires": { - "got": "^6.7.1", - "registry-auth-token": "^3.0.1", - "registry-url": "^3.0.3", - "semver": "^5.1.0" - } - }, - "pako": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", - "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==", - "dev": true - }, - "parallel-transform": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", - "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", - "dev": true, - "requires": { - "cyclist": "~0.2.2", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" - } - }, - "param-case": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", - "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", - "dev": true, - "requires": { - "no-case": "^2.2.0" - } - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-asn1": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz", - "integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==", - "dev": true, - "requires": { - "asn1.js": "^4.0.0", - "browserify-aes": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" - } - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "^1.2.0" - } - }, - "parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", - "dev": true - }, - "parse5": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", - "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", - "dev": true - }, - "parseurl": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", - "dev": true - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true - }, - "path-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", - "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", - "dev": true - }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", - "dev": true - }, - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, - "pause-stream": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", - "dev": true, - "requires": { - "through": "~2.3" - } - }, - "pbkdf2": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", - "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", - "dev": true, - "requires": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", - "dev": true - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, - "requires": { - "pinkie": "^2.0.0" - } - }, - "pirates": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", - "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", - "dev": true, - "requires": { - "node-modules-regexp": "^1.0.0" - } - }, - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "requires": { - "find-up": "^2.1.0" - } - }, - "pn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", - "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", - "dev": true - }, - "popper.js": { - "version": "1.14.4", - "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.14.4.tgz", - "integrity": "sha1-juwdj/AqWjoVLdQ0FKFce3n9abY=" - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-modules-extract-imports": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", - "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", - "dev": true, - "requires": { - "postcss": "^7.0.5" - } - }, - "postcss-modules-local-by-default": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-2.0.6.tgz", - "integrity": "sha512-oLUV5YNkeIBa0yQl7EYnxMgy4N6noxmiwZStaEJUSe2xPMcdNc8WmBQuQCx18H5psYbVxz8zoHk0RAAYZXP9gA==", - "dev": true, - "requires": { - "postcss": "^7.0.6", - "postcss-selector-parser": "^6.0.0", - "postcss-value-parser": "^3.3.1" - } - }, - "postcss-modules-scope": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.1.0.tgz", - "integrity": "sha512-91Rjps0JnmtUB0cujlc8KIKCsJXWjzuxGeT/+Q2i2HXKZ7nBUeF9YQTZZTNvHVoNYj1AthsjnGLtqDUE0Op79A==", - "dev": true, - "requires": { - "postcss": "^7.0.6", - "postcss-selector-parser": "^6.0.0" - } - }, - "postcss-modules-values": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-2.0.0.tgz", - "integrity": "sha512-Ki7JZa7ff1N3EIMlPnGTZfUMe69FFwiQPnVSXC9mnn3jozCRBYIxiZd44yJOV2AmabOo4qFf8s0dC/+lweG7+w==", - "dev": true, - "requires": { - "icss-replace-symbols": "^1.1.0", - "postcss": "^7.0.6" - } - }, - "postcss-selector-parser": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz", - "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==", - "dev": true, - "requires": { - "cssesc": "^3.0.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - }, - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", - "dev": true - }, - "pretty-bytes": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-4.0.2.tgz", - "integrity": "sha1-sr+C5zUNZcbDOqlaqlpPYyf2HNk=", - "dev": true - }, - "pretty-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", - "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", - "dev": true, - "requires": { - "renderkid": "^2.0.1", - "utila": "~0.4" - } - }, - "pretty-format": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.8.0.tgz", - "integrity": "sha512-P952T7dkrDEplsR+TuY7q3VXDae5Sr7zmQb12JU/NDQa/3CH7/QW0yvqLcGN6jL+zQFKaoJcPc+yJxMTGmosqw==", - "dev": true, - "requires": { - "@jest/types": "^24.8.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "react-is": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", - "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==", - "dev": true - } - } - }, - "private": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", - "dev": true - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "requires": { - "asap": "~2.0.3" - } - }, - "promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", - "dev": true - }, - "prompts": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.0.4.tgz", - "integrity": "sha512-HTzM3UWp/99A0gk51gAegwo1QRYA7xjcZufMNe33rCclFszUYAuHe1fIN/3ZmiHeGPkUsNaRyQm1hHOfM0PKxA==", - "dev": true, - "requires": { - "kleur": "^3.0.2", - "sisteransi": "^1.0.0" - } - }, - "prop-types": { - "version": "15.6.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", - "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==", - "requires": { - "loose-envify": "^1.3.1", - "object-assign": "^4.1.1" - } - }, - "proxy-addr": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", - "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", - "dev": true, - "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.8.0" - } - }, - "proxy-from-env": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", - "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=", - "dev": true - }, - "prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", - "dev": true - }, - "ps-tree": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", - "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", - "dev": true, - "requires": { - "event-stream": "=3.3.4" - } - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "psl": { - "version": "1.1.31", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", - "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==", - "dev": true - }, - "public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "dev": true, - "requires": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "puppeteer": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-1.16.0.tgz", - "integrity": "sha512-7hcmbUw+6INffSPBdnO8KSjJRg2bLRoI7EeZMf5MHdV5kpyYMeoMR5w8AIiZbKIhYGwrXlbgvO7gFTsXNHShuQ==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "extract-zip": "^1.6.6", - "https-proxy-agent": "^2.2.1", - "mime": "^2.0.3", - "progress": "^2.0.1", - "proxy-from-env": "^1.0.0", - "rimraf": "^2.6.1", - "ws": "^6.1.0" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "mime": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.2.tgz", - "integrity": "sha512-zJBfZDkwRu+j3Pdd2aHsR5GfH2jIWhmL1ZzBoc+X+3JEti2hbArWcyJ+1laC1D2/U/W1a/+Cegj0/OnEU2ybjg==", - "dev": true - }, - "ws": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", - "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0" - } - } - } - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true - }, - "query-string": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.5.0.tgz", - "integrity": "sha512-TYC4hDjZSvVxLMEucDMySkuAS9UIzSbAiYGyA9GWCjLKB8fQpviFbjd20fD7uejCDxZS+ftSdBKE6DS+xucJFg==", - "requires": { - "decode-uri-component": "^0.2.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - } - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true - }, - "querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", - "dev": true - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "dev": true, - "requires": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" - } - }, - "range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", - "dev": true - }, - "raw-body": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", - "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", - "dev": true, - "requires": { - "bytes": "3.0.0", - "http-errors": "1.6.3", - "iconv-lite": "0.4.23", - "unpipe": "1.0.0" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - } - } - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "react": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react/-/react-16.8.6.tgz", - "integrity": "sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw==", - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "scheduler": "^0.13.6" - } - }, - "react-autocomplete": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/react-autocomplete/-/react-autocomplete-1.8.1.tgz", - "integrity": "sha1-67vEAABqqRrVOLLRRye55+XQYxA=", - "requires": { - "dom-scroll-into-view": "1.0.1", - "prop-types": "^15.5.10" - } - }, - "react-dom": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.6.tgz", - "integrity": "sha512-1nL7PIq9LTL3fthPqwkvr2zY7phIPjYrT0jp4HjyEQrEROnw4dG41VVwi/wfoCneoleqrNX7iAD+pXebJZwrwA==", - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "scheduler": "^0.13.6" - } - }, - "react-fast-compare": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz", - "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==" - }, - "react-helmet": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-5.2.1.tgz", - "integrity": "sha512-CnwD822LU8NDBnjCpZ4ySh8L6HYyngViTZLfBBb3NjtrpN8m49clH8hidHouq20I51Y6TpCTISCBbqiY5GamwA==", - "requires": { - "object-assign": "^4.1.1", - "prop-types": "^15.5.4", - "react-fast-compare": "^2.0.2", - "react-side-effect": "^1.1.0" - } - }, - "react-icons": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-3.7.0.tgz", - "integrity": "sha512-7MyPwjIhuyW0D2N3s4DEd0hGPGFf0sK+IIRKhc1FvSpZNVmnUoGvHbmAwzGJU+3my+fvihVWgwU5SDtlAri56Q==", - "requires": { - "camelcase": "^5.0.0" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" - } - } - }, - "react-is": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", - "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==" - }, - "react-lifecycles-compat": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", - "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" - }, - "react-popper": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.0.2.tgz", - "integrity": "sha512-vjZ94ki8sfCAg45MMi4uqnUUWdzbnYkb95sR2+HgiMaAPzQcy4DfDKYtYUOhhE+sdtkufWcUHLv09DmH2Js57w==", - "requires": { - "babel-runtime": "6.x.x", - "create-react-context": "^0.2.1", - "popper.js": "^1.14.1", - "prop-types": "^15.6.1", - "typed-styles": "^0.0.5", - "warning": "^3.0.0" - } - }, - "react-redux": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.0.3.tgz", - "integrity": "sha512-vYZA7ftOYlDk3NetitsI7fLjryt/widNl1SLXYvFenIpm7vjb4ryK0EeFrgn62usg5fYkyIAWNUPKnwWPevKLg==", - "requires": { - "@babel/runtime": "^7.4.3", - "hoist-non-react-statics": "^3.3.0", - "invariant": "^2.2.4", - "loose-envify": "^1.4.0", - "prop-types": "^15.7.2", - "react-is": "^16.8.6" - }, - "dependencies": { - "prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.8.1" - } - } - } - }, - "react-side-effect": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-1.1.5.tgz", - "integrity": "sha512-Z2ZJE4p/jIfvUpiUMRydEVpQRf2f8GMHczT6qLcARmX7QRb28JDBTpnM2g/i5y/p7ZDEXYGHWg0RbhikE+hJRw==", - "requires": { - "exenv": "^1.2.1", - "shallowequal": "^1.0.1" - } - }, - "react-transition-group": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.5.0.tgz", - "integrity": "sha512-qYB3JBF+9Y4sE4/Mg/9O6WFpdoYjeeYqx0AFb64PTazVy8RPMiE3A47CG9QmM4WJ/mzDiZYslV+Uly6O1Erlgw==", - "requires": { - "dom-helpers": "^3.3.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2", - "react-lifecycles-compat": "^3.0.4" - } - }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "dev": true, - "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" - } - }, - "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", - "dev": true, - "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" - }, - "dependencies": { - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "^2.0.0" - } - } - } - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - } - }, - "realpath-native": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", - "integrity": "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==", - "dev": true, - "requires": { - "util.promisify": "^1.0.0" - } - }, - "redent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", - "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", - "dev": true, - "requires": { - "indent-string": "^2.1.0", - "strip-indent": "^1.0.1" - } - }, - "redux": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.1.tgz", - "integrity": "sha512-R7bAtSkk7nY6O/OYMVR9RiBI+XghjF9rlbl5806HJbQph0LJVHZrU5oaO4q70eUKiqMRqm4y07KLTlMZ2BlVmg==", - "requires": { - "loose-envify": "^1.4.0", - "symbol-observable": "^1.2.0" - } - }, - "redux-thunk": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz", - "integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==" - }, - "regenerate": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", - "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", - "dev": true - }, - "regenerate-unicode-properties": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz", - "integrity": "sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA==", - "dev": true, - "requires": { - "regenerate": "^1.4.0" - } - }, - "regenerator-runtime": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz", - "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==" - }, - "regenerator-transform": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.13.4.tgz", - "integrity": "sha512-T0QMBjK3J0MtxjPmdIMXm72Wvj2Abb0Bd4HADdfijwMdoIsyQZ6fWC7kDFhk2YinBBEMZDL7Y7wh0J1sGx3S4A==", - "dev": true, - "requires": { - "private": "^0.1.6" - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, - "regexp-tree": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.6.tgz", - "integrity": "sha512-LFrA98Dw/heXqDojz7qKFdygZmFoiVlvE1Zp7Cq2cvF+ZA+03Gmhy0k0PQlsC1jvHPiTUSs+pDHEuSWv6+6D7w==", - "dev": true - }, - "regexpp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", - "dev": true - }, - "regexpu-core": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.5.4.tgz", - "integrity": "sha512-BtizvGtFQKGPUcTy56o3nk1bGRp4SZOTYrDtGNlqCQufptV5IkkLN6Emw+yunAJjzf+C9FQFtvq7IoA3+oMYHQ==", - "dev": true, - "requires": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.0.2", - "regjsgen": "^0.5.0", - "regjsparser": "^0.6.0", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.1.0" - } - }, - "registry-auth-token": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", - "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", - "dev": true, - "requires": { - "rc": "^1.1.6", - "safe-buffer": "^5.0.1" - } - }, - "registry-url": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", - "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", - "dev": true, - "requires": { - "rc": "^1.0.1" - } - }, - "regjsgen": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.0.tgz", - "integrity": "sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA==", - "dev": true - }, - "regjsparser": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.0.tgz", - "integrity": "sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ==", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - } - } - }, - "regl": { - "version": "1.3.11", - "resolved": "https://registry.npmjs.org/regl/-/regl-1.3.11.tgz", - "integrity": "sha512-tmt6CRhRqbcsYDWNwv+iG7GGOXdgoOBC7lKzoPMgnzpt3WKBQ3c8i7AxgbvTRZzty29hrW92fAJeZkPFQehfWA==" - }, - "relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", - "dev": true - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "renderkid": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.2.tgz", - "integrity": "sha512-FsygIxevi1jSiPY9h7vZmBFUbAOcbYm9UwyiLNdVsLRs/5We9Ob5NMPbGYUTWiLq5L+ezlVdE0A8bbME5CWTpg==", - "dev": true, - "requires": { - "css-select": "^1.1.0", - "dom-converter": "~0.2", - "htmlparser2": "~3.3.0", - "strip-ansi": "^3.0.0", - "utila": "^0.4.0" - } - }, - "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true - }, - "repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "dev": true, - "requires": { - "is-finite": "^1.0.0" - } - }, - "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - }, - "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "dev": true, - "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - } - } - } - }, - "request-promise-core": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz", - "integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==", - "dev": true, - "requires": { - "lodash": "^4.17.11" - } - }, - "request-promise-native": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.7.tgz", - "integrity": "sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w==", - "dev": true, - "requires": { - "request-promise-core": "1.1.2", - "stealthy-require": "^1.1.1", - "tough-cookie": "^2.3.3" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true - }, - "resize-observer-polyfill": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.0.tgz", - "integrity": "sha512-M2AelyJDVR/oLnToJLtuDJRBBWUGUvvGigj1411hXhAdyFWqMaqHp7TixW3FpiLuVaikIcR1QL+zqoJoZlOgpg==" - }, - "resolve": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", - "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", - "dev": true, - "requires": { - "path-parse": "^1.0.5" - } - }, - "resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", - "dev": true, - "requires": { - "resolve-from": "^3.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", - "dev": true - } - } - }, - "resolve-dir": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-0.1.1.tgz", - "integrity": "sha1-shklmlYC+sXFxJatiUpujMQwJh4=", - "dev": true, - "requires": { - "expand-tilde": "^1.2.2", - "global-modules": "^0.2.3" - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "dev": true, - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true - }, - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "rsvp": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.4.tgz", - "integrity": "sha512-6FomvYPfs+Jy9TfXmBpBuMWNH94SgCsZmJKcanySzgNNP6LjWxBvyLTa9KaMfDDM5oxRfrKDB0r/qeRsLwnBfA==", - "dev": true - }, - "run-async": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", - "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", - "dev": true, - "requires": { - "is-promise": "^2.1.0" - } - }, - "run-queue": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", - "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", - "dev": true, - "requires": { - "aproba": "^1.1.1" - } - }, - "rw": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", - "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=" - }, - "rx": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz", - "integrity": "sha1-pfE/957zt0D+MKqAP7CfmIBdR4I=", - "dev": true - }, - "rxjs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", - "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "requires": { - "ret": "~0.1.10" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sane": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", - "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", - "dev": true, - "requires": { - "@cnakazawa/watch": "^1.0.3", - "anymatch": "^2.0.0", - "capture-exit": "^2.0.0", - "exec-sh": "^0.3.2", - "execa": "^1.0.0", - "fb-watchman": "^2.0.0", - "micromatch": "^3.1.4", - "minimist": "^1.1.1", - "walker": "~1.0.5" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } - } - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - }, - "scheduler": { - "version": "0.13.6", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.6.tgz", - "integrity": "sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==", - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - } - }, - "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - } - }, - "scroll-speed": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/scroll-speed/-/scroll-speed-1.0.0.tgz", - "integrity": "sha1-3P5AlRbnYXVZe2BgACtRznLubtc=", - "requires": { - "events": "^1.0.0", - "wheel": "0.0.0" - } - }, - "semver": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", - "dev": true - }, - "semver-diff": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", - "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", - "dev": true, - "requires": { - "semver": "^5.0.3" - } - }, - "send": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", - "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", - "dev": true, - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.6.2", - "mime": "1.4.1", - "ms": "2.0.0", - "on-finished": "~2.3.0", - "range-parser": "~1.2.0", - "statuses": "~1.4.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "serialize-javascript": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.5.0.tgz", - "integrity": "sha512-Ga8c8NjAAp46Br4+0oZ2WxJCwIzwP60Gq1YPgU+39PiTVxyed/iKE/zyZI6+UlVYH5Q4PaQdHhcegIFPZTUfoQ==", - "dev": true - }, - "serve-favicon": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.5.0.tgz", - "integrity": "sha1-k10kDN/g9YBTB/3+ln2IlCosvPA=", - "dev": true, - "requires": { - "etag": "~1.8.1", - "fresh": "0.5.2", - "ms": "2.1.1", - "parseurl": "~1.3.2", - "safe-buffer": "5.1.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", - "dev": true - } - } - }, - "serve-static": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", - "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", - "dev": true, - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.2", - "send": "0.16.2" - } - }, - "serviceworker-cache-polyfill": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serviceworker-cache-polyfill/-/serviceworker-cache-polyfill-4.0.0.tgz", - "integrity": "sha1-3hnuc77yGrPAdAo3sz22JGS6ves=", - "dev": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true - }, - "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "shallow-clone": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-0.1.2.tgz", - "integrity": "sha1-WQnodLp3EG1zrEFM/sH/yofZcGA=", - "dev": true, - "requires": { - "is-extendable": "^0.1.1", - "kind-of": "^2.0.1", - "lazy-cache": "^0.2.3", - "mixin-object": "^2.0.1" - }, - "dependencies": { - "kind-of": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-2.0.1.tgz", - "integrity": "sha1-AY7HpM5+OobLkUG+UZ0kyPqpgbU=", - "dev": true, - "requires": { - "is-buffer": "^1.0.2" - } - }, - "lazy-cache": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-0.2.7.tgz", - "integrity": "sha1-f+3fLctu23fRHvHRF6tf/fCrG2U=", - "dev": true - } - } - }, - "shallowequal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", - "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "shellwords": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", - "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", - "dev": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "sisteransi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.0.tgz", - "integrity": "sha512-N+z4pHB4AmUv0SjveWRd6q1Nj5w62m5jodv+GD8lvmbY/83T/rpbJGZOnK5T149OldDj4Db07BSv9xY4K6NTPQ==", - "dev": true - }, - "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", - "dev": true - }, - "slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" - } - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "requires": { - "kind-of": "^3.2.0" - } - }, - "sort-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz", - "integrity": "sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=", - "dev": true, - "requires": { - "is-plain-obj": "^1.0.0" - } - }, - "source-list-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", - "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "source-map-resolve": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", - "dev": true, - "requires": { - "atob": "^2.1.1", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "source-map-support": { - "version": "0.5.9", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz", - "integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true - }, - "spawnd": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/spawnd/-/spawnd-4.0.0.tgz", - "integrity": "sha512-ql3qhJnhAkvXpaqKBWOqou1rUTSQhFRaZkyOT+MTFB4xY3X+brgw6LTWV2wHuE9A6YPhrNe1cbg7S+jAYnbC0Q==", - "dev": true, - "requires": { - "exit": "^0.1.2", - "signal-exit": "^3.0.2", - "tree-kill": "^1.2.1", - "wait-port": "^0.2.2" - } - }, - "spdx-correct": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.2.tgz", - "integrity": "sha512-q9hedtzyXHr5S0A1vEPoK/7l8NpfkFYTq6iCY+Pno2ZbdZR6WexZFtqeVGkGxW3TEJMN914Z55EnAGMmenlIQQ==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.1.tgz", - "integrity": "sha512-TfOfPcYGBB5sDuPn3deByxPhmfegAhpDYKSOXZQN81Oyrrif8ZCodOLzK3AesELnCx03kikhyDwh0pfvvQvF8w==", - "dev": true - }, - "split": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", - "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", - "dev": true, - "requires": { - "through": "2" - } - }, - "split-on-first": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", - "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==" - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dev": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1" - } - }, - "stack-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", - "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==", - "dev": true - }, - "start-server-and-test": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-1.9.0.tgz", - "integrity": "sha512-aXOP5zL1CZnBX/N7ssnqQvYzoXMaG1H4rcGSZjqdi6Jth61xYUe+S/DQlE8PKFLoXnafceA61kdrv8xjG6mqyA==", - "dev": true, - "requires": { - "bluebird": "3.5.4", - "check-more-types": "2.24.0", - "debug": "4.1.1", - "execa": "0.11.0", - "lazy-ass": "1.6.0", - "ps-tree": "1.2.0", - "wait-on": "3.2.0" - }, - "dependencies": { - "bluebird": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.4.tgz", - "integrity": "sha512-FG+nFEZChJrbQ9tIccIfZJBz3J7mLrAhxakAbnrJWn8d7aKOC+LWifa0G+p4ZqKp4y13T7juYvdhq9NzKdsrjw==", - "dev": true - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "execa": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.11.0.tgz", - "integrity": "sha512-k5AR22vCt1DcfeiRixW46U5tMLtBg44ssdJM9PiXw3D8Bn5qyxFCSnKY/eR22y+ctFDGPqafpaXg2G4Emyua4A==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } - } - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", - "dev": true - }, - "stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", - "dev": true - }, - "stream-browserify": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", - "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", - "dev": true, - "requires": { - "inherits": "~2.0.1", - "readable-stream": "^2.0.2" - } - }, - "stream-combiner": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", - "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", - "dev": true, - "requires": { - "duplexer": "~0.1.1" - } - }, - "stream-each": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", - "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "stream-shift": "^1.0.0" - } - }, - "stream-http": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", - "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", - "dev": true, - "requires": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.3.6", - "to-arraybuffer": "^1.0.0", - "xtend": "^4.0.0" - } - }, - "stream-shift": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", - "dev": true - }, - "strict-uri-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", - "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=" - }, - "string-length": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz", - "integrity": "sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=", - "dev": true, - "requires": { - "astral-regex": "^1.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "requires": { - "is-utf8": "^0.2.0" - } - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, - "strip-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", - "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", - "dev": true, - "requires": { - "get-stdin": "^4.0.1" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - }, - "style-loader": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.23.1.tgz", - "integrity": "sha512-XK+uv9kWwhZMZ1y7mysB+zoihsEj4wneFWAS5qoiLwzW0WzSqMrrsIy+a3zkQJq0ipFtBpX5W3MqyRIBF/WFGg==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0", - "schema-utils": "^1.0.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "sw-precache": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/sw-precache/-/sw-precache-5.2.1.tgz", - "integrity": "sha512-8FAy+BP/FXE+ILfiVTt+GQJ6UEf4CVHD9OfhzH0JX+3zoy2uFk7Vn9EfXASOtVmmIVbL3jE/W8Z66VgPSZcMhw==", - "dev": true, - "requires": { - "dom-urls": "^1.1.0", - "es6-promise": "^4.0.5", - "glob": "^7.1.1", - "lodash.defaults": "^4.2.0", - "lodash.template": "^4.4.0", - "meow": "^3.7.0", - "mkdirp": "^0.5.1", - "pretty-bytes": "^4.0.2", - "sw-toolbox": "^3.4.0", - "update-notifier": "^2.3.0" - } - }, - "sw-precache-webpack-plugin": { - "version": "0.11.5", - "resolved": "http://registry.npmjs.org/sw-precache-webpack-plugin/-/sw-precache-webpack-plugin-0.11.5.tgz", - "integrity": "sha512-K6E52DbYyzGNXGyv2LhI2Duomr3t/2FFMmnGdHZ1Ruk3ulFHDMASJtg3WpA3CXlWODZx189tTaOIO5mWkSKyVg==", - "dev": true, - "requires": { - "del": "^3.0.0", - "sw-precache": "^5.2.1", - "uglify-es": "^3.3.9" - }, - "dependencies": { - "commander": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", - "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", - "dev": true - }, - "del": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", - "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", - "dev": true, - "requires": { - "globby": "^6.1.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "p-map": "^1.1.1", - "pify": "^3.0.0", - "rimraf": "^2.2.8" - } - }, - "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "uglify-es": { - "version": "3.3.9", - "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", - "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", - "dev": true, - "requires": { - "commander": "~2.13.0", - "source-map": "~0.6.1" - } - } - } - }, - "sw-toolbox": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/sw-toolbox/-/sw-toolbox-3.6.0.tgz", - "integrity": "sha1-Jt8dHHA0hljk3qKIQxkUm3sxg7U=", - "dev": true, - "requires": { - "path-to-regexp": "^1.0.1", - "serviceworker-cache-polyfill": "^4.0.0" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "path-to-regexp": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", - "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", - "dev": true, - "requires": { - "isarray": "0.0.1" - } - } - } - }, - "symbol-observable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" - }, - "symbol-tree": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", - "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", - "dev": true - }, - "table": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/table/-/table-5.3.3.tgz", - "integrity": "sha512-3wUNCgdWX6PNpOe3amTTPWPuF6VGvgzjKCaO1snFj0z7Y3mUPWf5+zDtxUVGispJkDECPmR29wbzh6bVMOHbcw==", - "dev": true, - "requires": { - "ajv": "^6.9.1", - "lodash": "^4.17.11", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" - }, - "dependencies": { - "ajv": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", - "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "tapable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.0.tgz", - "integrity": "sha512-IlqtmLVaZA2qab8epUXbVWRn3aB1imbDMJtjB3nu4X0NqPkcY/JH9ZtCBWKHWPxs8Svi9tyo8w2dBoi07qZbBA==", - "dev": true - }, - "term-size": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", - "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", - "dev": true, - "requires": { - "execa": "^0.7.0" - } - }, - "terser": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-3.17.0.tgz", - "integrity": "sha512-/FQzzPJmCpjAH9Xvk2paiWrFq+5M6aVOf+2KRbwhByISDX/EujxsK+BAvrhb6H+2rtrLCHK9N01wO014vrIwVQ==", - "dev": true, - "requires": { - "commander": "^2.19.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.10" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-support": { - "version": "0.5.12", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz", - "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - } - } - }, - "terser-webpack-plugin": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.2.3.tgz", - "integrity": "sha512-GOK7q85oAb/5kE12fMuLdn2btOS9OBZn4VsecpHDywoUC/jLhSAKOiYo0ezx7ss2EXPMzyEWFoE0s1WLE+4+oA==", - "dev": true, - "requires": { - "cacache": "^11.0.2", - "find-cache-dir": "^2.0.0", - "schema-utils": "^1.0.0", - "serialize-javascript": "^1.4.0", - "source-map": "^0.6.1", - "terser": "^3.16.1", - "webpack-sources": "^1.1.0", - "worker-farm": "^1.5.2" - }, - "dependencies": { - "bluebird": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.4.tgz", - "integrity": "sha512-FG+nFEZChJrbQ9tIccIfZJBz3J7mLrAhxakAbnrJWn8d7aKOC+LWifa0G+p4ZqKp4y13T7juYvdhq9NzKdsrjw==", - "dev": true - }, - "cacache": { - "version": "11.3.2", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz", - "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==", - "dev": true, - "requires": { - "bluebird": "^3.5.3", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.3", - "graceful-fs": "^4.1.15", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.2", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" - } - }, - "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "mississippi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", - "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", - "dev": true, - "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - } - }, - "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - } - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1" - } - }, - "yallist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", - "dev": true - } - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "throat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", - "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "through2": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", - "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", - "dev": true, - "requires": { - "readable-stream": "^2.1.5", - "xtend": "~4.0.1" - } - }, - "timed-out": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", - "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", - "dev": true - }, - "timers-browserify": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", - "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", - "dev": true, - "requires": { - "setimmediate": "^1.0.4" - } - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "tmpl": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", - "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", - "dev": true - }, - "to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - } - } - }, - "topo": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/topo/-/topo-3.0.3.tgz", - "integrity": "sha512-IgpPtvD4kjrJ7CRA3ov2FhWQADwv+Tdqbsf1ZnPUSAtCJ9e1Z44MmoSGDXGk4IppoZA7jd/QRkNddlLJWlUZsQ==", - "dev": true, - "requires": { - "hoek": "6.x.x" - }, - "dependencies": { - "hoek": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-6.1.3.tgz", - "integrity": "sha512-YXXAAhmF9zpQbC7LEcREFtXfGq5K1fmd+4PHkBq8NUqmzW3G+Dq10bI/i0KucLRwss3YYFQ0fSfoxBZYiGUqtQ==", - "dev": true - } - } - }, - "toposort": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz", - "integrity": "sha1-LmhELZ9k7HILjMieZEOsbKqVACk=", - "dev": true - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "tr46": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "tree-kill": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.1.tgz", - "integrity": "sha512-4hjqbObwlh2dLyW4tcz0Ymw0ggoaVDMveUB9w8kFSQScdRLo0gxO9J7WFcUBo+W3C1TLdFIEwNOWebgZZ0RH9Q==", - "dev": true - }, - "trim-newlines": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", - "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", - "dev": true - }, - "trim-right": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", - "dev": true - }, - "tslib": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", - "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" - }, - "tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", - "dev": true - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "type-is": { - "version": "1.6.16", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", - "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", - "dev": true, - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.18" - } - }, - "typed-styles": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/typed-styles/-/typed-styles-0.0.5.tgz", - "integrity": "sha512-ht+rEe5UsdEBAa3gr64+QjUOqjOLJfWLvl5HZR5Ev9uo/OnD3p43wPeFSB1hNFc13GXQF/JU1Bn0YHLUqBRIlw==" - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, - "ua-parser-js": { - "version": "0.7.19", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.19.tgz", - "integrity": "sha512-T3PVJ6uz8i0HzPxOF9SWzWAlfN/DavlpQqepn22xgve/5QecC+XMCAtmUNnY7C9StehaV6exjUCI801lOI7QlQ==" - }, - "uglify-js": { - "version": "3.4.9", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz", - "integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==", - "dev": true, - "requires": { - "commander": "~2.17.1", - "source-map": "~0.6.1" - }, - "dependencies": { - "commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "unicode-canonical-property-names-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", - "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", - "dev": true - }, - "unicode-match-property-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", - "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", - "dev": true, - "requires": { - "unicode-canonical-property-names-ecmascript": "^1.0.4", - "unicode-property-aliases-ecmascript": "^1.0.4" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz", - "integrity": "sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g==", - "dev": true - }, - "unicode-property-aliases-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.4.tgz", - "integrity": "sha512-2WSLa6OdYd2ng8oqiGIWnJqyFArvhn+5vgx5GTxMbUYjCYKUcuKS62YLFF0R/BDGlB1yzXjQOLtPAfHsgirEpg==", - "dev": true - }, - "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } - } - } - }, - "uniq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", - "dev": true - }, - "unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "dev": true, - "requires": { - "unique-slug": "^2.0.0" - } - }, - "unique-slug": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.1.tgz", - "integrity": "sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4" - } - }, - "unique-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", - "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", - "dev": true, - "requires": { - "crypto-random-string": "^1.0.0" - } - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "dev": true - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "unzip-response": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", - "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=", - "dev": true - }, - "upath": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz", - "integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==", - "dev": true - }, - "update-notifier": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz", - "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", - "dev": true, - "requires": { - "boxen": "^1.2.1", - "chalk": "^2.0.1", - "configstore": "^3.0.0", - "import-lazy": "^2.1.0", - "is-ci": "^1.0.10", - "is-installed-globally": "^0.1.0", - "is-npm": "^1.0.0", - "latest-version": "^3.0.0", - "semver-diff": "^2.0.0", - "xdg-basedir": "^3.0.0" - } - }, - "upper-case": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", - "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", - "dev": true - }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "urijs": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.1.tgz", - "integrity": "sha512-xVrGVi94ueCJNrBSTjWqjvtgvl3cyOTThp2zaMaFNGp3F542TR6sM3f2o8RqZl+AwteClSVmoCyt0ka4RjQOQg==" - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dev": true, - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - } - } - }, - "url-loader": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-1.1.2.tgz", - "integrity": "sha512-dXHkKmw8FhPqu8asTc1puBfe3TehOCo2+RmOOev5suNCIYBcT626kxiWg1NBVkwc4rO8BGa7gP70W7VXuqHrjg==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0", - "mime": "^2.0.3", - "schema-utils": "^1.0.0" - }, - "dependencies": { - "mime": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.3.1.tgz", - "integrity": "sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg==", - "dev": true - } - } - }, - "url-parse-lax": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", - "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", - "dev": true, - "requires": { - "prepend-http": "^1.0.1" - } - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true - }, - "util": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", - "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", - "dev": true, - "requires": { - "inherits": "2.0.3" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "util.promisify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", - "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "object.getownpropertydescriptors": "^2.0.3" - } - }, - "utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", - "dev": true - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "dev": true - }, - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", - "dev": true - }, - "v8-compile-cache": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz", - "integrity": "sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w==", - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "dev": true - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "vkey": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/vkey/-/vkey-0.0.3.tgz", - "integrity": "sha1-Y03XlS6ywRUxvxhZ8ObUGUeAM98=" - }, - "vm-browserify": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", - "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", - "dev": true, - "requires": { - "indexof": "0.0.1" - } - }, - "w3c-hr-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", - "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", - "dev": true, - "requires": { - "browser-process-hrtime": "^0.1.2" - } - }, - "wait-on": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-3.2.0.tgz", - "integrity": "sha512-QUGNKlKLDyY6W/qHdxaRlXUAgLPe+3mLL/tRByHpRNcHs/c7dZXbu+OnJWGNux6tU1WFh/Z8aEwvbuzSAu79Zg==", - "dev": true, - "requires": { - "core-js": "^2.5.7", - "joi": "^13.0.0", - "minimist": "^1.2.0", - "request": "^2.88.0", - "rx": "^4.1.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "wait-port": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/wait-port/-/wait-port-0.2.2.tgz", - "integrity": "sha1-1RpJHkhKF791qUfnEaLwErTm8uM=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "commander": "^2.9.0", - "debug": "^2.6.6" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "walker": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", - "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", - "dev": true, - "requires": { - "makeerror": "1.0.x" - } - }, - "warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", - "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", - "requires": { - "loose-envify": "^1.0.0" - } - }, - "watchpack": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", - "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", - "dev": true, - "requires": { - "chokidar": "^2.0.2", - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0" - } - }, - "webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "dev": true - }, - "webpack": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.31.0.tgz", - "integrity": "sha512-n6RVO3X0LbbipoE62akME9K/JI7qYrwwufs20VvgNNpqUoH4860KkaxJTbGq5bgkVZF9FqyyTG/0WPLH3PVNJA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-module-context": "1.8.5", - "@webassemblyjs/wasm-edit": "1.8.5", - "@webassemblyjs/wasm-parser": "1.8.5", - "acorn": "^6.0.5", - "acorn-dynamic-import": "^4.0.0", - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0", - "chrome-trace-event": "^1.0.0", - "enhanced-resolve": "^4.1.0", - "eslint-scope": "^4.0.0", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^2.3.0", - "loader-utils": "^1.1.0", - "memory-fs": "~0.4.1", - "micromatch": "^3.1.8", - "mkdirp": "~0.5.0", - "neo-async": "^2.5.0", - "node-libs-browser": "^2.0.0", - "schema-utils": "^1.0.0", - "tapable": "^1.1.0", - "terser-webpack-plugin": "^1.1.0", - "watchpack": "^1.5.0", - "webpack-sources": "^1.3.0" - }, - "dependencies": { - "acorn": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", - "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", - "dev": true - }, - "eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - } - } - }, - "webpack-cli": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.2.tgz", - "integrity": "sha512-FLkobnaJJ+03j5eplxlI0TUxhGCOdfewspIGuvDVtpOlrAuKMFC57K42Ukxqs1tn8947/PM6tP95gQc0DCzRYA==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "cross-spawn": "^6.0.5", - "enhanced-resolve": "^4.1.0", - "findup-sync": "^2.0.0", - "global-modules": "^1.0.0", - "import-local": "^2.0.0", - "interpret": "^1.1.0", - "loader-utils": "^1.1.0", - "supports-color": "^5.5.0", - "v8-compile-cache": "^2.0.2", - "yargs": "^12.0.5" - }, - "dependencies": { - "expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", - "dev": true, - "requires": { - "homedir-polyfill": "^1.0.1" - } - }, - "global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", - "dev": true, - "requires": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" - } - }, - "global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", - "dev": true, - "requires": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" - } - }, - "resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", - "dev": true, - "requires": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" - } - } - } - }, - "webpack-dev-middleware": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.6.2.tgz", - "integrity": "sha512-A47I5SX60IkHrMmZUlB0ZKSWi29TZTcPz7cha1Z75yYOsgWh/1AcPmQEbC8ZIbU3A1ytSv1PMU0PyPz2Lmz2jg==", - "dev": true, - "requires": { - "memory-fs": "^0.4.1", - "mime": "^2.3.1", - "range-parser": "^1.0.3", - "webpack-log": "^2.0.0" - }, - "dependencies": { - "mime": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.2.tgz", - "integrity": "sha512-zJBfZDkwRu+j3Pdd2aHsR5GfH2jIWhmL1ZzBoc+X+3JEti2hbArWcyJ+1laC1D2/U/W1a/+Cegj0/OnEU2ybjg==", - "dev": true - } - } - }, - "webpack-log": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", - "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", - "dev": true, - "requires": { - "ansi-colors": "^3.0.0", - "uuid": "^3.3.2" - } - }, - "webpack-sources": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.3.0.tgz", - "integrity": "sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA==", - "dev": true, - "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "dev": true, - "requires": { - "iconv-lite": "0.4.24" - } - }, - "whatwg-fetch": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz", - "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==" - }, - "whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", - "dev": true - }, - "whatwg-url": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", - "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", - "dev": true, - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - }, - "wheel": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/wheel/-/wheel-0.0.0.tgz", - "integrity": "sha1-BP+b6YcXXese3U8kgui2V/MqQWU=" - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "widest-line": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.0.tgz", - "integrity": "sha1-AUKk6KJD+IgsAjOqDgKBqnYVInM=", - "dev": true, - "requires": { - "string-width": "^2.1.1" - } - }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - }, - "worker-farm": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", - "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", - "dev": true, - "requires": { - "errno": "~0.1.7" - } - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "write": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", - "dev": true, - "requires": { - "mkdirp": "^0.5.1" - } - }, - "write-file-atomic": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz", - "integrity": "sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } - }, - "ws": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", - "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0" - } - }, - "xdg-basedir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", - "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", - "dev": true - }, - "xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", - "dev": true - }, - "xmlhttprequest": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", - "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=" - }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - }, - "yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", - "dev": true, - "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - } - } - }, - "yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - } - } - }, - "yauzl": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", - "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", - "dev": true, - "requires": { - "fd-slicer": "~1.0.1" - } - } - } -} diff --git a/client/package.json b/client/package.json deleted file mode 100644 index 99c85535..00000000 --- a/client/package.json +++ /dev/null @@ -1,155 +0,0 @@ -{ - "name": "cellxgene", - "version": "0.10.1", - "license": "MIT", - "description": "cellxgene is a web application for the interactive exploration of single cell sequence data.", - "repository": "https://github.com/chanzuckerberg/cellxgene", - "scripts": { - "backend-dev": "python3.6 -m venv cellxgene && source cellxgene/bin/activate && yes | pip uninstall cellxgene || true && pip install -e .. && cellxgene launch ", - "build": "npm run clean && webpack --config configuration/webpack/webpack.config.prod.js", - "clean": "rimraf build", - "dev": "npm run clean && webpack --config configuration/webpack/webpack.config.dev.js", - "e2e": "node node_modules/jest/bin/jest.js --verbose false --config __tests__/e2e/e2eJestConfig.json e2e/e2e.test.js", - "lint": "eslint src", - "smoke-test": "start-server-and-test start-server-for-test :5000 e2e", - "start": "node server/development.js", - "start-server-for-test": "cellxgene launch -p 5000 ../example-dataset/pbmc3k.h5ad", - "test": "node node_modules/jest/bin/jest.js", - "unit-test": "node node_modules/jest/bin/jest.js --testPathIgnorePatterns e2e" - }, - "engineStrict": true, - "engines": { - "npm": ">=3.0.0" - }, - "eslintConfig": { - "extends": "./configuration/eslint/eslint.js" - }, - "eslintIgnore": [ - "src/util/stateManager/matrix_generated.js" - ], - "resolutions": { - "eslint-scope": "3.7.1" - }, - "dependencies": { - "@blueprintjs/core": "^3.15.1", - "@blueprintjs/icons": "^3.8.0", - "@blueprintjs/select": "^3.8.0", - "canvas-fit": "^1.5.0", - "d3": "^4.10.0", - "d3-scale-chromatic": "^1.3.0", - "flatbuffers": "^1.10.2", - "font-color-contrast": "^1.0.3", - "fuzzysort": "^1.1.4", - "gl-mat4": "^1.1.4", - "gl-vec3": "^1.1.3", - "gl-matrix": "^3.0.0", - "is-number": "^7.0.0", - "key-pressed": "0.0.1", - "lodash": "^4.17.4", - "memoize-one": "^5.0.4", - "mouse-position": "^2.0.1", - "mouse-pressed": "^1.0.0", - "orbit-camera": "^1.0.0", - "query-string": "^6.5.0", - "react": "^16.8.6", - "react-autocomplete": "^1.7.2", - "react-dom": "^16.8.6", - "react-helmet": "^5.2.1", - "react-icons": "^3.7.0", - "react-redux": "^7.0.3", - "redux": "^4.0.1", - "redux-thunk": "^2.2.0", - "regl": "^1.3.11", - "scroll-speed": "^1.0.0", - "urijs": "^1.19.0" - }, - "devDependencies": { - "@babel/core": "^7.4.4", - "@babel/plugin-proposal-class-properties": "^7.4.4", - "@babel/plugin-proposal-decorators": "^7.4.4", - "@babel/plugin-proposal-export-namespace-from": "^7.2.0", - "@babel/plugin-proposal-function-bind": "^7.2.0", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.4.4", - "@babel/plugin-proposal-optional-chaining": "^7.2.0", - "@babel/plugin-transform-react-constant-elements": "^7.2.0", - "@babel/plugin-transform-runtime": "^7.4.4", - "@babel/preset-env": "^7.4.4", - "@babel/preset-react": "^7.0.0", - "@babel/register": "^7.4.4", - "@babel/runtime": "^7.4.4", - "babel-eslint": "^10.0.1", - "babel-jest": "^24.8.0", - "babel-loader": "^8.0.6", - "babel-preset-modern-browsers": "^14.0.0", - "chalk": "^2.4.2", - "connect-history-api-fallback": "^1.6.0", - "copy-webpack-plugin": "^5.0.3", - "css-loader": "^2.1.1", - "eslint": "^5.16.0", - "eslint-config-airbnb": "^17.1.0", - "eslint-config-prettier": "^4.2.0", - "eslint-loader": "^2.1.2", - "eslint-plugin-filenames": "^1.3.2", - "eslint-plugin-import": "^2.17.2", - "eslint-plugin-jest": "^22.5.1", - "eslint-plugin-jsx-a11y": "^6.2.1", - "eslint-plugin-react": "^7.13.0", - "express": "^4.14.0", - "file-loader": "^3.0.1", - "html-webpack-inline-source-plugin": "0.0.10", - "html-webpack-plugin": "^3.2.0", - "jest": "^24.8.0", - "jest-puppeteer": "^4.1.1", - "json-loader": "^0.5.4", - "mini-css-extract-plugin": "^0.6.0", - "puppeteer": "^1.16.0", - "rimraf": "^2.6.3", - "serve-favicon": "^2.3.0", - "start-server-and-test": "^1.9.0", - "style-loader": "^0.23.1", - "sw-precache-webpack-plugin": "^0.11.5", - "url-loader": "^1.1.0", - "webpack": "^4.31.0", - "webpack-cli": "^3.3.2", - "webpack-dev-middleware": "^3.6.2" - }, - "jest": { - "testMatch": [ - "**/__tests__/**/?(*.)(spec|test).js?(x)" - ], - "testURL": "http://localhost/", - "setupFiles": [ - "./__tests__/setupMissingGlobals.js" - ] - }, - "babel": { - "env": { - "test": { - "presets": [ - "@babel/preset-env", - "@babel/preset-react" - ], - "plugins": [ - "@babel/plugin-proposal-function-bind", - [ - "@babel/plugin-proposal-decorators", - { - "legacy": true - } - ], - [ - "@babel/plugin-proposal-class-properties", - { - "loose": true - } - ], - "@babel/plugin-proposal-export-namespace-from", - "@babel/plugin-transform-react-constant-elements", - "@babel/plugin-transform-runtime", - "@babel/plugin-proposal-optional-chaining", - "@babel/plugin-proposal-nullish-coalescing-operator" - ] - } - } - } -} diff --git a/client/server/development.js b/client/server/development.js deleted file mode 100644 index 4c6e5108..00000000 --- a/client/server/development.js +++ /dev/null @@ -1,55 +0,0 @@ -/* eslint-disable */ -// jshint esversion: 6 -var path = require("path"); -var historyApiFallback = require("connect-history-api-fallback"); -var chalk = require("chalk"); -var express = require("express"); -var favicon = require("serve-favicon"); -var webpack = require("webpack"); -var config = require("../configuration/webpack/webpack.config.dev"); -var utils = require("./utils"); - -process.env.NODE_ENV = "development"; - -var PORT = process.env.PORT || 3000; - -// Set up compiler -var compiler = webpack(config); - -compiler.plugin("invalid", () => { - utils.clearConsole(); - console.log("Compiling..."); -}); - -compiler.plugin("done", stats => { - utils.formatStats(stats, PORT); -}); - -// Launch server -var app = express(); - -app.use(historyApiFallback({ verbose: false })); - -app.use( - require("webpack-dev-middleware")(compiler, { - logLevel: "warn", - publicPath: config.output.publicPath - }) -); - -app.use(favicon("./favicon.png")); - -app.get("*", (req, res) => { - res.sendFile(path.resolve("index.html")); -}); - -app.listen(PORT, err => { - if (err) { - console.log(err); - return; - } - - utils.clearConsole(); - console.log(chalk.cyan("Starting the development server...")); - console.log(); -}); diff --git a/client/server/utils.js b/client/server/utils.js deleted file mode 100644 index f274b81c..00000000 --- a/client/server/utils.js +++ /dev/null @@ -1,80 +0,0 @@ -/* eslint-disable */ -// jshint esversion: 6 -var chalk = require("chalk"); - -var friendlySyntaxErrorLabel = "Syntax error:"; - -function isLikelyASyntaxError(message) { - return message.indexOf(friendlySyntaxErrorLabel) !== -1; -} - -function formatMessage(message) { - return message - .replace("Module build failed: SyntaxError:", friendlySyntaxErrorLabel) - .replace( - /Module not found: Error: Cannot resolve 'file' or 'directory'/, - "Module not found:" - ) - .replace(/^\s*at\s.*:\d+:\d+[\s\)]*\n/gm, "") - .replace("./~/css-loader!./~/postcss-loader!", ""); -} -var clearConsole = () => { - process.stdout.write("\x1bc"); -}; - -var formatStats = (stats, port) => { - clearConsole(); - var hasErrors = stats.hasErrors(); - var hasWarnings = stats.hasWarnings(); - if (!hasErrors && !hasWarnings) { - console.log(chalk.green("Compiled successfully!")); - console.log(); - console.log("The app is running at http://localhost:" + port + "/"); - console.log(); - return; - } - - var json = stats.toJson(); - var formattedErrors = json.errors.map( - message => "Error in " + formatMessage(message) - ); - var formattedWarnings = json.warnings.map( - message => "Warning in " + formatMessage(message) - ); - - if (hasErrors) { - console.log(chalk.red("Failed to compile.")); - console.log(); - if (formattedErrors.some(isLikelyASyntaxError)) { - formattedErrors = formattedErrors.filter(isLikelyASyntaxError); - } - formattedErrors.forEach(message => { - console.log(message); - console.log(); - }); - return; - } - - if (hasWarnings) { - console.log(chalk.yellow("Compiled with warnings.")); - console.log(); - formattedWarnings.forEach(message => { - console.log(message); - console.log(); - }); - - console.log("You may use special comments to disable some warnings."); - console.log( - "Use " + - chalk.yellow("// eslint-disable-next-line") + - " to ignore the next line." - ); - console.log( - "Use " + - chalk.yellow("/* eslint-disable */") + - " to ignore all warnings in a file." - ); - } -}; - -module.exports = { formatStats: formatStats, clearConsole: clearConsole }; diff --git a/client/src/actions/index.js b/client/src/actions/index.js deleted file mode 100644 index d8c9c5ca..00000000 --- a/client/src/actions/index.js +++ /dev/null @@ -1,350 +0,0 @@ -// jshint esversion: 6 -import _ from "lodash"; -import * as globals from "../globals"; -import { Universe } from "../util/stateManager"; -import { - catchErrorsWrap, - doJsonRequest, - doBinaryRequest, - dispatchNetworkErrorMessageToUser -} from "../util/actionHelpers"; - -/* -Bootstrap application with the initial data loading. - * /config - application configuration - * /schema - schema of dataframe - * /annotations - all metadata annotation - * /layout - all default layout -*/ -const doInitialDataLoad = () => - catchErrorsWrap(async dispatch => { - dispatch({ type: "initial data load start" }); - - try { - /* - Step 1 - config & schema, all JSON - */ - const requestJson = ["config", "schema"] - .map(r => `${globals.API.prefix}${globals.API.version}${r}`) - .map(url => doJsonRequest(url)); - const stepOneResults = await Promise.all(requestJson); - /* set config defaults */ - const config = { ...globals.configDefaults, ...stepOneResults[0].config }; - const schema = stepOneResults[1]; - - /* - Step 2 - dataframes, all binary. NOTE: uses results of step 1. - */ - /* only load names for var annotations, if possible*/ - const varIndexName = schema?.schema?.annotations?.var?.index; - const varAnnotationsQuery = varIndexName - ? `?annotation-name=${varIndexName}` - : ""; - const varAnnotationsURL = `annotations/var${varAnnotationsQuery}`; - const requestBinary = ["annotations/obs", varAnnotationsURL, "layout/obs"] - .map(r => `${globals.API.prefix}${globals.API.version}${r}`) - .map(url => doBinaryRequest(url)); - const stepTwoResults = await Promise.all(requestBinary); - const [obsAnno, varAnno, obsLayout] = [...stepTwoResults]; - - const universe = Universe.createUniverseFromResponse( - config, - schema, - obsAnno, - varAnno, - obsLayout - ); - - dispatch({ - type: "configuration load complete", - config - }); - dispatch({ - type: "initial data load complete (universe exists)", - universe - }); - } catch (error) { - dispatch({ type: "initial data load error", error }); - } - }, true); - -/* -Set the view (world) to current selection. Placeholder for an async action -which also does re-layout. -*/ -const regraph = () => (dispatch, getState) => { - const { universe, world, crossfilter } = getState(); - dispatch({ - type: "set World to current selection", - universe, - world, - crossfilter - }); -}; - -// Throws -const dispatchExpressionErrors = (dispatch, res) => { - const msg = `Unexpected HTTP response while fetching expression data ${ - res.status - }, ${res.statusText}`; - dispatchNetworkErrorMessageToUser(msg); - throw new Error(msg); -}; - -/* -Fetch expression vectors for each gene in genes. This is NOT an action -function, but rather a helper to be called from an action helper that -needs expression data. - -Transparently utilizes cached data if it is already present. -*/ -async function _doRequestExpressionData(dispatch, getState, genes) { - const state = getState(); - const { universe } = state; - const varIndexName = universe.schema.annotations.var.index; - - /* helper for this function only */ - const fetchData = async geneNames => { - const res = await fetch( - `${globals.API.prefix}${globals.API.version}data/var`, - { - method: "PUT", - body: JSON.stringify({ - filter: { - var: { - annotation_value: [{ name: varIndexName, values: geneNames }] - } - } - }), - headers: new Headers({ - accept: "application/octet-stream", - "Content-Type": "application/json" - }) - } - ); - - if ( - !res.ok || - res.headers.get("Content-Type") !== "application/octet-stream" - ) { - // WILL throw - return dispatchExpressionErrors(dispatch, res); - } - - const data = await res.arrayBuffer(); - return Universe.convertDataFBStoObject(universe, data); - }; - - /* preload data already in cache */ - let expressionData = _.transform( - genes, - (expData, g) => { - const data = universe.varData.col(g); - if (data) { - expData[g] = data.asArray(); - } - }, - {} - ); // --> { gene: data } - /* make a list of genes for which we do not have data */ - const genesToFetch = _.filter(genes, g => expressionData[g] === undefined); - - dispatch({ type: "expression load start" }); - - /* Fetch data for any genes not in cache */ - if (genesToFetch.length) { - try { - const newExpressionData = await fetchData(genesToFetch); - expressionData = { - ...expressionData, - ...newExpressionData - }; - } catch (error) { - dispatch({ type: "expression load error", error }); - throw error; // rethrow - } - } - - dispatch({ type: "expression load success", expressionData }); - return expressionData; -} - -function requestSingleGeneExpressionCountsForColoringPOST(gene) { - return async (dispatch, getState) => { - dispatch({ type: "get single gene expression for coloring started" }); - try { - await _doRequestExpressionData(dispatch, getState, [gene]); - const { world } = getState(); - dispatch({ - type: "color by expression", - gene, - data: { - [gene]: world.varData.col(gene).asArray() - } - }); - } catch (error) { - dispatch({ - type: "get single gene expression for coloring error", - error - }); - } - }; -} - -const requestUserDefinedGene = gene => async (dispatch, getState) => { - dispatch({ type: "request user defined gene started" }); - try { - await await _doRequestExpressionData(dispatch, getState, [gene]); - const { world } = getState(); - - /* then send the success case action through */ - return dispatch({ - type: "request user defined gene success", - data: { - genes: [gene], - expression: world.varData.col(gene).asArray() - } - }); - } catch (error) { - return dispatch({ - type: "request user defined gene error", - error - }); - } -}; - -const dispatchDiffExpErrors = (dispatch, response) => { - switch (response.status) { - case 403: - dispatchNetworkErrorMessageToUser( - "Too many cells selected for differential experesion calculation - please make a smaller selection." - ); - break; - case 501: - dispatchNetworkErrorMessageToUser( - "Differential expression is not implemented." - ); - break; - default: { - const msg = `Unexpected differential expression HTTP response ${ - response.status - }, ${response.statusText}`; - dispatchNetworkErrorMessageToUser(msg); - dispatch({ - type: "request differential expression error", - error: new Error(msg) - }); - } - } -}; - -const requestDifferentialExpression = (set1, set2, num_genes = 10) => async ( - dispatch, - getState -) => { - dispatch({ type: "request differential expression started" }); - try { - /* - Steps: - 1. get the most differentially expressed genes - 2. get expression data for each - */ - const state = getState(); - const { universe } = state; - const varIndexName = universe.schema.annotations.var.index; - - // Legal values are null, Array or TypedArray. Null is initial state. - if (!set1) set1 = []; - if (!set2) set2 = []; - - // These lines ensure that we convert any TypedArray to an Array. - // This is necessary because JSON.stringify() does some very strange - // things with TypedArrays (they are marshalled to JSON objects, rather - // than being marshalled as a JSON array). - set1 = Array.isArray(set1) ? set1 : Array.from(set1); - set2 = Array.isArray(set2) ? set2 : Array.from(set2); - - const res = await fetch( - `${globals.API.prefix}${globals.API.version}diffexp/obs`, - { - method: "POST", - headers: new Headers({ - Accept: "application/json", - "Content-Type": "application/json" - }), - body: JSON.stringify({ - mode: "topN", - count: num_genes, - set1: { filter: { obs: { index: set1 } } }, - set2: { filter: { obs: { index: set2 } } } - }) - } - ); - - if (!res.ok || res.headers.get("Content-Type") !== "application/json") { - return dispatchDiffExpErrors(dispatch, res); - } - - const data = await res.json(); - // result is [ [varIdx, ...], ... ] - const topNGenes = _.map(data, r => - universe.varAnnotations.at(r[0], varIndexName) - ); - - /* - Kick off secondary action to fetch all of the expression data for the - topN expressed genes. - */ - await _doRequestExpressionData(dispatch, getState, topNGenes); - - /* then send the success case action through */ - return dispatch({ - type: "request differential expression success", - data - }); - } catch (error) { - return dispatch({ - type: "request differential expression error", - error - }); - } -}; - -const resetInterface = () => (dispatch, getState) => { - const { universe } = getState(); - - dispatch({ - type: "user reset start" - }); - dispatch({ - type: "clear all user defined genes" - }); - dispatch({ - type: "clear differential expression" - }); - dispatch({ - type: "reset colorscale" - }); - dispatch({ - type: "clear scatterplot" - }); - dispatch({ - type: "reset World to eq Universe", - universe - }); - dispatch({ - type: "increment graph render counter" - }); - dispatch({ - type: "user reset end" - }); -}; - -export default { - regraph, - resetInterface, - requestSingleGeneExpressionCountsForColoringPOST, - requestDifferentialExpression, - requestUserDefinedGene, - doInitialDataLoad -}; diff --git a/client/src/components/app.js b/client/src/components/app.js deleted file mode 100644 index 8c2f1a11..00000000 --- a/client/src/components/app.js +++ /dev/null @@ -1,99 +0,0 @@ -// jshint esversion: 6 -import React from "react"; -import Helmet from "react-helmet"; -import { connect } from "react-redux"; - -import Container from "./framework/container"; -import LeftSideBar from "./leftSidebar"; -import Legend from "./continuousLegend"; -import Graph from "./graph/graph"; -import MenuBar from "./menubar"; - -import actions from "../actions"; - -@connect(state => ({ - loading: state.controls.loading, - error: state.controls.error, - graphRenderCounter: state.controls.graphRenderCounter -})) -class App extends React.Component { - constructor(props) { - super(props); - this.state = {}; - } - - componentDidMount() { - const { dispatch } = this.props; - - /* listen for url changes, fire one when we start the app up */ - window.addEventListener("popstate", this._onURLChanged); - this._onURLChanged(); - - dispatch(actions.doInitialDataLoad(window.location.search)); - - /* listen for resize events */ - window.addEventListener("resize", () => { - dispatch({ - type: "window resize", - data: { - height: window.innerHeight, - width: window.innerWidth - } - }); - }); - dispatch({ - type: "window resize", - data: { - height: window.innerHeight, - width: window.innerWidth - } - }); - } - - _onURLChanged() { - const { dispatch } = this.props; - - dispatch({ type: "url changed", url: document.location.href }); - } - - render() { - const { loading, error, graphRenderCounter } = this.props; - return ( - - - {loading ? ( -
- loading cellxgene -
- ) : null} - {error ? ( -
- error loading -
- ) : null} -
- {loading ? null : } - {loading ? null : } - {loading ? null : } - -
-
- ); - } -} - -export default App; diff --git a/client/src/components/brushableHistogram/index.js b/client/src/components/brushableHistogram/index.js deleted file mode 100644 index 80b303a4..00000000 --- a/client/src/components/brushableHistogram/index.js +++ /dev/null @@ -1,533 +0,0 @@ -/* -https://bl.ocks.org/mbostock/4341954 -https://bl.ocks.org/mbostock/34f08d5e11952a80609169b7917d4172 -https://bl.ocks.org/SpaceActuary/2f004899ea1b2bd78d6f1dbb2febf771 -*/ -// jshint esversion: 6 -import React from "react"; -import { Button, ButtonGroup, Tooltip } from "@blueprintjs/core"; -import { connect } from "react-redux"; -import * as d3 from "d3"; -import memoize from "memoize-one"; -import * as globals from "../../globals"; -import actions from "../../actions"; -import { makeContinuousDimensionName } from "../../util/nameCreators"; - -@connect(state => ({ - world: state.world, - scatterplotXXaccessor: state.controls.scatterplotXXaccessor, - scatterplotYYaccessor: state.controls.scatterplotYYaccessor, - continuousSelection: state.continuousSelection, - differential: state.differential, - colorAccessor: state.colors.colorAccessor -})) -class HistogramBrush extends React.Component { - static getColumn(world, field, clipped = true) { - /* - Return the underlying Dataframe column for our field. By default, - returns the clipped column. If clipped===false, will return the - unclipped column. - */ - const obsAnnotations = clipped - ? world.obsAnnotations - : world.unclipped.obsAnnotations; - const varData = clipped ? world.varData : world.unclipped.varData; - if (obsAnnotations.hasCol(field)) { - return obsAnnotations.col(field); - } - return varData.col(field); - } - - calcHistogramCache = memoize((col, field) => { - /* - recalculate expensive stuff, notably bins, summaries, etc. - */ - const histogramCache = {}; - const values = col.asArray(); - const summary = col.summarize(); - const { min: domainMin, max: domainMax } = summary; - histogramCache.x = d3 - .scaleLinear() - .domain([domainMin, domainMax]) - .range([0, this.width - this.marginRight]); - - histogramCache.bins = d3 - .histogram() - .domain(histogramCache.x.domain()) - .thresholds(40)(values); - - const yMax = histogramCache.bins - .map(b => b.length) - .reduce((a, b) => Math.max(a, b)); - histogramCache.y = d3 - .scaleLinear() - .domain([0, yMax]) - .range([this.height - this.marginBottom, 0]); - - return histogramCache; - }); - - constructor(props) { - super(props); - - this.width = 340; - this.height = 100; - this.marginBottom = 20; // space for X axis & labels - this.marginRight = 40; // space for Y axis & labels - } - - componentDidMount() { - const { field } = this.props; - const { x, y, bins, svgRef } = this._histogram; - - this.renderAxesBrushBins(x, y, bins, svgRef, field); - } - - componentDidUpdate(prevProps) { - const { field, world, continuousSelection } = this.props; - const { x, y, bins, svgRef } = this._histogram; - let { brushXselection, brushX } = this.state; - let forceBrushUpdate = false; - - /* - Update our axis if the underlying dataframe column has changed - */ - const dfColumn = HistogramBrush.getColumn(world, field); - const oldDfColumn = HistogramBrush.getColumn( - prevProps.world, - prevProps.field - ); - if (dfColumn !== oldDfColumn) { - ({ brushXselection, brushX } = this.renderAxesBrushBins( - x, - y, - bins, - svgRef, - field - )); - forceBrushUpdate = true; - } - - /* - if the selection has changed, ensure that the brush correctly reflects - the underlying selection. - */ - if ( - forceBrushUpdate || - continuousSelection !== prevProps.continuousSelection - ) { - const { isObs, isUserDefined, isDiffExp } = this.props; - const myName = makeContinuousDimensionName( - { isObs, isUserDefined, isDiffExp }, - field - ); - const range = continuousSelection[myName]; - if (brushXselection) { - const selection = d3.brushSelection(brushXselection.node()); - if (!range && selection) { - /* no active selection - clear brush */ - brushXselection.call(brushX.move, null); - } else if (range && !selection) { - /* there is an active selection, but no brush - set the brush */ - const x0 = x(range[0]); - const x1 = x(range[1]); - brushXselection.call(brushX.move, [x0, x1]); - } else if (range && selection) { - /* there is an active selection and a brush - make sure they match */ - const moveDeltaThreshold = 1; - const x0 = x(range[0]); - const x1 = x(range[1]); - const dX0 = Math.abs(x0 - selection[0]); - const dX1 = Math.abs(x1 - selection[1]); - /* - only update the brush if it is grossly incorrect, - as defined by the moveDeltaThreshold - */ - if (dX0 > moveDeltaThreshold || dX1 > moveDeltaThreshold) { - brushXselection.call(brushX.move, [x0, x1]); - } - } - } - } - } - - onBrush(selection, x, eventType) { - const type = `continuous metadata histogram ${eventType}`; - return () => { - const { dispatch, field, isObs, isUserDefined, isDiffExp } = this.props; - - // ignore programmatically generated events - if (!d3.event.sourceEvent) return; - // ignore cascading events, which are programmatically generated - if (d3.event.sourceEvent.sourceEvent) return; - - if (d3.event.selection) { - dispatch({ - type, - selection: field, - continuousNamespace: { - isObs, - isUserDefined, - isDiffExp - }, - range: [x(d3.event.selection[0]), x(d3.event.selection[1])] - }); - } else { - dispatch({ - type, - selection: field, - continuousNamespace: { - isObs, - isUserDefined, - isDiffExp - }, - range: null - }); - } - }; - } - - onBrushEnd(selection, x) { - return () => { - const { dispatch, field, isObs, isUserDefined, isDiffExp } = this.props; - const minAllowedBrushSize = 10; - const smallAmountToAvoidInfiniteLoop = 0.1; - - // ignore programmatically generated events - if (!d3.event.sourceEvent) return; - // ignore cascading events, which are programmatically generated - if (d3.event.sourceEvent.sourceEvent) return; - - if (d3.event.selection) { - let _range; - - if ( - d3.event.selection[1] - d3.event.selection[0] > - minAllowedBrushSize - ) { - _range = [x(d3.event.selection[0]), x(d3.event.selection[1])]; - } else { - /* the user selected range is too small and will be hidden #587, so take control of it procedurally */ - /* https://stackoverflow.com/questions/12354729/d3-js-limit-size-of-brush */ - - const procedurallyResizedBrushWidth = - d3.event.selection[0] + - minAllowedBrushSize + - smallAmountToAvoidInfiniteLoop; // - - _range = [x(d3.event.selection[0]), x(procedurallyResizedBrushWidth)]; - } - - dispatch({ - type: "continuous metadata histogram end", - selection: field, - continuousNamespace: { - isObs, - isUserDefined, - isDiffExp - }, - range: _range - }); - } else { - dispatch({ - type: "continuous metadata histogram cancel", - selection: field, - continuousNamespace: { - isObs, - isUserDefined, - isDiffExp - } - }); - } - }; - } - - drawHistogram(svgRef) { - const { field, world } = this.props; - const col = HistogramBrush.getColumn(world, field); - const histogramCache = this.calcHistogramCache(col, field); - const { x, y, bins } = histogramCache; - this._histogram = { x, y, bins, svgRef }; - } - - handleColorAction() { - const { dispatch, field, world, ranges } = this.props; - - if (world.obsAnnotations.hasCol(field)) { - dispatch({ - type: "color by continuous metadata", - colorAccessor: field, - rangeForColorAccessor: ranges - }); - } else if (world.varData.hasCol(field)) { - dispatch(actions.requestSingleGeneExpressionCountsForColoringPOST(field)); - } - } - - removeHistogram() { - const { - dispatch, - field, - colorAccessor, - scatterplotXXaccessor, - scatterplotYYaccessor - } = this.props; - dispatch({ - type: "clear user defined gene", - data: field - }); - if (field === colorAccessor) { - dispatch({ - type: "reset colorscale" - }); - } - if (field === scatterplotXXaccessor) { - dispatch({ - type: "set scatterplot x", - data: null - }); - } - if (field === scatterplotYYaccessor) { - dispatch({ - type: "set scatterplot y", - data: null - }); - } - } - - handleSetGeneAsScatterplotX() { - return () => { - const { dispatch, field } = this.props; - dispatch({ - type: "set scatterplot x", - data: field - }); - }; - } - - handleSetGeneAsScatterplotY() { - return () => { - const { dispatch, field } = this.props; - dispatch({ - type: "set scatterplot y", - data: field - }); - }; - } - - renderAxesBrushBins(x, y, bins, svgRef, field) { - const svg = d3.select(svgRef); - - /* Remove everything */ - svg.selectAll("*").remove(); - - /* BINS */ - svg - .insert("g", "*") - .attr("fill", "#bbb") - .selectAll("rect") - .data(bins) - .enter() - .append("rect") - .attr("x", d => x(d.x0) + 1) - .attr("y", d => y(d.length)) - .attr("width", d => Math.abs(x(d.x1) - x(d.x0) - 1)) - .attr("height", d => y(0) - y(d.length)); - - /* BRUSH */ - const brushX = d3 - .brushX() - .extent([[0, 0], [this.width - this.marginRight, this.height]]) - /* - emit start so that the Undoable history can save an undo point - upon drag start, and ignore the subsequent intermediate drag events. - */ - .on("start", this.onBrush(field, x.invert, "start").bind(this)) - .on("brush", this.onBrush(field, x.invert, "brush").bind(this)) - .on("end", this.onBrushEnd(field, x.invert).bind(this)); - const brushXselection = d3 - .select(svgRef) - .append("g") - .attr("class", "brush") - .attr("data-testid", `${svgRef.dataset.testid}-brush`) - .call(brushX); - - /* X AXIS */ - svg - .append("g") - .attr("class", "axis axis--x") - .attr("transform", `translate(0,${this.height - this.marginBottom})`) - .call(d3.axisBottom(x).ticks(5)); - - /* Y AXIS */ - svg - .append("g") - .attr("class", "axis axis--y") - .attr("transform", `translate(${this.width - this.marginRight},0)`) - .call(d3.axisRight(y).ticks(3)); - - /* axis style */ - svg.selectAll(".axis text").style("fill", "rgb(80,80,80)"); - svg.selectAll(".axis path").style("stroke", "rgb(230,230,230)"); - svg.selectAll(".axis line").style("stroke", "rgb(230,230,230)"); - - const newState = { brushX, brushXselection }; - this.setState(newState); - return newState; - } - - render() { - const { - field, - world, - colorAccessor, - isUserDefined, - isDiffExp, - logFoldChange, - pvalAdj, - scatterplotXXaccessor, - scatterplotYYaccessor, - zebra - } = this.props; - const fieldForId = field.replace(/\s/g, "_"); - const { - min: unclippedRangeMin, - max: unclippedRangeMax - } = HistogramBrush.getColumn(world, field, false).summarize(); - const unclippedRangeMinColor = - world.clipQuantiles.min === 0 ? "#bbb" : globals.blue; - const unclippedRangeMaxColor = - world.clipQuantiles.max === 1 ? "#bbb" : globals.blue; - - return ( -
-
- {isDiffExp || isUserDefined ? ( - - - - - - - - ) : null} - {isUserDefined ? ( - - ) : null} - -
- { - this.drawHistogram(svgRef); - }} - /> -
- - min {unclippedRangeMin.toPrecision(4)} - - - {field} - - - max {unclippedRangeMax.toPrecision(4)} - -
- - {isDiffExp ? ( -
- - log fold change: - {` ${logFoldChange.toPrecision(4)}`} - - - p-value (adj): - {pvalAdj < 0.0001 ? " < 0.0001" : ` ${pvalAdj.toFixed(4)}`} - -
- ) : null} -
- ); - } -} - -export default HistogramBrush; diff --git a/client/src/components/categorical/categorical.css b/client/src/components/categorical/categorical.css deleted file mode 100644 index e69de29b..00000000 diff --git a/client/src/components/categorical/categorical.js b/client/src/components/categorical/categorical.js deleted file mode 100644 index 437cf037..00000000 --- a/client/src/components/categorical/categorical.js +++ /dev/null @@ -1,30 +0,0 @@ -// jshint esversion: 6 -import React from "react"; -import _ from "lodash"; -import { connect } from "react-redux"; -import * as globals from "../../globals"; -import Category from "./category"; - -@connect(state => ({ - categoricalSelection: state.categoricalSelection -})) -class Categories extends React.Component { - render() { - const { categoricalSelection } = this.props; - if (!categoricalSelection) return null; - - return ( -
- {_.map(categoricalSelection, (catState, catName) => ( - - ))} -
- ); - } -} - -export default Categories; diff --git a/client/src/components/categorical/category.js b/client/src/components/categorical/category.js deleted file mode 100644 index 2353c729..00000000 --- a/client/src/components/categorical/category.js +++ /dev/null @@ -1,196 +0,0 @@ -import React from "react"; -import _ from "lodash"; -import { connect } from "react-redux"; -import { FaChevronRight, FaChevronDown } from "react-icons/fa"; -import { Button, Tooltip } from "@blueprintjs/core"; - -import * as globals from "../../globals"; -import Value from "./value"; -import sortedCategoryValues from "./util"; - -@connect(state => ({ - colorAccessor: state.colors.colorAccessor, - categoricalSelection: state.categoricalSelection -})) -class Category extends React.Component { - constructor(props) { - super(props); - this.state = { - isChecked: true, - isExpanded: false - }; - } - - componentDidUpdate(prevProps) { - const { categoricalSelection, metadataField } = this.props; - if (categoricalSelection !== prevProps.categoricalSelection) { - const cat = categoricalSelection[metadataField]; - const categoryCount = { - // total number of categories in this dimension - totalCatCount: cat.numCategoryValues, - // number of selected options in this category - selectedCatCount: _.reduce( - cat.categoryValueSelected, - (res, cond) => (cond ? res + 1 : res), - 0 - ) - }; - if (categoryCount.selectedCatCount === categoryCount.totalCatCount) { - /* everything is on, so not indeterminate */ - this.checkbox.indeterminate = false; - this.setState({ isChecked: true }); // eslint-disable-line react/no-did-update-set-state - } else if (categoryCount.selectedCatCount === 0) { - /* nothing is on, so no */ - this.checkbox.indeterminate = false; - this.setState({ isChecked: false }); // eslint-disable-line react/no-did-update-set-state - } else if (categoryCount.selectedCatCount < categoryCount.totalCatCount) { - /* to be explicit... */ - this.checkbox.indeterminate = true; - this.setState({ isChecked: false }); - } - } - } - - handleColorChange = () => { - const { dispatch, metadataField } = this.props; - dispatch({ - type: "color by categorical metadata", - colorAccessor: metadataField - }); - }; - - toggleAll() { - const { dispatch, metadataField } = this.props; - dispatch({ - type: "categorical metadata filter all of these", - metadataField - }); - this.setState({ isChecked: true }); - } - - toggleNone() { - const { dispatch, metadataField } = this.props; - dispatch({ - type: "categorical metadata filter none of these", - metadataField - }); - this.setState({ isChecked: false }); - } - - handleToggleAllClick() { - const { isChecked } = this.state; - // || this.checkbox.indeterminate === false - if (isChecked) { - this.toggleNone(); - } else if (!isChecked) { - this.toggleAll(); - } - } - - renderCategoryItems() { - const { categoricalSelection, metadataField } = this.props; - - const cat = categoricalSelection[metadataField]; - const optTuples = sortedCategoryValues([...cat.categoryValueIndices]); - return _.map(optTuples, (tuple, i) => ( - - )); - } - - render() { - const { isExpanded, isChecked } = this.state; - const { metadataField, colorAccessor, categoricalSelection } = this.props; - const { isTruncated } = categoricalSelection[metadataField]; - return ( -
-
-
- - - { - this.setState({ isExpanded: !isExpanded }); - }} - > - {metadataField} - {isExpanded ? ( - - ) : ( - - )} - -
- -
-
- {isExpanded ? this.renderCategoryItems() : null} -
-
- {isExpanded && isTruncated ? ( -

... truncated list ...

- ) : null} -
-
- ); - } -} - -export default Category; diff --git a/client/src/components/categorical/occupancy.js b/client/src/components/categorical/occupancy.js deleted file mode 100644 index 6ea86917..00000000 --- a/client/src/components/categorical/occupancy.js +++ /dev/null @@ -1,63 +0,0 @@ -// jshint esversion: 6 -import React from "react"; -import { connect } from "react-redux"; -import * as d3 from "d3"; - -@connect() -class Occupancy extends React.Component { - render() { - const { occupancy, colorScale, colorAccessor, schema, world } = this.props; - const width = 100; - const height = 11; - - const categories = schema.annotations.obsByName[colorAccessor]?.categories; - - const x = d3 - .scaleLinear() - /* get all the keys d[1] as an array, then find the sum */ - .domain([0, d3.sum(Array.from(occupancy, d => d[1]))]) - .range([0, width]); - - let currentOffset = 0; - const dfColumn = world.obsAnnotations.col(colorAccessor); - const categoryValues = dfColumn.summarize().categories; - const stacks = categoryValues.map(d => { - const o = occupancy.get(d); - - const scaledValue = x(o); - - const stackItem = { - key: d, - value: o || 0, - rectWidth: o ? scaledValue : 0, - offset: currentOffset, - fill: o ? colorScale(categories.indexOf(d)) : "rgb(255,255,255)" - }; - currentOffset += o ? scaledValue : 0; - return stackItem; - }); - - return ( - - {stacks.map(d => ( - - ))} - - ); - } -} - -export default Occupancy; diff --git a/client/src/components/categorical/util.js b/client/src/components/categorical/util.js deleted file mode 100644 index 086f2f65..00000000 --- a/client/src/components/categorical/util.js +++ /dev/null @@ -1,34 +0,0 @@ -// jshint esversion: 6 - -// values is [ [optVal, optIdx], ...] -// index is range array -// return sorted index - -import isNumber from "is-number"; - -const sortedCategoryValues = values => { - /* this sort could be memoized for perf */ - - const strings = []; - const ints = []; - - values.forEach(v => { - if (isNumber(v[0])) { - ints.push(v); - } else { - strings.push(v); - } - }); - - strings.sort((a, b) => { - const textA = String(a[0]).toUpperCase(); - const textB = String(b[0]).toUpperCase(); - return textA < textB ? -1 : textA > textB ? 1 : 0; - }); - - ints.sort((a, b) => +a[0] - +b[0]); - - return ints.concat(strings); -}; - -export default sortedCategoryValues; diff --git a/client/src/components/categorical/value.js b/client/src/components/categorical/value.js deleted file mode 100644 index 705412c9..00000000 --- a/client/src/components/categorical/value.js +++ /dev/null @@ -1,166 +0,0 @@ -// jshint esversion: 6 -import { connect } from "react-redux"; -import React from "react"; -import Occupancy from "./occupancy"; -import { countCategoryValues2D } from "../../util/stateManager/worldUtil"; -import * as globals from "../../globals"; - -@connect(state => ({ - categoricalSelection: state.categoricalSelection, - colorScale: state.colors.scale, - colorAccessor: state.colors.colorAccessor, - schema: state.world?.schema, - world: state.world -})) -class CategoryValue extends React.Component { - toggleOff = () => { - const { dispatch, metadataField, categoryIndex } = this.props; - dispatch({ - type: "categorical metadata filter deselect", - metadataField, - categoryIndex - }); - }; - - toggleOn = () => { - const { dispatch, metadataField, categoryIndex } = this.props; - dispatch({ - type: "categorical metadata filter select", - metadataField, - categoryIndex - }); - }; - - handleMouseEnter = () => { - const { dispatch, metadataField, categoryIndex } = this.props; - dispatch({ - type: "category value mouse hover start", - metadataField, - categoryIndex - }); - }; - - handleMouseExit = () => { - const { dispatch, metadataField, categoryIndex } = this.props; - dispatch({ - type: "category value mouse hover end", - metadataField, - categoryIndex - }); - }; - - render() { - const { - categoricalSelection, - metadataField, - categoryIndex, - colorAccessor, - colorScale, - i, - schema, - world - } = this.props; - - if (!categoricalSelection) return null; - - const category = categoricalSelection[metadataField]; - const selected = category.categoryValueSelected[categoryIndex]; - const count = category.categoryValueCounts[categoryIndex]; - const value = category.categoryValues[categoryIndex]; - const displayString = String( - category.categoryValues[categoryIndex] - ).valueOf(); - - /* this is the color scale, so add swatches below */ - const isColorBy = metadataField === colorAccessor; - let categories = null; - let occupancy = null; - - if (isColorBy && schema) { - categories = schema.annotations.obsByName[colorAccessor]?.categories; - } - - if (colorAccessor && !isColorBy && categoricalSelection[colorAccessor]) { - occupancy = countCategoryValues2D( - metadataField, - colorAccessor, - world.obsAnnotations - ); - } - - return ( -
-
- - - {colorAccessor && - !isColorBy && - categoricalSelection[colorAccessor] ? ( - - ) : null} - -
- - - {count} - - - -
- ); - } -} - -export default CategoryValue; diff --git a/client/src/components/continuous/continuous.js b/client/src/components/continuous/continuous.js deleted file mode 100644 index 6f831fb2..00000000 --- a/client/src/components/continuous/continuous.js +++ /dev/null @@ -1,96 +0,0 @@ -// jshint esversion: 6 -/* rc slider https://www.npmjs.com/package/rc-slider */ - -import React from "react"; -import _ from "lodash"; -import { connect } from "react-redux"; -import * as globals from "../../globals"; - -import HistogramBrush from "../brushableHistogram"; - -@connect(state => ({ - obsAnnotations: state.world?.obsAnnotations, - colorAccessor: state.colors.colorAccessor, - colorScale: state.colors.scale, - schema: state.world?.schema -})) -class Continuous extends React.Component { - constructor(props) { - super(props); - this.hasContinuous = false; - this.continuousChecked = false; - - this.state = {}; - } - - componentDidUpdate() {} - - handleColorAction(key) { - return () => { - const { dispatch, obsAnnotations } = this.props; - const summary = obsAnnotations.col(key).summarize(); - dispatch({ - type: "color by continuous metadata", - colorAccessor: key, - rangeForColorAccessor: summary - }); - }; - } - - render() { - const { obsAnnotations, schema } = this.props; - if (schema && !this.continuousChecked) { - this.hasContinuous = _.some( - schema.annotations.obs, - d => d.type === "int32" || d.type === "float32" - ); - this.continuousChecked = true; /* only do this once */ - } - - /* initial value for iterator to simulate index, ranges is an object */ - let zebra = 0; - - return ( -
- {this.hasContinuous ? ( -

- Continuous metadata -

- ) : null} - {obsAnnotations - ? _.map(obsAnnotations.colIndex.keys(), key => { - const isColorField = - key.includes("color") || key.includes("Color"); - if (key === schema.annotations.obs.index || isColorField) - return null; - - const summary = obsAnnotations.col(key).summarize(); - const nonFiniteExtent = - summary.min === undefined || summary.max === undefined; - if (!summary.categorical && !nonFiniteExtent) { - zebra += 1; - return ( - - ); - } - return null; - }) - : null} -
- ); - } -} - -export default Continuous; diff --git a/client/src/components/continuous/drawAxes.js b/client/src/components/continuous/drawAxes.js deleted file mode 100644 index 35aa43cf..00000000 --- a/client/src/components/continuous/drawAxes.js +++ /dev/null @@ -1,83 +0,0 @@ -// jshint esversion: 6 -import * as d3 from "d3"; -import styles from "./parallelCoordinates.css"; -import { yAxis, brushstart } from "./util"; - -const drawAxes = ( - svg, - ctx, - dimensions, - xscale, - height, - width, - handleBrushAction, - handleColorAction -) => { - /***************************************** - ****************************************** - Handles a brush event, toggling the display of foreground lines. - ****************************************** - ******************************************/ - - function brush() { - const actives = []; - svg - .selectAll(".parcoords_axis .parcoords_brush") - .filter(() => d3.brushSelection(this)) - .each(d => { - actives.push({ - dimension: d, - extent: d3.brushSelection(this) - }); - }); - /* fire action, with selected dimensions & their values */ - handleBrushAction(actives); - } - - const axes = svg - .selectAll(".parcoords_axis") - .data(dimensions) - .enter() - .append("g") - .attr("class", `${styles.axis} parcoords_axis`) - .attr("transform", (d, i) => `translate(${xscale(i)})`); - - axes - .append("g") - .each(d => { - const renderAxis = - "axis" in d - ? d.axis.scale(d.scale) // custom axis - : yAxis.scale(d.scale); // default axis - d3.select(this).call(renderAxis); - }) - .append("text") - .on("click", d => { - handleColorAction(d.key); - }) - .attr("class", styles.title) - .attr("text-anchor", "start") - .text(d => ("description" in d ? `${d.description} 🖌️` : `${d.key} 🖌️`)); - - // Add and store a brush for each axis. - axes - .append("g") - .attr("class", `${styles.brush} parcoords_brush`) - .each(d => { - d3.select(this).call( - (d.brush = d3 - .brushY() - .extent([[-10, 0], [10, height]]) - .on("start", brushstart) - .on("brush", brush) - .on("end", brush)) - ); - }) - .selectAll("rect") - .attr("x", -8) - .attr("width", 16); - - return axes; -}; - -export default drawAxes; diff --git a/client/src/components/continuous/parallelCoordinates.css b/client/src/components/continuous/parallelCoordinates.css deleted file mode 100644 index b2affc9c..00000000 --- a/client/src/components/continuous/parallelCoordinates.css +++ /dev/null @@ -1,94 +0,0 @@ -/* - -This code can be found: https://bl.ocks.org/syntagmatic/05a5b0897a48890133beb59c815bd953 - -body { - min-width: 760px; -} -*/ - -:local(.parcoords) { - display: block; -} - -:local(.parcoords) svg, -:local(.parcoords) canvas { - font: 10px sans-serif; - position: absolute; -} - -:local(.parcoords) canvas { - opacity: 0.9; - pointer-events: none; -} - -:local(.axis .title) { - font-size: 10px; - transform: rotate(-21deg) translate(-5px, -6px); - fill: #222; - cursor: pointer; -} - -/* -webkit-filter: grayscale(100%); - filter: grayscale(100%); */ - -:local(.axis) line, -:local(.axis) path { - fill: none; - stroke: #ccc; - stroke-width: 1px; -} - -:local(.axis .tick) text { - fill: #222; - pointer-events: none; -} - -/* -old, from reference bl.ocks -.axis.manufac_name .tick text, -.axis.food_group .tick text { - opacity: 1; -} -*/ - -:local(.axis:hover) line, -:local(.axis:hover) path, -:local(.axis.active) line, -:local(.axis.active) path { - fill: none; - stroke: #222; - stroke-width: 1px; -} - -:local(.axis:hover .title) { - font-weight: bold; -} - -:local(.axis:hover .tick) text { - opacity: 1; -} - -:local(.axis.active .title) { - font-weight: bold; -} - -:local(.axis.active .tick) text { - opacity: 1; - font-weight: bold; -} - -:local(.brush .extent) { - fill-opacity: 0.3; - stroke: #fff; - stroke-width: 1px; -} - -:local(.pre) { - width: 100%; - height: 300px; - margin: 6px 12px; - tab-size: 40; - font-size: 10px; - overflow: auto; -} diff --git a/client/src/components/continuous/setupParallelCoordinates.js b/client/src/components/continuous/setupParallelCoordinates.js deleted file mode 100644 index 90cb7f97..00000000 --- a/client/src/components/continuous/setupParallelCoordinates.js +++ /dev/null @@ -1,40 +0,0 @@ -/***************************************** -****************************************** - Setup SVG & Canvas elements -****************************************** -******************************************/ -// jshint esversion: 6 -import * as d3 from "d3"; - -const setupParallelCoordinates = (width, height, margin) => { - const container = d3.select("#parcoords"); - - const svg = container - .append("svg") - .attr("width", width + margin.left + margin.right) - .attr("height", height + margin.top + margin.bottom) - .append("g") - .attr("transform", `translate(${margin.left},${margin.top})`); - - const canvas = container - .append("canvas") - .attr("width", width * devicePixelRatio) - .attr("height", height * devicePixelRatio) - .style("width", `${width}px`) - .style("height", `${height}px`) - .style("margin-top", `${margin.top}px`) - .style("margin-left", `${margin.left}px`); - - const ctx = canvas.node().getContext("2d"); - ctx.globalCompositeOperation = "darken"; - ctx.globalAlpha = 0.15; - ctx.lineWidth = 1.5; - ctx.scale(devicePixelRatio, devicePixelRatio); - - return { - svg, - ctx - }; -}; - -export default setupParallelCoordinates; diff --git a/client/src/components/continuous/util.js b/client/src/components/continuous/util.js deleted file mode 100644 index 8a156726..00000000 --- a/client/src/components/continuous/util.js +++ /dev/null @@ -1,50 +0,0 @@ -// jshint esversion: 6 -import _ from "lodash"; -import * as d3 from "d3"; - -const paddingRight = 120; -const continuousChartWidth = 1200; - -export const margin = { top: 66, right: 110, bottom: 20, left: 60 }; -export const width = - continuousChartWidth - margin.left - margin.right - paddingRight; -export const height = 340 - margin.top - margin.bottom; -export const innerHeight = height - 2; - -export const devicePixelRatio = window.devicePixelRatio || 1; - -export const createDimensions = data => { - const newArr = []; - _.each(data, (value, key) => { - if (value.range) { - newArr.push({ - key /* room for confusion: lodash calls this key, it's also the name of the property parallel coords code is looking for */, - type: { - within: (d, extent, dim) => - extent[0] <= dim.scale(d) && dim.scale(d) <= extent[1] - }, - scale: d3 - .scaleSqrt() - .range([innerHeight, 0]) - .domain([0, value.range.max]) - }); - } - }); - return newArr; -}; - -export const yAxis = d3.axisLeft(); - -export const brushstart = () => { - d3.event.sourceEvent.stopPropagation(); -}; - -// Unused. -// export const d3_functor = v => (typeof v === "function" ? v : () => v); -export const project = (d, dimensions, xscale) => - dimensions.map((p, i) => { - // check if data element has property and contains a value - if (!(p.key in d) || d[p.key] === null) return null; - - return [xscale(i), p.scale(d[p.key])]; - }); diff --git a/client/src/components/continuousLegend/index.js b/client/src/components/continuousLegend/index.js deleted file mode 100644 index 3ef55850..00000000 --- a/client/src/components/continuousLegend/index.js +++ /dev/null @@ -1,153 +0,0 @@ -// jshint esversion: 6 -import React from "react"; -import { connect } from "react-redux"; -import * as d3 from "d3"; -import { interpolateCool } from "d3-scale-chromatic"; - -// create continuous color legend -// http://bl.ocks.org/syntagmatic/e8ccca52559796be775553b467593a9f -const continuous = (selectorId, colorscale, colorAccessor) => { - const legendheight = 200; - const legendwidth = 80; - const margin = { top: 10, right: 60, bottom: 10, left: 2 }; - - const canvas = d3 - .select(selectorId) - .style("height", `${legendheight}px`) - .style("width", `${legendwidth}px`) - .append("canvas") - .attr("height", legendheight - margin.top - margin.bottom) - .attr("width", 1) - .style("height", `${legendheight - margin.top - margin.bottom}px`) - .style("width", `${legendwidth - margin.left - margin.right}px`) - .style("position", "absolute") - .style("top", `${margin.top + 1}px`) - .style("left", `${margin.left + 1}px`) - .style( - "transform", - "scale(1,-1)" - ) /* flip it! dark is high value light is low. - we flip the color scale as well [1, 0] instead of [0, 1] */ - .node(); - - const ctx = canvas.getContext("2d"); - - const legendscale = d3 - .scaleLinear() - .range([1, legendheight - margin.top - margin.bottom]) - .domain([ - colorscale.domain()[1], - colorscale.domain()[0] - ]); /* we flip this to make viridis colors dark if high in the color scale */ - - // image data hackery based on http://bl.ocks.org/mbostock/048d21cf747371b11884f75ad896e5a5 - const image = ctx.createImageData(1, legendheight); - d3.range(legendheight).forEach(i => { - const c = d3.rgb(colorscale(legendscale.invert(i))); - image.data[4 * i] = c.r; - image.data[4 * i + 1] = c.g; - image.data[4 * i + 2] = c.b; - image.data[4 * i + 3] = 255; - }); - ctx.putImageData(image, 0, 0); - - // A simpler way to do the above, but possibly slower. keep in mind the legend - // width is stretched because the width attr of the canvas is 1 - // See http://stackoverflow.com/questions/4899799/whats-the-best-way-to-set-a-single-pixel-in-an-html5-canvas - /* - d3.range(legendheight).forEach(function(i) { - ctx.fillStyle = colorscale(legendscale.invert(i)); - ctx.fillRect(0,i,1,1); - }); - */ - - const legendaxis = d3 - .axisRight() - .scale(legendscale) - .tickSize(6) - .ticks(8); - - const svg = d3 - .select(selectorId) - .append("svg") - .attr("height", `${legendheight}px`) - .attr("width", `${legendwidth}px`) - .style("position", "absolute") - .style("left", "0px") - .style("top", "0px"); - - svg - .append("g") - .attr("class", "axis") - .attr( - "transform", - `translate(${legendwidth - margin.left - margin.right + 3},${margin.top})` - ) - .call(legendaxis); - - // text label for the y axis - svg - .append("text") - .attr("transform", "rotate(-90)") - .attr("y", 2) - .attr("x", 0 - legendheight / 2) - .attr("dy", "1em") - .style("text-anchor", "middle") - .style("fill", "white") - .text(colorAccessor); -}; - -@connect(state => ({ - colorAccessor: state.colors.colorAccessor, - colorScale: state.colors.scale, - responsive: state.responsive -})) -class ContinuousLegend extends React.Component { - constructor(props) { - super(props); - this.state = {}; - } - - componentDidUpdate(prevProps) { - const { colorAccessor, responsive, colorScale } = this.props; - if ( - prevProps.colorAccessor !== colorAccessor || - prevProps.colorScale !== colorScale || - prevProps.responsive.height !== responsive.height || - prevProps.responsive.width !== responsive.width - ) { - /* always remove it, if it's not continuous we don't put it back. */ - d3.select("#continuous_legend") - .selectAll("*") - .remove(); - } - - if (colorAccessor && colorScale && colorScale.range) { - /* fragile! continuous range is 0 to 1, not [#fa4b2c, ...], make this a flag? */ - if (colorScale.range()[0][0] !== "#") { - continuous( - "#continuous_legend", - d3.scaleSequential(interpolateCool).domain(colorScale.domain()), - colorAccessor - ); - } - } - } - - render() { - const { colorAccessor, responsive } = this.props; - return ( -
- ); - } -} - -export default ContinuousLegend; diff --git a/client/src/components/framework/buttons.css b/client/src/components/framework/buttons.css deleted file mode 100644 index 4e9167bf..00000000 --- a/client/src/components/framework/buttons.css +++ /dev/null @@ -1,6 +0,0 @@ -:local(.btn) { - background: none; - cursor: pointer; - color: "#FFF"; - border: "none"; -} diff --git a/client/src/components/framework/container.css b/client/src/components/framework/container.css deleted file mode 100644 index 2b76dccb..00000000 --- a/client/src/components/framework/container.css +++ /dev/null @@ -1,3 +0,0 @@ -:local(.container) { - min-height: 100%; -} diff --git a/client/src/components/framework/container.js b/client/src/components/framework/container.js deleted file mode 100644 index ce738d19..00000000 --- a/client/src/components/framework/container.js +++ /dev/null @@ -1,11 +0,0 @@ -// jshint esversion: 6 -import React from "react"; - -import styles from "./container.css"; - -const Container = props => { - const { children } = props; - return
{children}
; -}; - -export default Container; diff --git a/client/src/components/framework/logo.js b/client/src/components/framework/logo.js deleted file mode 100644 index 6d378e11..00000000 --- a/client/src/components/framework/logo.js +++ /dev/null @@ -1,18 +0,0 @@ -import React from "react"; -import * as globals from "../../globals"; - -const Logo = props => { - const { size } = props; - return ( - - - - - - - - - ); -}; - -export default Logo; diff --git a/client/src/components/framework/toasters.js b/client/src/components/framework/toasters.js deleted file mode 100644 index 46c9d4e8..00000000 --- a/client/src/components/framework/toasters.js +++ /dev/null @@ -1,31 +0,0 @@ -import { Position, Toaster, Intent } from "@blueprintjs/core"; - -/** Singleton toaster instance. Create separate instances for different options. */ - -const ErrorToastTopCenter = Toaster.create({ - className: "recipe-toaster", - position: Position.TOP -}); - -/* -A "user" error - eg, bad input -*/ -export const postUserErrorToast = message => - ErrorToastTopCenter.show({ message, intent: Intent.WARNING }); - -/* -A toast the user must dismiss manually, because they need to act on its information, -ie., 8 bulk add genes out of 40 were bad. Manually see which ones and fix. -*/ -export const keepAroundErrorToast = message => - ErrorToastTopCenter.show({ message, timeout: 0, intent: Intent.WARNING }); - -/* -a hard network error -*/ -export const postNetworkErrorToast = message => - ErrorToastTopCenter.show({ - message, - timeout: 30000, - intent: Intent.DANGER - }); diff --git a/client/src/components/geneExpression/index.js b/client/src/components/geneExpression/index.js deleted file mode 100644 index dfe0f93f..00000000 --- a/client/src/components/geneExpression/index.js +++ /dev/null @@ -1,336 +0,0 @@ -// jshint esversion: 6 -/* rc slider https://www.npmjs.com/package/rc-slider */ - -import React from "react"; -import _ from "lodash"; -import fuzzysort from "fuzzysort"; - -import { connect } from "react-redux"; -import { - MenuItem, - Button, - FormGroup, - InputGroup, - ControlGroup -} from "@blueprintjs/core"; -import { Suggest } from "@blueprintjs/select"; -import HistogramBrush from "../brushableHistogram"; -import * as globals from "../../globals"; -import actions from "../../actions"; -import { - postUserErrorToast, - keepAroundErrorToast -} from "../framework/toasters"; - -const renderGene = (fuzzySortResult, { handleClick, modifiers, query }) => { - if (!modifiers.matchesPredicate) { - return null; - } - /* the fuzzysort wraps the object with other properties, like a score */ - const geneName = fuzzySortResult.target; - - return ( - - /* this fires when user clicks a menu item */ - handleClick(g) - } - text={geneName} - /> - ); -}; - -const filterGenes = (query, genes) => - /* fires on load, once, and then for each character typed into the input */ - fuzzysort.go(query, genes, { - limit: 5, - threshold: -10000 // don't return bad results - }); - -@connect(state => { - return { - obsAnnotations: state.world?.obsAnnotations, - userDefinedGenes: state.controls.userDefinedGenes, - userDefinedGenesLoading: state.controls.userDefinedGenesLoading, - world: state.world, - colorAccessor: state.colors.colorAccessor, - differential: state.differential - }; -}) -class GeneExpression extends React.Component { - constructor(props) { - super(props); - this.state = { - bulkAdd: "", - tab: "autosuggest" - }; - } - - placeholderGeneNames() { - /* - return a string containing gene name suggestions for use as a user hint. - Eg., Apod, Cd74, ... - Will return a max of 3 genes, totalling 15 characters in length. - Randomly selects gene names. - - NOTE: the random selection means it will re-render constantly. - */ - const { world } = this.props; - const { varAnnotations } = world; - const varIndexName = world.schema.annotations.var.index; - const geneNames = varAnnotations.col(varIndexName).asArray(); - if (geneNames.length > 0) { - const placeholder = []; - let len = geneNames.length; - const maxGeneNameCount = 3; - const maxStrLength = 15; - len = len < maxGeneNameCount ? len : maxGeneNameCount; - for (let i = 0, strLen = 0; i < len && strLen < maxStrLength; i += 1) { - const deal = Math.floor(Math.random() * geneNames.length); - const geneName = geneNames[deal]; - placeholder.push(geneName); - strLen += geneName.length + 2; // '2' is the length of a comma and space - } - placeholder.push("..."); - return placeholder.join(", "); - } - // default - should never happen. - return "Apod, Cd74, ..."; - } - - handleClick(g) { - const { world, dispatch, userDefinedGenes } = this.props; - const varIndexName = world.schema.annotations.var.index; - const gene = g.target; - if (userDefinedGenes.indexOf(gene) !== -1) { - postUserErrorToast("That gene already exists"); - } else if (userDefinedGenes.length > 15) { - postUserErrorToast( - "That's too many genes, you can have at most 15 user defined genes" - ); - } else if ( - world.varAnnotations.col(varIndexName).indexOf(gene) === undefined - ) { - postUserErrorToast("That doesn't appear to be a valid gene name."); - } else { - dispatch({ type: "single user defined gene start" }); - dispatch(actions.requestUserDefinedGene(gene)).then( - () => dispatch({ type: "single user defined gene complete" }), - () => dispatch({ type: "single user defined gene error" }) - ); - } - } - - handleBulkAddClick() { - const { world, dispatch, userDefinedGenes } = this.props; - const varIndexName = world.schema.annotations.var.index; - const { bulkAdd } = this.state; - - /* - test: - Apod,,, Cd74,, ,,, Foo, Bar-2,, - */ - if (bulkAdd !== "") { - const genes = _.pull(_.uniq(bulkAdd.split(/[ ,]+/)), ""); - - dispatch({ type: "bulk user defined gene start" }); - Promise.all( - genes.map(gene => { - if (gene.length === 0) { - return keepAroundErrorToast("Must enter a gene name."); - } - if (userDefinedGenes.indexOf(gene) !== -1) { - return keepAroundErrorToast("That gene already exists"); - } - if ( - world.varAnnotations.col(varIndexName).indexOf(gene) === undefined - ) { - return keepAroundErrorToast( - `${gene} doesn't appear to be a valid gene name.` - ); - } - return dispatch(actions.requestUserDefinedGene(gene)); - }) - ).then( - () => dispatch({ type: "bulk user defined gene complete" }), - () => dispatch({ type: "bulk user defined gene error" }) - ); - } - - this.setState({ bulkAdd: "" }); - } - - render() { - const { - world, - userDefinedGenes, - userDefinedGenesLoading, - differential - } = this.props; - const varIndexName = world?.schema?.annotations?.var?.index; - const { tab, bulkAdd } = this.state; - - return ( -
-
-
- - -
- - {tab === "autosuggest" ? ( - - true : () => false - } - noResults={} - onItemSelect={g => { - /* this happens on 'enter' */ - this.handleClick(g); - }} - inputProps={{ "data-testid": "gene-search" }} - inputValueRenderer={g => { - return ""; - }} - itemListPredicate={filterGenes} - itemRenderer={renderGene.bind(this)} - items={ - world && world.varAnnotations - ? world.varAnnotations.col(varIndexName).asArray() - : ["No genes"] - } - popoverProps={{ minimal: true }} - /> - - - ) : null} - {tab === "bulkadd" ? ( -
-
{ - e.preventDefault(); - this.handleBulkAddClick(); - }} - > - - - { - this.setState({ bulkAdd: e.target.value }); - }} - id="text-input-bulk-add" - data-testid="input-bulk-add" - placeholder={this.placeholderGeneNames()} - value={bulkAdd} - /> - - - -
-
- ) : null} - {world && userDefinedGenes.length > 0 - ? _.map(userDefinedGenes, (geneName, index) => { - const values = world.varData.col(geneName); - if (!values) { - return null; - } - const summary = values.summarize(); - return ( - - ); - }) - : null} -
-
- {differential.diffExp - ? _.map(differential.diffExp, (value, index) => { - const name = world.varAnnotations.at(value[0], varIndexName); - const values = world.varData.col(name); - if (!values) { - return null; - } - const summary = values.summarize(); - return ( - - ); - }) - : null} -
-
- ); - } -} - -export default GeneExpression; diff --git a/client/src/components/graph/drawPointsRegl.js b/client/src/components/graph/drawPointsRegl.js deleted file mode 100644 index 8da7e56b..00000000 --- a/client/src/components/graph/drawPointsRegl.js +++ /dev/null @@ -1,62 +0,0 @@ -// jshint esversion: 6 -const mat4 = require("gl-mat4"); -const vec3 = require("gl-vec3"); - -// opacity: https://github.com/spacetx/starfish/blob/master/viz/draw/regions.js - -export default function(regl) { - return regl({ - vert: ` - precision mediump float; - attribute vec2 position; - attribute vec3 color; - attribute float size; - uniform float distance; - uniform mat4 projection, view; - varying vec3 fragColor; - void main() { - gl_PointSize = 7.0 / pow(distance, 2.5) + size; - gl_Position = projection * view * vec4(position.x, -position.y, 0, 1); - fragColor = color; - }`, - - frag: ` - precision mediump float; - varying vec3 fragColor; - void main() { - if (length(gl_PointCoord.xy - 0.5) > 0.5) { - discard; - } - gl_FragColor = vec4(fragColor, 1); - }`, - - attributes: { - position: regl.prop("position"), - color: regl.prop("color"), - size: regl.prop("size") - }, - - uniforms: { - distance: regl.prop("distance"), - view: regl.prop("view"), - projection: ({ viewportWidth, viewportHeight }) => { - const aspectRatio = viewportWidth / viewportHeight; - let m = mat4.perspective( - [], - Math.PI / 2, - viewportWidth / viewportHeight, - 0.01, - 1000 - ); - if (aspectRatio < 1) { - m = mat4.scale(m, m, vec3.fromValues(1, 1, 1 / aspectRatio)); - } - return m; - } - }, - - count: regl.prop("count"), - - primitive: "points" - }); -} diff --git a/client/src/components/graph/graph.css b/client/src/components/graph/graph.css deleted file mode 100644 index e0bebaa2..00000000 --- a/client/src/components/graph/graph.css +++ /dev/null @@ -1,34 +0,0 @@ -:local(.graphCanvas) path { - shape-rendering: crispEdges; -} - -#graphWrapper { - position: relative; -} - -#graphAttachPoint, -.graphSVG, -.graphCanvas { - position: absolute; -} - -#graphSVG { - pointer-events: none; -} - -:local(.axis) path, -:local(.axis) line { - fill: none; - stroke: #000; - stroke-width: 1px; -} - -circle { - stroke-width: 4px; - stroke: #000; - fill: none; -} - -:local(.hidden) { - display: none; -} diff --git a/client/src/components/graph/graph.js b/client/src/components/graph/graph.js deleted file mode 100644 index 05106dc5..00000000 --- a/client/src/components/graph/graph.js +++ /dev/null @@ -1,656 +0,0 @@ -// jshint esversion: 6 -import React from "react"; -import * as d3 from "d3"; -import { connect } from "react-redux"; -import mat4 from "gl-mat4"; -import _regl from "regl"; -import memoize from "memoize-one"; - -import * as globals from "../../globals"; -import setupSVGandBrushElements from "./setupSVGandBrush"; -import setupCentroidSVG from "./setupCentroidSVG"; -import actions from "../../actions"; -import _camera from "../../util/camera"; -import _drawPoints from "./drawPointsRegl"; -import scaleLinear from "../../util/scaleLinear"; - -/* https://bl.ocks.org/mbostock/9078690 - quadtree for onClick / hover selections */ - -@connect(state => ({ - world: state.world, - crossfilter: state.crossfilter, - responsive: state.responsive, - colorRGB: state.colors.rgb, - selectionTool: state.graphSelection.tool, - currentSelection: state.graphSelection.selection, - layoutChoice: state.layoutChoice, - centroidLabel: state.centroidLabel, - graphInteractionMode: state.controls.graphInteractionMode, - colorAccessor: state.colors.colorAccessor -})) -class Graph extends React.Component { - computePointPositions = memoize((X, Y, scaleX, scaleY) => { - /* - compute webgl coordinate buffer for each point - */ - const positions = new Float32Array(2 * X.length); - for (let i = 0, len = X.length; i < len; i += 1) { - positions[2 * i] = scaleX(X[i]); - positions[2 * i + 1] = scaleY(Y[i]); - } - return positions; - }); - - computePointColors = memoize(rgb => { - /* - compute webgl colors for each point - */ - const colors = new Float32Array(3 * rgb.length); - for (let i = 0, len = rgb.length; i < len; i += 1) { - colors.set(rgb[i], 3 * i); - } - return colors; - }); - - computePointSizes = memoize((len, crossfilter) => { - /* - compute webgl dot size for each point - */ - const sizes = new Float32Array(len); - crossfilter.fillByIsSelected(sizes, 4, 0.2); - return sizes; - }); - - constructor(props) { - super(props); - this.count = 0; - this.graphPaddingTop = 0; - this.graphPaddingRight = globals.leftSidebarWidth; - this.renderCache = { - X: null, - Y: null, - positions: null, - colors: null, - sizes: null - }; - this.state = { - toolSVG: null, - centroidSVG: null, - tool: null, - container: null - }; - } - - componentDidMount() { - // setup canvas and camera - const camera = _camera(this.reglCanvas, { scale: true, rotate: false }); - const regl = _regl(this.reglCanvas); - - const drawPoints = _drawPoints(regl); - - // preallocate buffers - const pointBuffer = regl.buffer(); - const colorBuffer = regl.buffer(); - const sizeBuffer = regl.buffer(); - - // preallocate coordinate system transformation between data and gl - const fractionToUse = 0.93; // fraction of dimension to use - const shiftForMenuBar = 0.05; - const transform = { - glScaleX: scaleLinear([0, 1], [-1 * fractionToUse, 1 * fractionToUse]), - glScaleY: scaleLinear( - [0, 1], - [ - (1 + shiftForMenuBar) * fractionToUse, - (-1 + shiftForMenuBar) * fractionToUse - ] - ) - }; - - /* first time, but this duplicates above function, should be possile to avoid this */ - const reglRender = regl.frame(() => { - this.reglDraw( - regl, - drawPoints, - sizeBuffer, - colorBuffer, - pointBuffer, - camera - ); - camera.tick(); - }); - - this.reglRenderState = "rendering"; - - this.setState({ - regl, - drawPoints, - pointBuffer, - colorBuffer, - sizeBuffer, - camera, - reglRender, - transform - }); - } - - componentDidUpdate(prevProps) { - const { renderCache } = this; - const { - world, - crossfilter, - colorRGB, - responsive, - selectionTool, - currentSelection, - layoutChoice, - graphInteractionMode, - colorAccessor, - centroidLabel - } = this.props; - const { reglRender, mode, regl, toolSVG, centroidSVG } = this.state; - let stateChanges = {}; - - if (reglRender) { - if ( - // If it IS RENDERING and it is NOT IN ZOOM mode, stop rendering. - this.reglRenderState === "rendering" && - graphInteractionMode !== "zoom" - ) { - reglRender.cancel(); - this.reglRenderState = "paused"; - } - - if ( - // If it is NOT RENDERING and it IS IN ZOOM mode, start rendering - this.reglRenderState !== "rendering" && - graphInteractionMode === "zoom" - ) { - this.restartReglLoop(); - this.reglRenderState = "rendering"; - } - } - - if (regl && world) { - /* update the regl state */ - const { obsLayout, nObs } = world; - const { - drawPoints, - transform, - camera, - pointBuffer, - colorBuffer, - sizeBuffer - } = this.state; - - /* coordinates for each point */ - const { glScaleX, glScaleY } = transform; - const X = obsLayout.col(layoutChoice.currentDimNames[0]).asArray(); - const Y = obsLayout.col(layoutChoice.currentDimNames[1]).asArray(); - const newPositions = this.computePointPositions(X, Y, glScaleX, glScaleY); - if (renderCache.positions !== newPositions) { - /* update our cache & GL if the buffer changes */ - renderCache.positions = newPositions; - pointBuffer({ data: newPositions, dimension: 2 }); - } - - /* colors for each point */ - const newColors = this.computePointColors(colorRGB); - if (renderCache.colors !== newColors) { - /* update our cache & GL if the buffer changes */ - renderCache.colors = newColors; - colorBuffer({ data: newColors, dimension: 3 }); - } - - /* sizes for each point */ - const newSizes = this.computePointSizes(nObs, crossfilter); - if (renderCache.sizes !== newSizes) { - /* update our cache & GL if the buffer changes */ - renderCache.size = newSizes; - sizeBuffer({ data: newSizes, dimension: 1 }); - } - - this.count = nObs; - - regl._refresh(); - this.reglDraw( - regl, - drawPoints, - sizeBuffer, - colorBuffer, - pointBuffer, - camera - ); - } - - const createToolSVG = () => { - /* clear out whatever was on the div, even if nothing, but usually the brushes etc */ - d3.select("#graphAttachPoint") - .select("#tool") - .remove(); - - let handleStart; - let handleDrag; - let handleEnd; - let handleCancel; - if (selectionTool === "brush") { - handleStart = this.handleBrushStartAction.bind(this); - handleDrag = this.handleBrushDragAction.bind(this); - handleEnd = this.handleBrushEndAction.bind(this); - } else { - handleStart = this.handleLassoStart.bind(this); - handleEnd = this.handleLassoEnd.bind(this); - handleCancel = this.handleLassoCancel.bind(this); - } - - const { svg: newToolSVG, tool, container } = setupSVGandBrushElements( - selectionTool, - handleStart, - handleDrag, - handleEnd, - handleCancel, - responsive, - this.graphPaddingRight, - graphInteractionMode - ); - - stateChanges = { ...stateChanges, toolSVG: newToolSVG, tool, container }; - }; - - const createCentroidSVG = () => { - d3.select("#graphAttachPoint") - .select("#centroid-container") - .remove(); - - if (centroidLabel.metadataField === "" || !centroidLabel.centroidXY) { - return; - } - - const centroidScreen = this.mapPointToScreen(centroidLabel.centroidXY); - - const newCentroidSVG = setupCentroidSVG( - responsive, - this.graphPaddingRight, - centroidScreen, - centroidLabel.categoryField, - colorAccessor - ); - - stateChanges = { ...stateChanges, centroidSVG: newCentroidSVG }; - }; - - if ( - prevProps.responsive.height !== responsive.height || - prevProps.responsive.width !== responsive.width - ) { - // If the window size has changed we want to recreate all SVGs - createToolSVG(); - createCentroidSVG(); - } else if ( - (responsive.height && responsive.width && !toolSVG) || - selectionTool !== prevProps.selectionTool || - prevProps.graphInteractionMode !== graphInteractionMode - ) { - // first time or change of selection tool6 - createToolSVG(); - } else if ( - centroidLabel !== prevProps.centroidLabel || - (responsive.height && responsive.width && !centroidSVG) - ) { - // First time for centroid or label change - createCentroidSVG(); - } - - /* - if the selection tool or state has changed, ensure that the selection - tool correctly reflects the underlying selection. - */ - if ( - currentSelection !== prevProps.currentSelection || - graphInteractionMode !== prevProps.graphInteractionMode || - stateChanges.toolSVG - ) { - const { tool, container } = this.state; - this.selectionToolUpdate( - stateChanges.tool ? stateChanges.tool : tool, - stateChanges.container ? stateChanges.container : container - ); - } - - if (Object.keys(stateChanges).length > 0) { - this.setState(stateChanges); - } - } - - brushToolUpdate(tool, container) { - /* - this is called from componentDidUpdate(), so be very careful using - anything from this.state, which may be updated asynchronously. - */ - const { currentSelection } = this.props; - if (container) { - const toolCurrentSelection = d3.brushSelection(container.node()); - - if (currentSelection.mode === "within-rect") { - /* - if there is a selection, make sure the brush tool matches - */ - const screenCoords = [ - this.mapPointToScreen(currentSelection.brushCoords.northwest), - this.mapPointToScreen(currentSelection.brushCoords.southeast) - ]; - if (!toolCurrentSelection) { - /* tool is not selected, so just move the brush */ - container.call(tool.move, screenCoords); - } else { - /* there is an active selection and a brush - make sure they match */ - /* this just sums the difference of each dimension, of each point */ - let delta = 0; - for (let x = 0; x < 2; x += 1) { - for (let y = 0; y < 2; y += 1) { - delta += Math.abs( - screenCoords[x][y] - toolCurrentSelection[x][y] - ); - } - } - if (delta > 0) { - container.call(tool.move, screenCoords); - } - } - } else if (toolCurrentSelection) { - /* no selection, so clear the brush tool if it is set */ - container.call(tool.move, null); - } - } - } - - lassoToolUpdate(tool) { - /* - this is called from componentDidUpdate(), so be very careful using - anything from this.state, which may be updated asynchronously. - */ - const { currentSelection } = this.props; - if (currentSelection.mode === "within-polygon") { - /* - if there is a current selection, make sure the lasso tool matches - */ - const polygon = currentSelection.polygon.map(p => - this.mapPointToScreen(p) - ); - tool.move(polygon); - } else { - tool.reset(); - } - } - - selectionToolUpdate(tool, container) { - /* - this is called from componentDidUpdate(), so be very careful using - anything from this.state, which may be updated asynchronously. - */ - const { selectionTool } = this.props; - switch (selectionTool) { - case "brush": - this.brushToolUpdate(tool, container); - break; - case "lasso": - this.lassoToolUpdate(tool, container); - break; - default: - /* punt? */ - break; - } - } - - reglDraw(regl, drawPoints, sizeBuffer, colorBuffer, pointBuffer, camera) { - regl.clear({ - depth: 1, - color: [1, 1, 1, 1] - }); - drawPoints({ - size: sizeBuffer, - distance: camera.distance, - color: colorBuffer, - position: pointBuffer, - count: this.count, - view: camera.view() - }); - } - - restartReglLoop() { - const { - regl, - drawPoints, - sizeBuffer, - colorBuffer, - pointBuffer, - camera - } = this.state; - const reglRender = regl.frame(() => { - this.reglDraw( - regl, - drawPoints, - sizeBuffer, - colorBuffer, - pointBuffer, - camera - ); - camera.tick(); - }); - - this.reglRenderState = "rendering"; - - this.setState({ - reglRender - }); - } - - mapScreenToPoint(pin) { - /* - Map an XY coordinates from screen domain to cell/point range, - accounting for current pan/zoom camera. - */ - const { responsive } = this.props; - const { regl, camera, transform } = this.state; - const { glScaleX, glScaleY } = transform; - - const gl = regl._gl; - - // get aspect ratio - const aspect = gl.drawingBufferWidth / gl.drawingBufferHeight; - const scale = aspect < 1 ? 1 / aspect : 1; - - // compute inverse view matrix - const inverse = mat4.invert([], camera.view()); - - // transform screen coordinates -> cell coordinates - const x = (2 * pin[0]) / (responsive.width - this.graphPaddingRight) - 1; - const y = 2 * (1 - pin[1] / (responsive.height - this.graphPaddingTop)) - 1; - const pout = [ - x * inverse[14] * aspect * scale + inverse[12], - -(y * inverse[14] * scale + inverse[13]) - ]; - - const xy = [glScaleX.invert(pout[0]), glScaleY.invert(pout[1])]; - return xy; - } - - mapPointToScreen(xyCell) { - /* - Map an XY coordinate from cell/point domain to screen range. Inverse - of mapScreenToPoint() - */ - const { responsive } = this.props; - const { regl, camera, transform } = this.state; - const { glScaleX, glScaleY } = transform; - - const gl = regl._gl; - - // get aspect ratio - const aspect = gl.drawingBufferWidth / gl.drawingBufferHeight; - const scale = aspect < 1 ? 1 / aspect : 1; - - // compute inverse view matrix - const inverse = mat4.invert([], camera.view()); - - // variable names are choosen to reflect inverse of those used - // in mapScreenToPoint(). - const pout = [glScaleX(xyCell[0]), glScaleY(xyCell[1])]; - const x = (pout[0] - inverse[12]) / aspect / scale / inverse[14]; - const y = (-pout[1] - inverse[13]) / scale / inverse[14]; - - const pin = [ - Math.round(((x + 1) * (responsive.width - this.graphPaddingRight)) / 2), - Math.round( - -((y + 1) / 2 - 1) * (responsive.height - this.graphPaddingTop) - ) - ]; - return pin; - } - - handleBrushDragAction() { - /* - event describing brush position: - @-------| - | | - | | - |-------@ - */ - // ignore programatically generated events - if (d3.event.sourceEvent === null || !d3.event.selection) return; - - const { dispatch } = this.props; - const s = d3.event.selection; - const brushCoords = { - northwest: this.mapScreenToPoint([s[0][0], s[0][1]]), - southeast: this.mapScreenToPoint([s[1][0], s[1][1]]) - }; - - dispatch({ - type: "graph brush change", - brushCoords - }); - } - - handleBrushStartAction() { - // Ignore programatically generated events. - if (!d3.event.sourceEvent) return; - - const { dispatch } = this.props; - dispatch({ type: "graph brush start" }); - } - - handleBrushEndAction() { - // Ignore programatically generated events. - if (!d3.event.sourceEvent) return; - - /* - coordinates will be included if selection made, null - if selection cleared. - */ - const { dispatch } = this.props; - const s = d3.event.selection; - if (s) { - const brushCoords = { - northwest: this.mapScreenToPoint(s[0]), - southeast: this.mapScreenToPoint(s[1]) - }; - dispatch({ - type: "graph brush end", - brushCoords - }); - } else { - dispatch({ - type: "graph brush deselect" - }); - } - } - - handleBrushDeselectAction() { - const { dispatch } = this.props; - dispatch({ - type: "graph brush deselect" - }); - } - - handleLassoStart() { - const { dispatch } = this.props; - dispatch({ - type: "graph lasso start" - }); - } - - // when a lasso is completed, filter to the points within the lasso polygon - handleLassoEnd(polygon) { - const minimumPolygonArea = 10; - const { dispatch } = this.props; - - if ( - polygon.length < 3 || - Math.abs(d3.polygonArea(polygon)) < minimumPolygonArea - ) { - // if less than three points, or super small area, treat as a clear selection. - dispatch({ type: "graph lasso deselect" }); - } else { - dispatch({ - type: "graph lasso end", - polygon: polygon.map(xy => this.mapScreenToPoint(xy)) // transform the polygon - }); - } - } - - handleLassoCancel() { - const { dispatch } = this.props; - dispatch({ type: "graph lasso cancel" }); - } - - handleLassoDeselectAction() { - const { dispatch } = this.props; - dispatch({ type: "graph lasso deselect" }); - } - - handleDeselectAction() { - const { selectionTool } = this.props; - if (selectionTool === "brush") this.handleBrushDeselectAction(); - if (selectionTool === "lasso") this.handleLassoDeselectAction(); - } - - handleOpacityRangeChange(e) { - const { dispatch } = this.props; - dispatch({ - type: "change opacity deselected cells in 2d graph background", - data: e.target.value - }); - } - - render() { - const { responsive, graphInteractionMode } = this.props; - - return ( -
-
-
-
- { - this.reglCanvas = canvas; - }} - /> -
-
-
- ); - } -} - -export default Graph; diff --git a/client/src/components/graph/setupCentroidSVG.js b/client/src/components/graph/setupCentroidSVG.js deleted file mode 100644 index 68b7a253..00000000 --- a/client/src/components/graph/setupCentroidSVG.js +++ /dev/null @@ -1,34 +0,0 @@ -import * as d3 from "d3"; -import styles from "./graph.css"; - -export default (responsive, graphPaddingRight, xy, text, colorBy) => { - const containerWidth = responsive.width - graphPaddingRight; - - const svg = d3 - .select("#graphAttachPoint") - .append("svg") - .attr("id", "centroid-container") - .attr("data-testid", "centroid-overlay") - .attr("width", containerWidth) - .attr("height", responsive.height) - .attr("class", `${styles.graphSVG}`) - .style("z-index", 998) - .style("pointer-events", "none"); - // TODO: Create own styles, ask Colin for an explanation on the css - // For now I'm going to put centroid z-index at 998 and lasso on 999 - - const label = svg - .append("g") - .attr("transform", `translate(${xy[0]}, ${xy[1]})`); - - label - .append("text") - .attr("text-anchor", "middle") - .text(text) - .style("font-family", "Roboto Condensed") - .style("font-size", "18px") - .style("font-weight", "700") - .style("fill", colorBy ? "black" : "rgb(32, 178, 212)"); - - return svg; -}; diff --git a/client/src/components/graph/setupLasso.js b/client/src/components/graph/setupLasso.js deleted file mode 100644 index 9bbc9d1b..00000000 --- a/client/src/components/graph/setupLasso.js +++ /dev/null @@ -1,146 +0,0 @@ -// https://bl.ocks.org/pbeshai/8008075f9ce771ee8be39e8c38907570 - -import * as d3 from "d3"; - -const Lasso = () => { - const dispatch = d3.dispatch("start", "end", "cancel"); - - const polygonToPath = polygon => - `M${polygon.map(d => d.join(",")).join("L")}`; - - const distance = (pt1, pt2) => - Math.sqrt((pt2[0] - pt1[0]) ** 2 + (pt2[1] - pt1[1]) ** 2); - - // distance last point has to be to first point before it auto closes when mouse is released - const closeDistance = 75; - - const lasso = svg => { - let lassoPolygon; - let lassoPath; - let closePath; - - const handleDragStart = () => { - lassoPolygon = [d3.mouse(svg.node())]; // current x y of mouse within element - - if (lassoPath) { - lassoPath.remove(); - } - - lassoPath = g - .append("path") - .attr("data-testid", "lasso-element") - .attr("fill", "#0bb") - .attr("fill-opacity", 0.1) - .attr("stroke", "#0bb") - .attr("stroke-dasharray", "3, 3"); - - closePath = g - .append("line") - .attr("x2", lassoPolygon[0][0]) - .attr("y2", lassoPolygon[0][1]) - .attr("stroke", "#0bb") - .attr("stroke-dasharray", "3, 3") - .attr("opacity", 0); - - dispatch.call("start", lasso, lassoPolygon); - }; - - const handleDrag = () => { - const point = d3.mouse(svg.node()); - lassoPolygon.push(point); - lassoPath.attr("d", polygonToPath(lassoPolygon)); - - // indicate if we are within closing distance - if ( - distance(lassoPolygon[0], lassoPolygon[lassoPolygon.length - 1]) < - closeDistance - ) { - closePath - .attr("x1", point[0]) - .attr("y1", point[1]) - .attr("opacity", 1); - } else { - closePath.attr("opacity", 0); - } - }; - - const handleDragEnd = () => { - // remove the close path - closePath.remove(); - closePath = null; - - // succesfully closed - if ( - distance(lassoPolygon[0], lassoPolygon[lassoPolygon.length - 1]) < - closeDistance - ) { - lassoPath.attr("d", `${polygonToPath(lassoPolygon)}Z`); - dispatch.call("end", lasso, lassoPolygon); - - // otherwise cancel - } else { - lassoPath.remove(); - lassoPath = null; - lassoPolygon = null; - dispatch.call("cancel"); - } - }; - - // append a with a rect - const g = svg.append("g").attr("class", "lasso-group"); - const bbox = svg.node().getBoundingClientRect(); - const area = g - .append("rect") - .attr("width", bbox.width) - .attr("height", bbox.height) - .attr("fill", "tomato") - .attr("opacity", 0); - - const drag = d3 - .drag() - .on("start", handleDragStart) - .on("drag", handleDrag) - .on("end", handleDragEnd); - - area.call(drag); - - lasso.reset = () => { - if (lassoPath) { - lassoPath.remove(); - lassoPath = null; - } - - lassoPolygon = null; - if (closePath) { - closePath.remove(); - closePath = null; - } - }; - - lasso.move = polygon => { - if (polygon !== lassoPolygon || polygon.length !== lassoPolygon.length) { - lasso.reset(); - - lassoPolygon = polygon; - lassoPath = g - .append("path") - .attr("data-testid", "lasso-element") - .attr("fill", "#0bb") - .attr("fill-opacity", 0.1) - .attr("stroke", "#0bb") - .attr("stroke-dasharray", "3, 3"); - - lassoPath.attr("d", `${polygonToPath(lassoPolygon)}Z`); - } - }; - }; - - lasso.on = (type, callback) => { - dispatch.on(type, callback); - return lasso; - }; - - return lasso; -}; - -export default Lasso; diff --git a/client/src/components/graph/setupSVGandBrush.js b/client/src/components/graph/setupSVGandBrush.js deleted file mode 100644 index 04e38e4f..00000000 --- a/client/src/components/graph/setupSVGandBrush.js +++ /dev/null @@ -1,66 +0,0 @@ -// jshint esversion: 6 -import * as d3 from "d3"; -import styles from "./graph.css"; -import Lasso from "./setupLasso"; - -/****************************************** -******************************************* - put svg & brush in DOM -******************************************* -******************************************/ - -export default ( - selectionToolType, - handleStartAction, - handleDragAction, - handleEndAction, - handleCancelAction, - responsive, - graphPaddingRight, - graphInteractionMode -) => { - const svg = d3 - .select("#graphAttachPoint") - .append("svg") - .attr("id", "tool") - .attr("data-testid", "layout-overlay") - .attr("width", responsive.width - graphPaddingRight) - .attr("height", responsive.height) - .attr("class", `${styles.graphSVG}`) - .style("z-index", 999) - .style("display", graphInteractionMode === "select" ? "inherit" : "none"); - - if (selectionToolType === "brush") { - const brush = d3 - .brush() - .extent([ - [0, 0], - [responsive.width - graphPaddingRight, responsive.height] - ]) - .on("start", handleStartAction) - .on("brush", handleDragAction) - // FYI, brush doesn't generate cancel - .on("end", handleEndAction); - - const brushContainer = svg - .append("g") - .attr("class", "graph_brush") - .call(brush); - - return { svg, container: brushContainer, tool: brush }; - } - - if (selectionToolType === "lasso") { - const lasso = Lasso() - .on("end", handleEndAction) - // FYI, Lasso doesn't generate drag - .on("start", handleStartAction) - .on("cancel", handleCancelAction); - - const lassoContainer = svg.call(lasso); - - return { svg, container: lassoContainer, tool: lasso }; - } - - throw new Error("unknown graph selection tool"); -}; diff --git a/client/src/components/leftSidebar/index.js b/client/src/components/leftSidebar/index.js deleted file mode 100644 index 200f1f7a..00000000 --- a/client/src/components/leftSidebar/index.js +++ /dev/null @@ -1,61 +0,0 @@ -// jshint esversion: 6 -import React from "react"; -import { connect } from "react-redux"; -import Categorical from "../categorical/categorical"; -import Continuous from "../continuous/continuous"; -import GeneExpression from "../geneExpression"; -import * as globals from "../../globals"; -import DynamicScatterplot from "../scatterplot/scatterplot"; -import TopLeftLogoAndTitle from "./topLeftLogoAndTitle"; - -@connect(state => ({ - responsive: state.responsive, - scatterplotXXaccessor: state.controls.scatterplotXXaccessor, - scatterplotYYaccessor: state.controls.scatterplotYYaccessor -})) -class LeftSideBar extends React.Component { - render() { - const { - responsive, - scatterplotXXaccessor, - scatterplotYYaccessor - } = this.props; - - /* - this magic number should be made less fragile, - if cellxgene logo or tabs change, this must as well - */ - const logoRelatedPadding = 50; - - return ( -
- -
- - - -
- {scatterplotXXaccessor && scatterplotYYaccessor ? ( - - ) : null} -
- ); - } -} - -export default LeftSideBar; diff --git a/client/src/components/leftSidebar/topLeftLogoAndTitle.js b/client/src/components/leftSidebar/topLeftLogoAndTitle.js deleted file mode 100644 index 380a10a3..00000000 --- a/client/src/components/leftSidebar/topLeftLogoAndTitle.js +++ /dev/null @@ -1,71 +0,0 @@ -// jshint esversion: 6 -import React from "react"; -import { connect } from "react-redux"; -import * as globals from "../../globals"; -import Logo from "../framework/logo"; - -@connect(state => ({ - responsive: state.responsive, - datasetTitle: state.config?.displayNames?.dataset ?? "", - scatterplotXXaccessor: state.controls.scatterplotXXaccessor, - scatterplotYYaccessor: state.controls.scatterplotYYaccessor -})) -class LeftSideBar extends React.Component { - render() { - const { datasetTitle } = this.props; - - const paddingToAvoidScrollBar = 15; - - return ( -
- - - cell - × - gene - - - {datasetTitle} - -
- ); - } -} - -export default LeftSideBar; diff --git a/client/src/components/menubar/cellSetButtons.js b/client/src/components/menubar/cellSetButtons.js deleted file mode 100644 index 5822c748..00000000 --- a/client/src/components/menubar/cellSetButtons.js +++ /dev/null @@ -1,61 +0,0 @@ -// jshint esversion: 6 -import React from "react"; -import { AnchorButton, Tooltip } from "@blueprintjs/core"; -import { connect } from "react-redux"; -import { World } from "../../util/stateManager"; - -@connect() -class CellSetButton extends React.Component { - set() { - const { - differential, - crossfilter, - dispatch, - eitherCellSetOneOrTwo - } = this.props; - - // Reducer and components assume that value will be null if - // no selection made. World..getSelectedByIndex() returns a - // zero length TypedArray when nothing is selected. - let set = World.getSelectedByIndex(crossfilter); - if (set.length === 0) set = null; - - if (!differential.diffExp) { - /* diffexp needs to be cleared before we store a new set */ - dispatch({ - type: `store current cell selection as differential set ${eitherCellSetOneOrTwo}`, - data: set - }); - } - } - - render() { - const { differential, eitherCellSetOneOrTwo } = this.props; - const cellListName = `celllist${eitherCellSetOneOrTwo}`; - const cellsSelected = differential[cellListName] - ? differential[cellListName].length - : 0; - return ( - - - {eitherCellSetOneOrTwo} - {": "} - - {cellsSelected} - - {" cells"} - - - ); - } -} - -export default CellSetButton; diff --git a/client/src/components/menubar/clip.js b/client/src/components/menubar/clip.js deleted file mode 100644 index b9fb8cf8..00000000 --- a/client/src/components/menubar/clip.js +++ /dev/null @@ -1,130 +0,0 @@ -// jshint esversion: 6 -import React from "react"; -import { - Position, - Button, - Popover, - NumericInput, - Icon -} from "@blueprintjs/core"; - -function Clip(props) { - const { - pendingClipPercentiles, - clipPercentileMin, - clipPercentileMax, - handleClipOpening, - handleClipClosing, - handleClipCommit, - isClipDisabled, - handleClipOnKeyPress, - handleClipPercentileMaxValueChange, - handleClipPercentileMinValueChange - } = props; - - const clipMin = - pendingClipPercentiles?.clipPercentileMin ?? clipPercentileMin; - const clipMax = - pendingClipPercentiles?.clipPercentileMax ?? clipPercentileMax; - const activeClipClass = - clipPercentileMin > 0 || clipPercentileMax < 100 - ? " bp3-intent-warning" - : ""; - - return ( -
- - } - position={Position.BOTTOM_RIGHT} - onOpening={handleClipOpening} - onClosing={handleClipClosing} - content={ -
-
Clip all continuous values to percentile range
-
- - -
- } - /> - - - - -
- } - /> - -
-
- } - /> -
- ); -} - -export default Clip; diff --git a/client/src/components/menubar/index.js b/client/src/components/menubar/index.js deleted file mode 100644 index 7c807f10..00000000 --- a/client/src/components/menubar/index.js +++ /dev/null @@ -1,464 +0,0 @@ -// jshint esversion: 6 -import React from "react"; -import { connect } from "react-redux"; -import { - Button, - AnchorButton, - Tooltip, - Popover, - Position, - RadioGroup, - Radio -} from "@blueprintjs/core"; -import { World } from "../../util/stateManager"; -import actions from "../../actions"; -import CellSetButton from "./cellSetButtons"; -import InformationMenu from "./infoMenu"; -import UndoRedoReset from "./undoRedoReset"; -import Clip from "./clip"; - -@connect(state => ({ - universe: state.universe, - world: state.world, - loading: state.controls.loading, - crossfilter: state.crossfilter, - differential: state.differential, - resettingInterface: state.controls.resettingInterface, - layoutChoice: state.layoutChoice, - graphInteractionMode: state.controls.graphInteractionMode, - clipPercentileMin: Math.round(100 * (state.world?.clipQuantiles?.min ?? 0)), - clipPercentileMax: Math.round(100 * (state.world?.clipQuantiles?.max ?? 1)), - userDefinedGenes: state.controls.userDefinedGenes, - diffexpGenes: state.controls.diffexpGenes, - colorAccessor: state.colors.colorAccessor, - scatterplotXXaccessor: state.controls.scatterplotXXaccessor, - scatterplotYYaccessor: state.controls.scatterplotYYaccessor, - celllist1: state.differential.celllist1, - celllist2: state.differential.celllist2, - libraryVersions: state.config?.library_versions, // eslint-disable-line camelcase - undoDisabled: state["@@undoable/past"].length === 0, - redoDisabled: state["@@undoable/future"].length === 0 -})) -class MenuBar extends React.Component { - static isValidDigitKeyEvent(e) { - /* - Return true if this event is necessary to enter a percent number input. - Return false if not. - - Returns true for events with keys: backspace, control, alt, meta, [0-9], - or events that don't have a key. - */ - if (e.key === null) return true; - if (e.ctrlKey || e.altKey || e.metaKey) return true; - - // concept borrowed from blueprint's numericInputUtils: - // keys that print a single character when pressed have a `key` name of - // length 1. every other key has a longer `key` name (e.g. "Backspace", - // "ArrowUp", "Shift"). since none of those keys can print a character - // to the field--and since they may have important native behaviors - // beyond printing a character--we don't want to disable their effects. - const isSingleCharKey = e.key.length === 1; - if (!isSingleCharKey) return true; - - const key = e.key.charCodeAt(0) - 48; /* "0" */ - return key >= 0 && key <= 9; - } - - constructor(props) { - super(props); - this.state = { - pendingClipPercentiles: null - }; - } - - isClipDisabled = () => { - /* - return true if clip button should be disabled. - */ - const { pendingClipPercentiles } = this.state; - const clipPercentileMin = pendingClipPercentiles?.clipPercentileMin; - const clipPercentileMax = pendingClipPercentiles?.clipPercentileMax; - - const { world } = this.props; - const currentClipMin = 100 * world?.clipQuantiles?.min; - const currentClipMax = 100 * world?.clipQuantiles?.max; - - // if you change this test, be careful with logic around - // comparisons between undefined / NaN handling. - const isDisabled = - !(clipPercentileMin < clipPercentileMax) || - (clipPercentileMin === currentClipMin && - clipPercentileMax === currentClipMax); - - return isDisabled; - }; - - isResetDisabled = () => { - /* - Reset should be disabled when all of the following are true: - * nothing is selected in the crossfilter - * world EQ universe - * nothing is colored by - * there are no userDefinedGenes or diffexpGenes displayed - * scatterplot is not displayed - * nothing in cellset1 or cellset2 - * clip percentiles are [0,100] - */ - const { - crossfilter, - world, - universe, - userDefinedGenes, - diffexpGenes, - colorAccessor, - scatterplotXXaccessor, - scatterplotYYaccessor, - celllist1, - celllist2, - clipPercentileMin, - clipPercentileMax - } = this.props; - - if (!crossfilter || !world || !universe) { - return false; - } - const nothingSelected = crossfilter.countSelected() === crossfilter.size(); - const nothingColoredBy = !colorAccessor; - const noGenes = userDefinedGenes.length === 0 && diffexpGenes.length === 0; - const scatterNotDpl = !scatterplotXXaccessor || !scatterplotYYaccessor; - const nothingInCellsets = !celllist1 && !celllist2; - - return ( - nothingSelected && - World.worldEqUniverse(world, universe) && - nothingColoredBy && - noGenes && - scatterNotDpl && - nothingInCellsets && - clipPercentileMax === 100 && - clipPercentileMin === 0 - ); - }; - - resetInterface = () => { - const { dispatch } = this.props; - dispatch({ - type: "interface reset started" - }); - dispatch(actions.resetInterface()); - }; - - handleClipOnKeyPress = e => { - /* - allow only numbers, plus other critical keys which - may be required to make a number - */ - if (!MenuBar.isValidDigitKeyEvent(e)) { - e.preventDefault(); - } - }; - - handleClipPercentileMinValueChange = v => { - /* - Ignore anything that isn't a legit number - */ - if (!Number.isFinite(v)) return; - - const { pendingClipPercentiles } = this.state; - const clipPercentileMax = pendingClipPercentiles?.clipPercentileMax; - - /* - clamp to [0, currentClipPercentileMax] - */ - if (v <= 0) v = 0; - if (v > 100) v = 100; - const clipPercentileMin = Math.round(v); // paranoia - this.setState({ - pendingClipPercentiles: { clipPercentileMin, clipPercentileMax } - }); - }; - - handleClipPercentileMaxValueChange = v => { - /* - Ignore anything that isn't a legit number - */ - if (!Number.isFinite(v)) return; - - const { pendingClipPercentiles } = this.state; - const clipPercentileMin = pendingClipPercentiles?.clipPercentileMin; - - /* - clamp to [0, 100] - */ - if (v < 0) v = 0; - if (v > 100) v = 100; - const clipPercentileMax = Math.round(v); // paranoia - - this.setState({ - pendingClipPercentiles: { clipPercentileMin, clipPercentileMax } - }); - }; - - handleClipCommit = () => { - const { dispatch } = this.props; - const { pendingClipPercentiles } = this.state; - const { clipPercentileMin, clipPercentileMax } = pendingClipPercentiles; - const min = clipPercentileMin / 100; - const max = clipPercentileMax / 100; - dispatch({ - type: "set clip quantiles", - clipQuantiles: { min, max } - }); - }; - - handleClipOpening = () => { - const { clipPercentileMin, clipPercentileMax } = this.props; - this.setState({ - pendingClipPercentiles: { clipPercentileMin, clipPercentileMax } - }); - }; - - handleClipClosing = () => { - this.setState({ pendingClipPercentiles: null }); - }; - - handleLayoutChoiceChange = e => { - const { dispatch } = this.props; - dispatch({ - type: "set layout choice", - layoutChoice: e.currentTarget.value - }); - }; - - computeDiffExp = () => { - const { dispatch, differential } = this.props; - if (differential.celllist1 && differential.celllist2) { - dispatch( - actions.requestDifferentialExpression( - differential.celllist1, - differential.celllist2 - ) - ); - } - }; - - clearDifferentialExpression = () => { - const { dispatch, differential } = this.props; - dispatch({ - type: "clear differential expression", - diffExp: differential.diffExp - }); - dispatch({ - type: "clear scatterplot" - }); - }; - - render() { - const { - dispatch, - differential, - crossfilter, - resettingInterface, - libraryVersions, - undoDisabled, - redoDisabled, - selectionTool, - clipPercentileMin, - clipPercentileMax, - layoutChoice, - graphInteractionMode - } = this.props; - const { pendingClipPercentiles } = this.state; - - const haveBothCellSets = - !!differential.celllist1 && !!differential.celllist2; - - // constants used to create selection tool button - let selectionTooltip; - let selectionButtonClass; - if (selectionTool === "brush") { - selectionTooltip = "Brush selection"; - selectionButtonClass = "bp3-icon-select"; - } else { - selectionTooltip = "Lasso selection"; - selectionButtonClass = "bp3-icon-polygon-filter"; - } - - return ( -
-
- - - {!differential.diffExp ? ( - - - Compute Differential Expression - - - ) : null} - - {differential.diffExp ? ( - - - - ) : null} -
- - { - dispatch(actions.regraph()); - dispatch({ type: "increment graph render counter" }); - }} - > - subset to current selection - - -
- -
-
- - } - position={Position.BOTTOM_RIGHT} - content={ -
- - {layoutChoice.available.map(name => ( - - ))} - -
- } - /> -
- - - -
- ); - } -} - -export default MenuBar; diff --git a/client/src/components/menubar/infoMenu.js b/client/src/components/menubar/infoMenu.js deleted file mode 100644 index 736e2968..00000000 --- a/client/src/components/menubar/infoMenu.js +++ /dev/null @@ -1,61 +0,0 @@ -// jshint esversion: 6 -import React from "react"; -import { Button, Popover, Menu, MenuItem, Position } from "@blueprintjs/core"; - -function InformationMenu(props) { - const { libraryVersions } = props; - return ( -
- - - - - - - - - } - position={Position.BOTTOM_RIGHT} - > -
- ); -} - -export default InformationMenu; diff --git a/client/src/components/menubar/undoRedoReset.js b/client/src/components/menubar/undoRedoReset.js deleted file mode 100644 index 7baafcf7..00000000 --- a/client/src/components/menubar/undoRedoReset.js +++ /dev/null @@ -1,64 +0,0 @@ -// jshint esversion: 6 -import React from "react"; -import { AnchorButton, Tooltip } from "@blueprintjs/core"; - -function InformationMenu(props) { - const { - resettingInterface, - undoDisabled, - redoDisabled, - resetInterface, - isResetDisabled, - dispatch - } = props; - return ( -
- - { - dispatch({ type: "@@undoable/undo" }); - }} - style={{ - cursor: "pointer" - }} - /> - - - { - dispatch({ type: "@@undoable/redo" }); - }} - style={{ - cursor: "pointer" - }} - /> - - - - reset - - -
- ); -} - -export default InformationMenu; diff --git a/client/src/components/scatterplot/drawPointsRegl.js b/client/src/components/scatterplot/drawPointsRegl.js deleted file mode 100644 index b5f9d6da..00000000 --- a/client/src/components/scatterplot/drawPointsRegl.js +++ /dev/null @@ -1,48 +0,0 @@ -// jshint esversion: 6 -const mat4 = require("gl-mat4"); - -// opacity: https://github.com/spacetx/starfish/blob/master/viz/draw/regions.js - -export default function(regl) { - return regl({ - vert: ` - precision mediump float; - attribute vec2 position; - attribute vec3 color; - attribute float size; - uniform float distance; - uniform mat4 projection, view; - varying vec3 fragColor; - void main() { - gl_PointSize = 7.0 / pow(distance, 2.5) + size; - gl_Position = projection * view * vec4(position.x, -position.y, 0, 1); - fragColor = color; - }`, - - frag: ` - precision mediump float; - varying vec3 fragColor; - void main() { - if (length(gl_PointCoord.xy - 0.5) > 0.5) { - discard; - } - gl_FragColor = vec4(fragColor, 1); - }`, - - attributes: { - position: regl.prop("position"), - color: regl.prop("color"), - size: regl.prop("size") - }, - - uniforms: { - distance: regl.prop("distance"), - view: regl.prop("view"), - projection: () => mat4.perspective([], Math.PI / 2, 1, 0.01, 1000) - }, - - count: regl.prop("count"), - - primitive: "points" - }); -} diff --git a/client/src/components/scatterplot/scatterplot.css b/client/src/components/scatterplot/scatterplot.css deleted file mode 100644 index dab91e0a..00000000 --- a/client/src/components/scatterplot/scatterplot.css +++ /dev/null @@ -1,14 +0,0 @@ -:local(.scatterplot) { - display: block; -} - -:local(.scatterplot) svg, -:local(.scatterplot) canvas { - font: 10px sans-serif; - position: absolute; -} - -:local(.scatterplot) canvas { - opacity: 0.9; - pointer-events: none; -} diff --git a/client/src/components/scatterplot/scatterplot.js b/client/src/components/scatterplot/scatterplot.js deleted file mode 100644 index bb331649..00000000 --- a/client/src/components/scatterplot/scatterplot.js +++ /dev/null @@ -1,383 +0,0 @@ -// jshint esversion: 6 -// https://bl.ocks.org/Jverma/076377dd0125b1a508621441752735fc -// https://peterbeshai.com/scatterplot-in-d3-with-voronoi-interaction.html - -import React from "react"; -import { connect } from "react-redux"; -import { Button, ButtonGroup } from "@blueprintjs/core"; -import _regl from "regl"; -import * as d3 from "d3"; -import * as globals from "../../globals"; - -import _camera from "../../util/camera"; - -import setupScatterplot from "./setupScatterplot"; -import styles from "./scatterplot.css"; - -import _drawPoints from "./drawPointsRegl"; -import scaleLinear from "../../util/scaleLinear"; - -import { margin, width, height } from "./util"; -import finiteExtent from "../../util/finiteExtent"; - -@connect(state => { - const { world, crossfilter } = state; - const { scatterplotXXaccessor, scatterplotYYaccessor } = state.controls; - const expressionX = - world && - scatterplotXXaccessor && - world.varData.hasCol(scatterplotXXaccessor) - ? world.varData.col(scatterplotXXaccessor).asArray() - : null; - const expressionY = - world && - scatterplotYYaccessor && - world.varData.hasCol(scatterplotYYaccessor) - ? world.varData.col(scatterplotYYaccessor).asArray() - : null; - - return { - world, - - colorRGB: state.colors.rgb, - colorScale: state.colors.scale, - colorAccessor: state.colors.colorAccessor, - - // Accessors are var/gene names (strings) - scatterplotXXaccessor, - scatterplotYYaccessor, - opacityForDeselectedCells: state.controls.opacityForDeselectedCells, - - differential: state.differential, - - expressionX, - expressionY, - - crossfilter - }; -}) -class Scatterplot extends React.Component { - constructor(props) { - super(props); - this.count = 0; - this.axes = false; - this.renderCache = { - positions: null, - colors: null, - sizes: null, - xScale: null, - yScale: null - }; - this.state = { - svg: null, - minimized: null - }; - } - - componentDidMount() { - const { svg } = setupScatterplot(width, height, margin); - let scales; - const { expressionX, expressionY } = this.props; - - if (svg && expressionX && expressionY) { - scales = Scatterplot.setupScales(expressionX, expressionY); - this.drawAxesSVG(scales.xScale, scales.yScale, svg); - this.renderCache = { ...this.renderCache, ...scales }; - } - - const camera = _camera(this.reglCanvas, { scale: true, rotate: false }); - const regl = _regl(this.reglCanvas); - - const drawPoints = _drawPoints(regl); - - // preallocate buffers - const pointBuffer = regl.buffer(); - const colorBuffer = regl.buffer(); - const sizeBuffer = regl.buffer(); - - const reglRender = regl.frame(() => { - this.reglDraw( - regl, - drawPoints, - sizeBuffer, - colorBuffer, - pointBuffer, - camera - ); - camera.tick(); - }); - - this.reglRenderState = "rendering"; - - this.setState({ - regl, - sizeBuffer, - pointBuffer, - colorBuffer, - svg, - reglRender, - camera, - drawPoints - }); - } - - componentDidUpdate(prevProps) { - const { - world, - crossfilter, - scatterplotXXaccessor, - scatterplotYYaccessor, - expressionX, - expressionY, - colorRGB - } = this.props; - const { - reglRender, - regl, - pointBuffer, - colorBuffer, - sizeBuffer, - svg, - drawPoints, - camera - } = this.state; - - if ( - scatterplotXXaccessor !== prevProps.scatterplotXXaccessor || // was CLU now FTH1 etc - scatterplotYYaccessor !== prevProps.scatterplotYYaccessor || // was CLU now FTH1 etc - world !== prevProps.world // shape or clip of world changed - ) { - const scales = Scatterplot.setupScales(expressionX, expressionY); - this.drawAxesSVG(scales.xScale, scales.yScale, svg); - this.renderCache = { ...this.renderCache, ...scales }; - } - - if (reglRender && this.reglRenderState === "rendering") { - reglRender.cancel(); - this.reglRenderState = "paused"; - } - - if ( - world && - regl && - pointBuffer && - colorBuffer && - sizeBuffer && - expressionX && - expressionY && - scatterplotXXaccessor && - scatterplotYYaccessor - ) { - const { renderCache } = this; - const { xScale, yScale } = this.renderCache; - const cellCount = expressionX.length; - - // Points change when expressionX or expressionY change. - if ( - !renderCache.positions || - expressionX !== prevProps.expressionX || - expressionY !== prevProps.expressionY - ) { - if (!renderCache.positions) { - renderCache.positions = new Float32Array(2 * cellCount); - } - const glScaleX = scaleLinear([0, width], [-0.95, 0.95]); - const glScaleY = scaleLinear([0, height], [-1, 1]); - for (let i = 0, { positions } = renderCache; i < cellCount; i += 1) { - positions[2 * i] = glScaleX(xScale(expressionX[i])); - positions[2 * i + 1] = glScaleY(yScale(expressionY[i])); - } - pointBuffer({ data: renderCache.positions, dimension: 2 }); - } - - // Colors for each point - change only when props.colorsRGB change. - if (!renderCache.colors || colorRGB !== prevProps.colorRGB) { - if (!renderCache.colors) { - renderCache.colors = new Float32Array(3 * cellCount); - } - for (let i = 0, { colors } = renderCache; i < cellCount; i += 1) { - colors.set(colorRGB[i], 3 * i); - } - colorBuffer({ data: renderCache.colors, dimension: 3 }); - } - - // Sizes for each point - updates are triggered only when selected - // obs change - if (!renderCache.sizes || crossfilter !== prevProps.crossfilter) { - if (!renderCache.sizes) { - renderCache.sizes = new Float32Array(cellCount); - } - crossfilter.fillByIsSelected(renderCache.sizes, 4, 0.2); - sizeBuffer({ data: renderCache.sizes, dimension: 1 }); - } - - this.count = cellCount; - - regl._refresh(); - this.reglDraw( - regl, - drawPoints, - sizeBuffer, - colorBuffer, - pointBuffer, - camera - ); - } - } - - static setupScales(expressionX, expressionY) { - const xScale = d3 - .scaleLinear() - .domain(finiteExtent(expressionX)) - .range([0, width]); - const yScale = d3 - .scaleLinear() - .domain(finiteExtent(expressionY)) - .range([height, 0]); - - return { - xScale, - yScale - }; - } - - reglDraw(regl, drawPoints, sizeBuffer, colorBuffer, pointBuffer, camera) { - regl.clear({ - depth: 1, - color: [1, 1, 1, 1] - }); - - drawPoints({ - size: sizeBuffer, - distance: camera.distance, - color: colorBuffer, - position: pointBuffer, - count: this.count, - view: camera.view() - }); - } - - drawAxesSVG(xScale, yScale, svg) { - const { scatterplotYYaccessor, scatterplotXXaccessor } = this.props; - svg.selectAll("*").remove(); - - // the axes are much cleaner and easier now. No need to rotate and orient - // the axis, just call axisBottom, axisLeft etc. - const xAxis = d3 - .axisBottom() - .ticks(7) - .scale(xScale); - - const yAxis = d3 - .axisLeft() - .ticks(7) - .scale(yScale); - - // adding axes is also simpler now, just translate x-axis to (0,height) - // and it's alread defined to be a bottom axis. - svg - .append("g") - .attr("transform", `translate(0,${height})`) - .attr("class", "x axis") - .call(xAxis); - - // y-axis is translated to (0,0) - svg - .append("g") - .attr("transform", "translate(0,0)") - .attr("class", "y axis") - .call(yAxis); - - // adding label. For x-axis, it's at (10, 10), and for y-axis at (width, height-10). - svg - .append("text") - .attr("x", 10) - .attr("y", 10) - .attr("class", "label") - .style("font-style", "italic") - .text(scatterplotYYaccessor); - - svg - .append("text") - .attr("x", width) - .attr("y", height - 10) - .attr("text-anchor", "end") - .attr("class", "label") - .style("font-style", "italic") - .text(scatterplotXXaccessor); - } - - render() { - const { dispatch } = this.props; - const { minimized } = this.state; - - return ( -
- - - - -
- { - this.reglCanvas = canvas; - }} - /> -
-
- ); - } -} - -export default Scatterplot; diff --git a/client/src/components/scatterplot/setupScatterplot.js b/client/src/components/scatterplot/setupScatterplot.js deleted file mode 100644 index 53b411cc..00000000 --- a/client/src/components/scatterplot/setupScatterplot.js +++ /dev/null @@ -1,26 +0,0 @@ -// jshint esversion: 6 -/***************************************** -****************************************** - Setup SVG & Canvas elements -****************************************** -******************************************/ - -import * as d3 from "d3"; - -const setupScatterplot = (width, height, margin) => { - const container = d3.select("#scatterplot"); - - const svg = container - .append("svg") - .attr("width", width + margin.left + margin.right) - .attr("height", height + margin.top + margin.bottom) - .attr("data-testid", "scatterplot-svg") - .append("g") - .attr("transform", `translate(${margin.left},${margin.top})`); - - return { - svg - }; -}; - -export default setupScatterplot; diff --git a/client/src/components/scatterplot/util.js b/client/src/components/scatterplot/util.js deleted file mode 100644 index f60216f0..00000000 --- a/client/src/components/scatterplot/util.js +++ /dev/null @@ -1,7 +0,0 @@ -// jshint esversion: 6 -export const margin = { top: 40, right: 5, bottom: 20, left: 60 }; -export const width = 340; -export const height = 340 - margin.top - margin.bottom; -export const innerHeight = height - 2; - -export const devicePixelRatio = window.devicePixelRatio || 1; diff --git a/client/src/globals.js b/client/src/globals.js deleted file mode 100644 index 1ff89b05..00000000 --- a/client/src/globals.js +++ /dev/null @@ -1,75 +0,0 @@ -import { Colors } from "@blueprintjs/core"; - -/* if a categorical metadata field has more options than this, truncate */ -export const maxCategoricalOptionsToDisplay = 100; - -/* -these are default values for configuration the CLI may supply. -See the REST API and CLI specs for more info. -*/ -export const configDefaults = { - features: {}, - displayNames: {}, - parameters: { - "max-category-items": 1000 - } -}; - -/* colors */ -export const blue = Colors.BLUE3; -export const linkBlue = Colors.BLUE5; -export const lightestGrey = "rgb(249,249,249)"; -export const lighterGrey = "rgb(245,245,245)"; -export const lightGrey = "rgb(211,211,211)"; -export const mediumGrey = "rgb(153,153,153)"; -export const darkGrey = "rgb(102,102,102)"; -export const darkerGrey = "rgb(51,51,51)"; - -export const brightBlue = "#4a90e2"; -export const brightGreen = "#A2D729"; -export const darkGreen = "#448C4D"; - -export const nonFiniteCellColor = lightGrey; -export const defaultCellColor = "rgb(0,0,0,1)"; -export const logoColor = "black"; /* logo pink: "#E9429A" */ - -/* typography constants */ - -export const tiniestFontSize = 12; -export const largestFontSize = 24; -export const uppercaseLetterSpacing = "0.04em"; -export const bolder = 700; -export const accentFont = "Georgia,Times,Times New Roman,serif"; -export const maxParagraphWidth = 600; - -/* layout styles constants */ - -export const cellxgeneTitleLeftPadding = 14; -export const cellxgeneTitleTopPadding = 7; - -export const maxControlsWidth = 800; - -export const graphMargin = { top: 20, right: 10, bottom: 30, left: 40 }; -export const graphWidth = 700; -export const graphHeight = 700; -export const scatterplotMarginLeft = 25; - -export const leftSidebarWidth = 365; -export const leftSidebarSectionHeading = { - fontSize: 18, - textTransform: "uppercase", - fontWeight: 500, - letterSpacing: ".05em" -}; -export const leftSidebarSectionPadding = 10; - -let _API = { - // prefix: "http://api.clustering.czi.technology/api/", - // prefix: "http://tabulamuris.cxg.czi.technology/api/", - // prefix: "http://api-staging.clustering.czi.technology/api/", - prefix: "http://localhost:5005/api/", - version: "v0.2/" -}; - -if (window.CELLXGENE && window.CELLXGENE.API) _API = window.CELLXGENE.API; -export const API = _API; diff --git a/client/src/index.css b/client/src/index.css deleted file mode 100644 index f0e9c405..00000000 --- a/client/src/index.css +++ /dev/null @@ -1,9 +0,0 @@ -/* ->>>GLOBAL<<< CSS imports & definitions - -Do NOT put anything in this file that is not global. We do not perform -namespace mangling on these definitions. See webpack config for specifics. -*/ -@import "~@blueprintjs/core/lib/css/blueprint.css"; -@import "~@blueprintjs/icons/lib/css/blueprint-icons.css"; -@import "~@blueprintjs/select/lib/css/blueprint-select.css"; diff --git a/client/src/index.js b/client/src/index.js deleted file mode 100644 index b4655afa..00000000 --- a/client/src/index.js +++ /dev/null @@ -1,21 +0,0 @@ -// jshint esversion: 6 -/* eslint-disable no-console */ -import React from "react"; -import ReactDOM from "react-dom"; -import { Provider } from "react-redux"; -import { FocusStyleManager } from "@blueprintjs/core"; - -import "./index.css"; - -/* our code */ -import App from "./components/app"; -import store from "./reducers"; - -FocusStyleManager.onlyShowFocusOnTabs(); - -ReactDOM.render( - - - , - document.getElementById("root") -); diff --git a/client/src/reducers/cascade.js b/client/src/reducers/cascade.js deleted file mode 100644 index 3ae980ec..00000000 --- a/client/src/reducers/cascade.js +++ /dev/null @@ -1,44 +0,0 @@ -export default function cascadeReducers(arg) { - /* - Combined a set of cascading reducers into a single reducer. Cascading - reducers are reducers which may rely on state computed by another reducer. - Therefore, they: - - must be composed in a particular order (currently, this is a simple - linear list of reducers, run in list order) - - must have access to partially updated "next state" so they can further - derive state. - - Parameter is one of: - - a Map object - - an array of tuples, [ [key1, reducer1], [key2, reducer2], ... ] - Ie, cascadeReducers([ ["a", reduceA], ["b", reduceB] ]) - - Each reducer will be called with the sigature: - (prevState, action, sharedNextState, sharedPrevState) => newState - - cascadeReducers will build a composite newState object, much - like combinedReducers. Additional semantics: - - reducers guaranteed to be called in order - - each reducer will receive shared objects - */ - const reducers = arg instanceof Map ? arg : new Map(arg); - const reducerKeys = [...reducers.keys()]; - return (prevState, action) => { - const nextState = {}; - let stateChange = false; - for (let i = 0, l = reducerKeys.length; i < l; i += 1) { - const key = reducerKeys[i]; - const reducer = reducers.get(key); - const prevStateForKey = prevState ? prevState[key] : undefined; - const nextStateForKey = reducer( - prevStateForKey, - action, - nextState, - prevState - ); - nextState[key] = nextStateForKey; - stateChange = stateChange || nextStateForKey !== prevStateForKey; - } - return stateChange ? nextState : prevState; - }; -} diff --git a/client/src/reducers/categoricalSelection.js b/client/src/reducers/categoricalSelection.js deleted file mode 100644 index af48a765..00000000 --- a/client/src/reducers/categoricalSelection.js +++ /dev/null @@ -1,105 +0,0 @@ -import { ControlsHelpers } from "../util/stateManager"; -import * as globals from "../globals"; - -function maxCategoryItems(state) { - return ( - state.config.parameters?.["max-category-items"] ?? - globals.configDefaults.parameters["max-category-items"] - ); -} - -const CategoricalSelection = ( - state, - action, - nextSharedState, - prevSharedState -) => { - switch (action.type) { - case "initial data load complete (universe exists)": - case "set World to current selection": - case "reset World to eq Universe": - case "set clip quantiles": { - const { world } = nextSharedState; - return ControlsHelpers.createCategoricalSelection( - maxCategoryItems(prevSharedState), - world - ); - } - - case "categorical metadata filter select": { - /* - Set the specific category in this field to false - */ - const newCategoryValueSelected = Array.from( - state[action.metadataField].categoryValueSelected - ); - newCategoryValueSelected[action.categoryIndex] = true; - const newCategoricalSelection = { - ...state, - [action.metadataField]: { - ...state[action.metadataField], - categoryValueSelected: newCategoryValueSelected - } - }; - return newCategoricalSelection; - } - - case "categorical metadata filter deselect": { - /* - Set the specific category in this field to false - */ - const newCategoryValueSelected = Array.from( - state[action.metadataField].categoryValueSelected - ); - newCategoryValueSelected[action.categoryIndex] = false; - const newCategoricalSelection = { - ...state, - [action.metadataField]: { - ...state[action.metadataField], - categoryValueSelected: newCategoryValueSelected - } - }; - return newCategoricalSelection; - } - - case "categorical metadata filter none of these": { - /* - set all categories in this field to false. - */ - const newCategoricalSelection = { - ...state, - [action.metadataField]: { - ...state[action.metadataField], - categorySelected: false, - categoryValueSelected: Array.from( - state[action.metadataField].categoryValueSelected - ).fill(false) - } - }; - return newCategoricalSelection; - } - - case "categorical metadata filter all of these": { - /* - set all categories in this field to true. - */ - const newCategoricalSelection = { - ...state, - [action.metadataField]: { - ...state[action.metadataField], - categorySelected: true, - categoryValueSelected: Array.from( - state[action.metadataField].categoryValueSelected - ).fill(true) - } - }; - return newCategoricalSelection; - } - - default: { - return state; - } - } -}; - -export default CategoricalSelection; diff --git a/client/src/reducers/centroidLabel.js b/client/src/reducers/centroidLabel.js deleted file mode 100644 index 5582e5cc..00000000 --- a/client/src/reducers/centroidLabel.js +++ /dev/null @@ -1,44 +0,0 @@ -import calcCentroid from "../util/centroid"; - -const initialState = { - metadataField: "", - categoryIndex: -1, - categoryField: "", - centroidXY: [-1, -1] -}; - -const CentroidLabel = (state = initialState, action, sharedNextState) => { - const { categoricalSelection, world, layoutChoice } = sharedNextState; - const { metadataField, categoryIndex } = action; - const categoryField = - categoricalSelection?.[metadataField]?.categoryValues[categoryIndex]; - switch (action.type) { - case "category value mouse hover start": - return { - ...state, - metadataField, - categoryIndex, - categoryField, - centroidXY: calcCentroid( - world, - metadataField, - categoryField, - layoutChoice.currentDimNames - ) - }; - - case "category value mouse hover end": - if ( - metadataField === state.metadataField && - categoryIndex === state.categoryIndex - ) { - return initialState; - } - return state; - - default: - return state; - } -}; - -export default CentroidLabel; diff --git a/client/src/reducers/colors.js b/client/src/reducers/colors.js deleted file mode 100644 index 95daa2db..00000000 --- a/client/src/reducers/colors.js +++ /dev/null @@ -1,100 +0,0 @@ -import { createColors } from "../util/stateManager"; - -const ColorsReducer = ( - state = { - colorMode: null, - colorAccessor: null, - rgb: null, - scale: null - }, - action, - nextSharedState, - prevSharedState -) => { - switch (action.type) { - case "initial data load complete (universe exists)": - case "reset World to eq Universe": { - const { world } = nextSharedState; - const colorMode = null; - const colorAccessor = null; - const { rgb, scale } = createColors(world, colorMode); - return { - ...state, - colorAccessor, - colorMode, - rgb, - scale - }; - } - - case "set clip quantiles": - case "set World to current selection": { - const { colorMode, colorAccessor } = state; - const { world } = nextSharedState; - const { rgb, scale } = createColors(world, colorMode, colorAccessor); - return { - ...state, - rgb, - scale - }; - } - - case "reset colorscale": { - const { world } = prevSharedState; - const { rgb, scale } = createColors(world); - return { - ...state, - colorMode: null, - colorAccessor: null, - rgb, - scale - }; - } - - case "color by categorical metadata": - case "color by continuous metadata": { - const { world } = prevSharedState; - - /* toggle between this mode and reset */ - const resetCurrent = - action.type === state.colorMode && - action.colorAccessor === state.colorAccessor; - const colorMode = !resetCurrent ? action.type : null; - const colorAccessor = !resetCurrent ? action.colorAccessor : null; - - const { rgb, scale } = createColors(world, colorMode, colorAccessor); - return { - ...state, - colorMode, - colorAccessor, - rgb, - scale - }; - } - - case "color by expression": { - const { world } = prevSharedState; - - /* toggle between this mode and reset */ - const resetCurrent = - action.type === state.colorMode && action.gene === state.colorAccessor; - const colorMode = !resetCurrent ? action.type : null; - const colorAccessor = !resetCurrent ? action.gene : null; - - const { rgb, scale } = createColors(world, colorMode, colorAccessor); - return { - ...state, - colorMode, - colorAccessor, - rgb, - scale - }; - } - - default: { - return state; - } - } -}; - -export default ColorsReducer; diff --git a/client/src/reducers/config.js b/client/src/reducers/config.js deleted file mode 100644 index 4ddf4ec3..00000000 --- a/client/src/reducers/config.js +++ /dev/null @@ -1,34 +0,0 @@ -// jshint esversion: 6 -const Config = ( - state = { - displayNames: null, - features: null, - parameters: null - }, - action -) => { - switch (action.type) { - case "initial data load start": - return { - ...state, - loading: true, - error: null - }; - case "configuration load complete": - return { - ...state, - loading: false, - error: null, - ...action.config - }; - case "initial data load error": - return { - ...state, - error: action.error - }; - default: - return state; - } -}; - -export default Config; diff --git a/client/src/reducers/continuousSelection.js b/client/src/reducers/continuousSelection.js deleted file mode 100644 index 459aa575..00000000 --- a/client/src/reducers/continuousSelection.js +++ /dev/null @@ -1,35 +0,0 @@ -import { makeContinuousDimensionName } from "../util/nameCreators"; - -const ContinuousSelection = (state = {}, action) => { - switch (action.type) { - case "reset World to eq Universe": - case "set clip quantiles": { - return {}; - } - case "continuous metadata histogram start": - case "continuous metadata histogram brush": - case "continuous metadata histogram end": { - const name = makeContinuousDimensionName( - action.continuousNamespace, - action.selection - ); - return { - ...state, - [name]: action.range - }; - } - case "continuous metadata histogram cancel": { - const name = makeContinuousDimensionName( - action.continuousNamespace, - action.selection - ); - const { [name]: deletedField, ...newState } = state; - return newState; - } - default: { - return state; - } - } -}; - -export default ContinuousSelection; diff --git a/client/src/reducers/controls.js b/client/src/reducers/controls.js deleted file mode 100644 index e9f77956..00000000 --- a/client/src/reducers/controls.js +++ /dev/null @@ -1,190 +0,0 @@ -// jshint esversion: 6 - -import _ from "lodash"; - -import { WorldUtil } from "../util/stateManager"; - -const Controls = ( - state = { - // data loading flag - loading: false, - error: null, - - // all of the data + selection state - userDefinedGenes: [], - userDefinedGenesLoading: false, - diffexpGenes: [], - - resettingInterface: false, - graphInteractionMode: "select", - opacityForDeselectedCells: 0.2, - scatterplotXXaccessor: null, // just easier to read - scatterplotYYaccessor: null, - graphRenderCounter: 0 /* integer as { - /* - For now, log anything looking like an error to the console. - */ - if (action.error || /error/i.test(action.type)) { - console.error(action.error); - } - - switch (action.type) { - /***************************************************** - Initialization, World/Universe management - and data loading. - ******************************************************/ - case "initial data load start": { - return { ...state, loading: true }; - } - case "initial data load complete (universe exists)": { - /* first light - create world & other data-driven defaults */ - WorldUtil.clearCaches(); - return { - ...state, - loading: false, - error: null, - resettingInterface: false - }; - } - case "reset World to eq Universe": { - WorldUtil.clearCaches(); - return { - ...state, - resettingInterface: false - }; - } - case "set World to current selection": { - WorldUtil.clearCaches(); - return { - ...state, - loading: false, - error: null - }; - } - case "request user defined gene started": { - return { - ...state, - userDefinedGenesLoading: true - }; - } - case "request user defined gene error": { - return { - ...state, - userDefinedGenesLoading: false - }; - } - case "request user defined gene success": { - const { userDefinedGenes } = state; - const _userDefinedGenes = _.uniq( - userDefinedGenes.concat(action.data.genes) - ); - return { - ...state, - userDefinedGenes: _userDefinedGenes, - userDefinedGenesLoading: false - }; - } - case "request differential expression success": { - const { world } = prevSharedState; - const varIndexName = world.schema.annotations.var.index; - const _diffexpGenes = []; - action.data.forEach(d => { - _diffexpGenes.push(world.varAnnotations.at(d[0], varIndexName)); - }); - return { - ...state, - diffexpGenes: _diffexpGenes - }; - } - case "clear differential expression": { - return { - ...state, - diffexpGenes: [] - }; - } - case "clear user defined gene": { - const { userDefinedGenes } = state; - const newUserDefinedGenes = _.filter( - userDefinedGenes, - d => d !== action.data - ); - return { - ...state, - userDefinedGenes: newUserDefinedGenes - }; - } - case "clear all user defined genes": { - return { - ...state, - userDefinedGenes: [] - }; - } - case "expression load error": - case "initial data load error": { - return { - ...state, - loading: false, - error: action.error - }; - } - - /******************************* - User Events - *******************************/ - case "change graph interaction mode": - return { - ...state, - graphInteractionMode: action.data - }; - case "change opacity deselected cells in 2d graph background": - return { - ...state, - opacityForDeselectedCells: action.data - }; - case "increment graph render counter": { - const c = state.graphRenderCounter + 1; - return { - ...state, - graphRenderCounter: c - }; - } - case "interface reset started": { - return { - ...state, - resettingInterface: true - }; - } - - /******************************* - Scatterplot - *******************************/ - case "set scatterplot x": - return { - ...state, - scatterplotXXaccessor: action.data - }; - case "set scatterplot y": - return { - ...state, - scatterplotYYaccessor: action.data - }; - case "clear scatterplot": - return { - ...state, - scatterplotXXaccessor: null, - scatterplotYYaccessor: null - }; - - default: - return state; - } -}; - -export default Controls; diff --git a/client/src/reducers/crossfilter.js b/client/src/reducers/crossfilter.js deleted file mode 100644 index 06f8e30e..00000000 --- a/client/src/reducers/crossfilter.js +++ /dev/null @@ -1,215 +0,0 @@ -import _ from "lodash"; - -import Crossfilter from "../util/typedCrossfilter"; -import { World, ControlsHelpers } from "../util/stateManager"; -import { - layoutDimensionName, - obsAnnoDimensionName, - userDefinedDimensionName, - diffexpDimensionName, - makeContinuousDimensionName -} from "../util/nameCreators"; - -const XYDimName = layoutDimensionName("XY"); - -const CrossfilterReducer = ( - state = null, - action, - nextSharedState, - prevSharedState -) => { - switch (action.type) { - case "initial data load complete (universe exists)": { - const { world, layoutChoice } = nextSharedState; - const crossfilter = World.createObsDimensions( - new Crossfilter(world.obsAnnotations), - world, - layoutChoice.currentDimNames - ); - return crossfilter; - } - - case "reset World to eq Universe": { - const { userDefinedGenes, diffexpGenes } = prevSharedState.controls; - const { world } = nextSharedState; - const crossfilter = ControlsHelpers.createGeneDimensions( - userDefinedGenes, - diffexpGenes, - world, - prevSharedState.resetCache.crossfilter - ); - return crossfilter; - } - - case "set clip quantiles": - case "set World to current selection": { - const { userDefinedGenes, diffexpGenes } = prevSharedState.controls; - const { world, layoutChoice } = nextSharedState; - let crossfilter = new Crossfilter(world.obsAnnotations); - crossfilter = World.createObsDimensions( - crossfilter, - world, - layoutChoice.currentDimNames - ); - crossfilter = ControlsHelpers.createGeneDimensions( - userDefinedGenes, - diffexpGenes, - world, - crossfilter - ); - return crossfilter; - } - - case "set layout choice": { - /* - when switching layouts: - - delete the existing XY index - - add the new XY index (which implicitly selects all on it) - */ - const { world, layoutChoice } = nextSharedState; - return state - .delDimension(layoutDimensionName("XY")) - .addDimension( - layoutDimensionName("XY"), - "spatial", - world.obsLayout.col(layoutChoice.currentDimNames[0]).asArray(), - world.obsLayout.col(layoutChoice.currentDimNames[1]).asArray() - ); - } - - case "request user defined gene success": { - const { world } = prevSharedState; - const gene = action.data.genes[0]; - return state.addDimension( - userDefinedDimensionName(gene), - "scalar", - world.varData.col(gene).asArray(), - Float32Array - ); - } - - case "request differential expression success": { - const { world } = prevSharedState; - const varIndexName = world.schema.annotations.var.index; - const genes = _.map(action.data, d => - world.varAnnotations.at(d[0], varIndexName) - ); - const crossfilter = _.reduce( - genes, - (xfltr, gene) => - xfltr.addDimension( - diffexpDimensionName(gene), - "scalar", - world.varData.col(gene).asArray(), - Float32Array - ), - state - ); - return crossfilter; - } - - case "clear differential expression": { - const { world } = prevSharedState; - const varIndexName = world.schema.annotations.var.index; - const crossfilter = _.reduce( - action.diffExp, - (xfltr, values) => { - const name = world.varAnnotations.at(values[0], varIndexName); - return xfltr.delDimension(diffexpDimensionName(name)); - }, - state - ); - return crossfilter; - } - - case "clear user defined gene": { - return state.delDimension(userDefinedDimensionName(action.data)); - } - - case "clear all user defined genes": { - const { userDefinedGenes } = prevSharedState.controls; - const crossfilter = _.reduce( - userDefinedGenes, - (xfltr, gene) => xfltr.delDimension(userDefinedDimensionName(gene)), - state - ); - return crossfilter; - } - - case "graph brush end": - case "graph brush change": { - const [minX, maxY] = action.brushCoords.northwest; - const [maxX, minY] = action.brushCoords.southeast; - return state.select(XYDimName, { - mode: "within-rect", - minX, - minY, - maxX, - maxY - }); - } - - case "graph lasso end": { - const { polygon } = action; - return state.select(XYDimName, { - mode: "within-polygon", - polygon - }); - } - - case "graph lasso cancel": - case "graph brush cancel": - case "graph lasso deselect": - case "graph brush deselect": { - return state.select(XYDimName, { mode: "all" }); - } - - case "continuous metadata histogram start": - case "continuous metadata histogram brush": - case "continuous metadata histogram cancel": - case "continuous metadata histogram end": { - const name = makeContinuousDimensionName( - action.continuousNamespace, - action.selection - ); - // action.selection: metadata name being selected - // action.range: filter range, or null if deselected - if (!action.range) { - return state.select(name, { mode: "all" }); - } - const [lo, hi] = action.range; - const newState = state.select(name, { mode: "range", lo, hi }); - return newState; - } - - case "categorical metadata filter select": - case "categorical metadata filter deselect": { - const { categoricalSelection } = nextSharedState; - const { world } = prevSharedState; - const cat = categoricalSelection[action.metadataField]; - const col = world.obsAnnotations.col(action.metadataField); - return state.select(obsAnnoDimensionName(action.metadataField), { - mode: "exact", - values: ControlsHelpers.selectedValuesForCategory(cat, col) - }); - } - - case "categorical metadata filter none of these": { - return state.select(obsAnnoDimensionName(action.metadataField), { - mode: "none" - }); - } - - case "categorical metadata filter all of these": { - return state.select(obsAnnoDimensionName(action.metadataField), { - mode: "all" - }); - } - - default: { - return state; - } - } -}; - -export default CrossfilterReducer; diff --git a/client/src/reducers/differential.js b/client/src/reducers/differential.js deleted file mode 100644 index 966f8020..00000000 --- a/client/src/reducers/differential.js +++ /dev/null @@ -1,63 +0,0 @@ -// jshint esversion: 6 - -const Differential = ( - state = { - diffExp: null, - loading: null, - error: null, - celllist1: null, - celllist2: null - }, - action -) => { - switch (action.type) { - case "request differential expression started": - return { - ...state, - loading: true, - error: null - }; - case "request differential expression success": - return { - ...state, - error: null, - loading: false, - diffExp: action.data - }; - case "request differential expression error": - return { - ...state, - loading: false, - error: action.data - }; - case "store current cell selection as differential set 1": - return { - ...state, - celllist1: action.data - }; - case "store current cell selection as differential set 2": - return { - ...state, - celllist2: action.data - }; - case "clear differential expression": - return { - ...state, - diffExp: null, - celllist1: null, - celllist2: null - }; - case "reset World to eq Universe": - case "set World to current selection": - return { - ...state, - diffExp: null, - celllist1: null, - celllist2: null - }; - default: - return state; - } -}; - -export default Differential; diff --git a/client/src/reducers/graphSelection.js b/client/src/reducers/graphSelection.js deleted file mode 100644 index 00db09cb..00000000 --- a/client/src/reducers/graphSelection.js +++ /dev/null @@ -1,61 +0,0 @@ -const GraphSelection = ( - state = { - tool: "lasso", // what selection tool mode (lasso, brush, ...) - selection: { mode: "all" } // current selection, which is tool specific - }, - action -) => { - switch (action.type) { - case "set clip quantiles": - case "reset World to eq Universe": - case "set layout choice": { - return { - ...state, - selection: { - mode: "all" - } - }; - } - - case "graph brush end": - case "graph brush change": { - const { brushCoords } = action; - return { - ...state, - selection: { - mode: "within-rect", - brushCoords - } - }; - } - - case "graph lasso end": { - const { polygon } = action; - return { - ...state, - selection: { - mode: "within-polygon", - polygon - } - }; - } - - case "graph lasso cancel": - case "graph brush cancel": - case "graph lasso deselect": - case "graph brush deselect": { - return { - ...state, - selection: { - mode: "all" - } - }; - } - - default: { - return state; - } - } -}; - -export default GraphSelection; diff --git a/client/src/reducers/index.js b/client/src/reducers/index.js deleted file mode 100644 index 96ea5c23..00000000 --- a/client/src/reducers/index.js +++ /dev/null @@ -1,56 +0,0 @@ -import { createStore, applyMiddleware } from "redux"; -import thunk from "redux-thunk"; - -import cascadeReducers from "./cascade"; -import undoable from "./undoable"; -import config from "./config"; -import universe from "./universe"; -import world from "./world"; -import categoricalSelection from "./categoricalSelection"; -import continuousSelection from "./continuousSelection"; -import graphSelection from "./graphSelection"; -import crossfilter from "./crossfilter"; -import colors from "./colors"; -import differential from "./differential"; -import layoutChoice from "./layoutChoice"; -import responsive from "./responsive"; -import controls from "./controls"; -import resetCache from "./resetCache"; -import centroidLabel from "./centroidLabel"; - -import undoableConfig from "./undoableConfig"; - -const Reducer = undoable( - cascadeReducers([ - ["config", config], - ["universe", universe], - ["world", world], - ["layoutChoice", layoutChoice], - ["categoricalSelection", categoricalSelection], - ["continuousSelection", continuousSelection], - ["graphSelection", graphSelection], - ["crossfilter", crossfilter], - ["colors", colors], - ["controls", controls], - ["differential", differential], - ["responsive", responsive], - ["centroidLabel", centroidLabel], - ["resetCache", resetCache] - ]), - [ - "world", - "categoricalSelection", - "continuousSelection", - "graphSelection", - "crossfilter", - "colors", - "controls", - "differential", - "layoutChoice" - ], - undoableConfig -); - -const store = createStore(Reducer, applyMiddleware(thunk)); - -export default store; diff --git a/client/src/reducers/layoutChoice.js b/client/src/reducers/layoutChoice.js deleted file mode 100644 index 8f64d0e9..00000000 --- a/client/src/reducers/layoutChoice.js +++ /dev/null @@ -1,50 +0,0 @@ -/* -we have a UI heuristic to pick the default layout, based on assumptions -about commonly used names. Preferentially, pick in the following order: - - 1. "umap" - 2. "tsne" - 3. "pca" - 4. give up, use the first available -*/ -function bestDefaultLayout(layouts) { - const preferredNames = ["umap", "tsne", "pca"]; - const idx = preferredNames.findIndex(name => layouts.indexOf(name) !== -1); - if (idx !== -1) return preferredNames[idx]; - return layouts[0]; -} - -const LayoutChoice = ( - state = { - available: [], // all available choices - current: undefined, // name of the current layout, eg, 'umap' - currentDimNames: [] // dimension name - }, - action, - nextSharedState -) => { - switch (action.type) { - case "initial data load complete (universe exists)": - case "reset World to eq Universe": { - // set default to default - const { schema } = nextSharedState.world; - const available = schema.layout.obs.map(v => v.name); - const current = bestDefaultLayout(available); - const currentDimNames = schema.layout.obsByName[current].dims; - return { available, current, currentDimNames }; - } - - case "set layout choice": { - const { schema } = nextSharedState.world; - const current = action.layoutChoice; - const currentDimNames = schema.layout.obsByName[current].dims; - return { ...state, current, currentDimNames }; - } - - default: { - return state; - } - } -}; - -export default LayoutChoice; diff --git a/client/src/reducers/resetCache.js b/client/src/reducers/resetCache.js deleted file mode 100644 index 9a4c2df1..00000000 --- a/client/src/reducers/resetCache.js +++ /dev/null @@ -1,31 +0,0 @@ -/* -Reducer which caches derived state to be used in a reset or other -recomputation. - -Currently this only caches the baseline (full universe) world & crossfilter, -for use in a Reset. -*/ -const ResetCacheReducer = ( - state = { - world: null, - crossfilter: null - }, - action, - nextSharedState -) => { - switch (action.type) { - case "initial data load complete (universe exists)": { - const { world, crossfilter } = nextSharedState; - return { - ...state, - world, - crossfilter - }; - } - default: { - return state; - } - } -}; - -export default ResetCacheReducer; diff --git a/client/src/reducers/responsive.js b/client/src/reducers/responsive.js deleted file mode 100644 index 73587f16..00000000 --- a/client/src/reducers/responsive.js +++ /dev/null @@ -1,21 +0,0 @@ -// jshint esversion: 6 -const Responsive = ( - state = { - width: null, - height: null - }, - action -) => { - switch (action.type) { - case "window resize": - return { - ...state, - width: action.data.width, - height: action.data.height - }; - default: - return state; - } -}; - -export default Responsive; diff --git a/client/src/reducers/undoable.js b/client/src/reducers/undoable.js deleted file mode 100644 index 3a435f34..00000000 --- a/client/src/reducers/undoable.js +++ /dev/null @@ -1,287 +0,0 @@ -/* -A redo/undo meta reducer for Redux. Designed to work well with the cascadeReducer(). - -Requires three parameters: -* reducer - a reducer, which MUST return an object as state. -* undoableKeys - an array of object keys (strings). If any of these keys - are in the object/state returned by the reducer, they will be treated as - state to be made "undoable". -* options - an optional object, which may contain the following parameters: - * historyLimit: max number of historical states to remember (aka max undo depth) - * actionFilter: filter function, (state, action, filterState) => value. - See below for details. - * debug: if truish, will print helpful log messages about history manipulation - -This meta reducer accepts three actions types: -* @@undoable/undo - move back in history -* @@undoable/redo - move forward in history -* @@undoable/clear - clear history - ---- - -Action filter - controls the undoable reducer side-effects. If not -specified, the action filter defaults to "save", ie, pushes a redo -point upon each action. - -The action filter callback has access to the current action, the entire -undoable reducer state, and any state it wants to manage ("filterState"). -This filter state will be passed to each action filter call, and any -value returned (via @@undoable/filterState field described below) will be -MERGED into the current filter state. - -An object must be returned (the "undoable action"), indicating desired -history state processing. The undoable action object contents, by key: - - @@undoable/filterAction: required. Can be one of: - "skip" - reduce the current action, but no other side effects. - Same as returning false. - "clear" - reduce the current action, and clear history state. - "save" - push the previous state onto the history stack (ie, - before reducing the action) - "stashPending" - reduce action, save state as pending. Does not - not commit it to history. Along with cancelPending and applyPending, - can be used to delay commit of history (eg, for multi-action - groupings, asynch operations, etc). - "cancelPending" - reduce action, cancel any pending state save. - "applyPending" - commit any pending state to the history stack, - then reduce action. - - @@undoable/filterState: optional. If this value is set, it will be - MERGED into the current filter state. The value and semantics of any - filter state are entirely at the discretion of the action filter. - -*/ -import fromEntries from "../util/fromEntries"; - -const historyKeyPrefix = "@@undoable/"; -const pastKey = `${historyKeyPrefix}past`; -const futureKey = `${historyKeyPrefix}future`; -const filterStateKey = `${historyKeyPrefix}filterState`; -const filterActionKey = `${historyKeyPrefix}filterAction`; -const pendingKey = `${historyKeyPrefix}pending`; -const defaultHistoryLimit = -100; - -const Undoable = (reducer, undoableKeys, options = {}) => { - const { debug } = options; - let { historyLimit } = options; - if (!historyLimit) historyLimit = defaultHistoryLimit; - if (historyLimit > 0) historyLimit = -historyLimit; - const actionFilter = - options.actionFilter || (() => ({ [filterActionKey]: "save" })); - - if (!Array.isArray(undoableKeys) || undoableKeys.length === 0) - throw new Error("undoable keys array must be specified"); - const undoableKeysSet = new Set(undoableKeys); - - /* - Undo the current to previous history - */ - function undo(currentState) { - const past = currentState[pastKey]; - const future = currentState[futureKey]; - if (past.length === 0) return currentState; - const currentUndoableState = Object.entries(currentState).filter(kv => - undoableKeysSet.has(kv[0]) - ); - const newPast = [...past]; - const newState = newPast.pop(); - const newFuture = push(future, currentUndoableState); - const nextState = { - ...currentState, - ...fromEntries(newState), - [pastKey]: newPast, - [futureKey]: newFuture, - [pendingKey]: null - }; - return nextState; - } - - /* - Replay future, previously undone. - */ - function redo(currentState) { - const past = currentState[pastKey] || []; - const future = currentState[futureKey] || []; - if (future.length === 0) return currentState; - const currentUndoableState = Object.entries(currentState).filter(kv => - undoableKeysSet.has(kv[0]) - ); - const newFuture = [...future]; - const newState = newFuture.pop(); - const newPast = push(past, currentUndoableState); - const nextState = { - ...currentState, - ...fromEntries(newState), - [pastKey]: newPast, - [futureKey]: newFuture, - [pendingKey]: null - }; - return nextState; - } - - /* - Clear the history state. No side-effects on current state. - */ - function clear(currentState) { - return { - ...currentState, - [pastKey]: [], - [futureKey]: [], - [filterStateKey]: {}, - [pendingKey]: null - }; - } - - /* - Reduce current action, with no history side-effects - */ - function skip(currentState, action, filterState) { - const past = currentState[pastKey] || []; - const pending = currentState[pendingKey]; - const res = reducer(currentState, action); - return { - ...res, - [pastKey]: past, - [futureKey]: [], - [filterStateKey]: filterState, - [pendingKey]: pending - }; - } - - /* - Save current state in the history, then reduce action. - */ - function save(currentState, action, filterState) { - const past = currentState[pastKey] || []; - const currentUndoableState = Object.entries(currentState).filter(kv => - undoableKeysSet.has(kv[0]) - ); - const res = reducer(currentState, action); - const newPast = push(past, currentUndoableState, historyLimit); - const nextState = { - ...res, - [pastKey]: newPast, - [futureKey]: [], - [filterStateKey]: filterState, - [pendingKey]: null - }; - return nextState; - } - - /* - Save current state as pending history change. No other side effects. - */ - function stashPending(currentState) { - const currentUndoableState = Object.entries(currentState).filter(kv => - undoableKeysSet.has(kv[0]) - ); - return { - ...currentState, - [pendingKey]: currentUndoableState - }; - } - - /* - Cancel pending history state change. No other side effects. - */ - function cancelPending(currentState) { - return { - ...currentState, - [pendingKey]: null - }; - } - - /* - Push pending state onto the history stack - */ - function applyPending(currentState) { - const past = currentState[pastKey] || []; - const pendingState = currentState[pendingKey]; - const newPast = push(past, pendingState, historyLimit); - const nextState = { - ...currentState, - [pastKey]: newPast, - [futureKey]: [], - [pendingKey]: null - }; - return nextState; - } - - return ( - currentState = { - [pastKey]: [], - [futureKey]: [], - [filterStateKey]: {}, - [pendingKey]: null - }, - action - ) => { - const aType = action.type; - switch (aType) { - case "@@undoable/undo": { - return undo(currentState, action); - } - - case "@@undoable/redo": { - return redo(currentState, action); - } - - case "@@undoable/clear": { - return clear(currentState, action); - } - - default: { - const currentFilterState = currentState[filterStateKey]; - const actionFilterResp = actionFilter( - currentState, - action, - currentFilterState - ); - const { - [filterActionKey]: filterAction, - [filterStateKey]: filterStateUpdate - } = actionFilterResp; - const nextFilterState = { ...currentFilterState, ...filterStateUpdate }; - - switch (filterAction) { - case "clear": - if (debug) console.log("---- CLEAR HISTO", action.type); - return clear(skip(currentState, action, nextFilterState)); - - case "save": - if (debug) console.log("---- SAVE HISTO", action.type); - return save(currentState, action, nextFilterState); - - case "stashPending": - if (debug) console.log("---- STASH PENDING", action.type); - return skip(stashPending(currentState), action, nextFilterState); - - case "cancelPending": - if (debug) console.log("---- CANCEL PENDING", action.type); - return skip(cancelPending(currentState), action, nextFilterState); - - case "applyPending": - if (debug) console.log("---- APPLY PENDING", action.type); - return skip(applyPending(currentState), action, nextFilterState); - - case "skip": - default: - return skip(currentState, action, nextFilterState); - } - } - } - }; -}; - -function push(arr, val, limit = undefined) { - /* - functional array push, with a max length limit to the new array. - Like Array.push, except it returns new array and discards as needed - to enforce the length limit. - */ - const narr = arr.slice(limit); - narr.push(val); - return narr; -} - -export default Undoable; diff --git a/client/src/reducers/undoableConfig.js b/client/src/reducers/undoableConfig.js deleted file mode 100644 index 76c59228..00000000 --- a/client/src/reducers/undoableConfig.js +++ /dev/null @@ -1,263 +0,0 @@ -import StateMachine from "../util/statemachine"; -import createFsmTransitions from "./undoableFsm"; - -const actionKey = "@@undoable/filterAction"; -const stateKey = "@@undoable/filterState"; - -/* -these actions will not affect history -*/ -const skipOnActions = new Set([ - "url changed", - "interface reset started", - "initial data load start", - "configuration load complete", - "increment graph render counter", - "window resize", - "user reset start", - "reset colorscale", - - "graph brush change", - "continuous metadata histogram brush", - - "expression load start", - "expression load success", - "expression load error", - - "request user defined gene started", - "request user defined gene success", - "clear all user defined genes", - - "get single gene expression for coloring started", - "get single gene expression for coloring error", - - "category value mouse hover start", - "category value mouse hover end" -]); - -/* -identical, repeated occurances of these action types will be debounced. -Entire action must be identical (all keys). -*/ -const debounceOnActions = new Set([ - "color by categorical metadata", - "color by continuous metadata", - "color by expression" -]); - -/* -history will be cleared when these actions occur -*/ -const clearOnActions = new Set([ - "initial data load complete (universe exists)", - "reset World to eq Universe", - "initial data load error", - "user reset end" -]); - -/* -An immediate history save will be done for these -*/ -const saveOnActions = new Set([ - "categorical metadata filter select", - "categorical metadata filter deselect", - "categorical metadata filter all of these", - "categorical metadata filter none of these", - - "color by categorical metadata", - "color by continuous metadata", - "color by expression", - - "set scatterplot x", - "set scatterplot y", - - "store current cell selection as differential set 1", - "store current cell selection as differential set 2", - - "set World to current selection", - "set clip quantiles", - - "set layout choice", - "change graph interaction mode" -]); - -/** -StateMachine - processing complex action handling - see FSM graph for -actual structure, in undoableFsm.js -**/ - -/* -Default FSM actions. Used to side-effect transitions in the graph. -See graph definition for the transitions that use each. - -Signature: (fsm, transition, reducerState, reducerAction) => undoableAction -*/ -const stashPending = fsm => ({ - [actionKey]: "stashPending", - [stateKey]: { fsm } -}); -const cancelPending = () => ({ - [actionKey]: "cancelPending", - [stateKey]: { fsm: null } -}); -const applyPending = () => ({ - [actionKey]: "applyPending", - [stateKey]: { fsm: null } -}); -const skip = (fsm, transition) => ({ - [actionKey]: "skip", - [stateKey]: { fsm: transition.to !== "done" ? fsm : null } -}); -const clear = () => ({ [actionKey]: "clear", [stateKey]: { fsm: null } }); -const save = (fsm, transition) => ({ - [actionKey]: "save", - [stateKey]: { fsm: transition.to !== "done" ? fsm : null } -}); - -/* -Error handler for state transitions that are unexpected. Called by -StateMachine when it doesn't know what to do. - -Signature: (fsm, event, from) => undoableAction -*/ -const onFsmError = (fsm, event, from) => { - console.error(`FSM error [event: "${event}", state: "${from}"]`, fsm); - // In production, try to recover gracefully if we have unexpected state - return clear(fsm); -}; - -/* -Definition of the transition graph mapping action types to history side effects. -*/ -const fsmTransitions = createFsmTransitions( - stashPending, - cancelPending, - applyPending, - skip, - clear, - save -); -/* State machine we clone whenever we need to run it */ -const seedFsm = new StateMachine("init", fsmTransitions, onFsmError); - -/* -See undoable.js for description action filter interface description. - -Basic approach: - * trivial handlers for skip, clear & save cases to keep config simple. - * only implement complex state machines where absolutely required (eg, - multi-event seleciton and the like) -*/ -const actionFilter = debug => (state, action, prevFilterState) => { - const actionType = action.type; - const filterState = { - ...prevFilterState, - prevAction: action - }; - if (skipOnActions.has(actionType)) { - return { [actionKey]: "skip", [stateKey]: filterState }; - } - if ( - debounceOnActions.has(actionType) && - shallowObjectEq(action, prevFilterState.prevAction) - ) { - return { [actionKey]: "skip", [stateKey]: filterState }; - } - if (clearOnActions.has(actionType)) { - return { [actionKey]: "clear", [stateKey]: filterState }; - } - if (saveOnActions.has(actionType)) { - return { [actionKey]: "save", [stateKey]: filterState }; - } - - /* - Else, something more complex OR unknown to us.... - */ - if (seedFsm.events.has(actionType)) { - let { fsm } = filterState; - if (!fsm) { - /* no active FSM, so create one in init state */ - fsm = seedFsm.clone("init"); - } - return fsm.next(action.type, { state, action }); - } - - /* else, we have no idea what this is - skip it */ - if (debug) console.log("**** ACTION FILTER EVENT HANDLER MISS", actionType); - return { [actionKey]: "skip", [stateKey]: filterState }; -}; - -/* -return true if objA and objB are ===, OR if: - - are both objects and not null - - have same own properties - - all values are strict equal (===) -*/ -function shallowObjectEq(objA, objB) { - if (objA === objB) return true; - if (!objA || !objB) return false; - if (!shallowArrayEq(Object.keys(objA), Object.keys(objB))) return false; - if (!shallowArrayEq(Object.values(objA), Object.values(objB))) return false; - return true; -} - -/* -return true if arrA and arrB contain the same strict-equal values, -in the same order. -*/ -function shallowArrayEq(arrA, arrB) { - if (arrA.length !== arrB.length) return false; - for (let i = 0, l = arrA.length; i < l; i += 1) { - if (arrA[i] !== arrB[i]) return false; - } - return true; -} - -/* configuration for the undoable meta reducer */ -const debug = false; // set truish for undoble debugging -const undoableConfig = { - debug, - historyLimit: 50, // maximum history size - actionFilter: actionFilter(debug) -}; - -/* -this code is strictly for sanity checking configuration, and is only -enabled when we are debugging the undoable configuration (ie, debug === true). -*/ -if (debug) { - /* - Confirm no intersection between the various trivial rejection action filters - */ - if ( - new Set([...skipOnActions].filter(x => clearOnActions.has(x))).size > 0 || - new Set([...skipOnActions].filter(x => saveOnActions.has(x))).size > 0 || - new Set([...clearOnActions].filter(x => saveOnActions.has(x))).size > 0 - ) { - console.error( - "Undoable misconfiguration - action filters have redundant events" - ); - } - - /* - Confirm that no FSM events are blocked by a trivial rejection filter. - If this occurs, the FSM can't ever see the events needed to process - state transitions. - */ - const trivialFilters = new Set([ - ...skipOnActions, - ...clearOnActions, - ...saveOnActions - ]); - const trivialOverlapWithFsm = new Set( - [...trivialFilters].filter(x => seedFsm.events.has(x)) - ); - if (trivialOverlapWithFsm.size > 0) { - console.error( - "Undoable misconfiguration - trivival action filter blocking FSM filter", - [...trivialOverlapWithFsm] - ); - } -} - -export default undoableConfig; diff --git a/client/src/reducers/undoableFsm.js b/client/src/reducers/undoableFsm.js deleted file mode 100644 index 2e4d8e6f..00000000 --- a/client/src/reducers/undoableFsm.js +++ /dev/null @@ -1,206 +0,0 @@ -/* -State transition graph for complex action/history interactions. - -Assumed configuration from undoableConfig: - * By convention, "init" is used as the start state for all, and "done" - as the final state. - * Unexpected states will result in an error, plus a clear and cancelPending - side-effect. - -TODO: is is possible there is a more concise format for this, as it is -a fairly repetitive pattern. - -These events are largely one of two types: -a) async operations or multi-event options that should only be committed -upon some success criteria, otherwise cancelled. - -b) compound actions that should be collapsed into a single history change. - -*/ - -const createFsmTransitions = ( - stashPending, - cancelPending, - applyPending, - skip, - clear, - save -) => { - return [ - /* graph selection brushing */ - { - event: "graph brush start", - from: "init", - to: "graph brush in progress", - action: stashPending - }, - { - event: "graph brush cancel", - from: "graph brush in progress", - to: "done", - action: applyPending - }, - { - event: "graph brush deselect", - from: "graph brush in progress", - to: "done", - /* if current selection is all, cancelPending. Else, applyPending */ - action: (fsm, transition, data) => - data.state.graphSelection.selection.mode === "all" - ? cancelPending() - : applyPending() - }, - { - event: "graph brush end", - from: "graph brush in progress", - to: "done", - action: applyPending - }, - - /* graph selection lasso */ - { - event: "graph lasso start", - from: "init", - to: "graph lasso in progress", - action: stashPending - }, - { - event: "graph lasso cancel", - from: "graph lasso in progress", - to: "done", - action: applyPending - }, - { - event: "graph lasso deselect", - from: "graph lasso in progress", - to: "done", - /* if current selection is all, cancelPending. Else, applyPending */ - action: (fsm, transition, data) => - data.state.graphSelection.selection.mode === "all" - ? cancelPending() - : applyPending() - }, - { - event: "graph lasso end", - from: "graph lasso in progress", - to: "done", - action: applyPending - }, - - /* Continuous metadata histogram brush selection */ - { - event: "continuous metadata histogram start", - from: "init", - to: "continuous histo select in progress", - action: stashPending - }, - { - event: "continuous metadata histogram cancel", - from: "continuous histo select in progress", - to: "done", - action: cancelPending - }, - { - event: "continuous metadata histogram end", - from: "continuous histo select in progress", - to: "done", - action: applyPending - }, - - /* Single gene request by user */ - { - event: "single user defined gene start", - from: "init", - to: "single user gene request in progress", - action: stashPending - }, - { - event: "request user defined gene error", - from: "single user gene request in progress", - to: "single user gene error in progress", - action: skip - }, - { - event: "single user defined gene error", - from: "single user gene error in progress", - to: "done", - action: cancelPending - }, - { - event: "single user defined gene complete", - from: "single user gene request in progress", - to: "done", - action: applyPending - }, - - /* Bulk gene request by user */ - { - event: "bulk user defined gene start", - from: "init", - to: "bulk user gene request in progress", - action: stashPending - }, - { - event: "request user defined gene error", - from: "bulk user gene request in progress", - to: "bulk user gene request error in progress", - action: skip - }, - { - event: "bulk user defined gene error", - from: "bulk user gene request error in progress", - to: "done", - action: cancelPending - }, - { - event: "bulk user defined gene complete", - from: "bulk user gene request in progress", - to: "done", - action: applyPending - }, - - /* Compute Differential Expression button user action */ - { - event: "request differential expression started", - from: "init", - to: "diffexp in progress", - action: stashPending - }, - { - event: "request user defined gene error", - from: "diffexp in progress", - to: "done", - action: cancelPending - }, - { - event: "request differential expression success", - from: "diffexp in progress", - to: "done", - action: applyPending - }, - - /* Clear Differential Expression button user action */ - { - event: "clear differential expression", - from: "init", - to: "CDE Button in progress", - action: stashPending - }, - { - event: "clear scatterplot", - from: "CDE Button in progress", - to: "done", - action: applyPending - }, - - /* clear scatter plot button (eg, on scatterplot view) */ - { - event: "clear scatterplot", - from: "init", - to: "done", - action: save - } - ]; -}; - -export default createFsmTransitions; diff --git a/client/src/reducers/universe.js b/client/src/reducers/universe.js deleted file mode 100644 index b12e2b55..00000000 --- a/client/src/reducers/universe.js +++ /dev/null @@ -1,47 +0,0 @@ -import { ControlsHelpers } from "../util/stateManager"; - -const Universe = (state = null, action, nextSharedState, prevSharedState) => { - switch (action.type) { - case "initial data load complete (universe exists)": { - const { universe } = action; - return universe; - } - - case "expression load success": { - let { varData } = state; - - // Lazy load new expression data into the varData dataframe, if - // not already present. - // - Object.entries(action.expressionData).forEach(([key, val]) => { - // If not already in universe.varData, save entire expression column - if (!varData.hasCol(key)) { - varData = varData.withCol(key, val); - } - }); - - // Prune size of varData "cache" if getting out of hand.... - // - const { userDefinedGenes, diffexpGenes } = prevSharedState; - const allTheGenesWeNeed = [ - ...new Set( - userDefinedGenes, - diffexpGenes, - Object.keys(action.expressionData) - ) - ]; - varData = ControlsHelpers.pruneVarDataCache(varData, allTheGenesWeNeed); - - return { - ...state, - varData - }; - } - - default: { - return state; - } - } -}; - -export default Universe; diff --git a/client/src/reducers/world.js b/client/src/reducers/world.js deleted file mode 100644 index f0742687..00000000 --- a/client/src/reducers/world.js +++ /dev/null @@ -1,131 +0,0 @@ -import { World, ControlsHelpers } from "../util/stateManager"; -import clip from "../util/clip"; -import quantile from "../util/quantile"; - -const WorldReducer = ( - state = null, - action, - nextSharedState, - prevSharedState -) => { - switch (action.type) { - case "initial data load complete (universe exists)": { - const { universe } = nextSharedState; - const world = World.createWorldFromEntireUniverse(universe); - return world; - } - - case "reset World to eq Universe": { - return prevSharedState.resetCache.world; - } - - case "set World to current selection": { - /* Set viewable world to be the currently selected data */ - const world = World.createWorldBySelection( - action.universe, - action.world, - action.crossfilter - ); - return world; - } - - case "set clip quantiles": { - const world = World.createWorldWithNewClip( - prevSharedState.universe, - state, - prevSharedState.crossfilter, - action.clipQuantiles - ); - return world; - } - - case "expression load success": { - const { universe } = nextSharedState; - const universeVarData = universe.varData; - let unclippedVarData = state.unclipped.varData; - - // Lazy load new expression data into the unclipped varData dataframe, if - // not already present. - // - Object.entries(action.expressionData).forEach(([key, val]) => { - // If not already in world.varData, save sliced expression column - if (!unclippedVarData.hasCol(key)) { - // Slice if world !== universe, else just use whole column. - // Use the obsAnnotation index as the cut key, as we keep - // all world dataframes in sync. - let worldValSlice = val; - if (!World.worldEqUniverse(state, universe)) { - worldValSlice = universeVarData - .subset(state.obsAnnotations.rowIndex.keys(), [key], null) - .icol(0) - .asArray(); - } - - // Now build world's varData dataframe - unclippedVarData = unclippedVarData.withCol( - key, - worldValSlice, - state.obsAnnotations.rowIndex - ); - } - }); - - // Prune size of varData unclipped dataframe if getting out of hand.... - // - const { userDefinedGenes, diffexpGenes } = prevSharedState; - const allTheGenesWeNeed = [ - ...new Set( - userDefinedGenes, - diffexpGenes, - Object.keys(action.expressionData) - ) - ]; - unclippedVarData = ControlsHelpers.pruneVarDataCache( - unclippedVarData, - allTheGenesWeNeed - ); - - // at this point, we have the unclipped data in unclippedVarData. - // Now create clipped. - // - Drop columns no longer needed - // - Add new columns - // - let clippedVarData = state.varData; - const keysToDrop = clippedVarData.colIndex - .keys() - .filter(k => !unclippedVarData.hasCol(k)); - const keysToAdd = unclippedVarData.colIndex - .keys() - .filter(k => !clippedVarData.hasCol(k)); - keysToDrop.forEach(k => { - clippedVarData = clippedVarData.dropCol(k); - }); - keysToAdd.forEach(k => { - const data = unclippedVarData.col(k).asArray(); - const q = [state.clipQuantiles.min, state.clipQuantiles.max]; - const [qMinVal, qMaxVal] = quantile(q, data); - const clippedData = clip(data, qMinVal, qMaxVal, Number.NaN); - clippedVarData = clippedVarData.withCol( - k, - clippedData, - state.obsAnnotations.rowIndex - ); - }); - - return { - ...state, - varData: clippedVarData, - unclipped: { - ...state.unclipped, - varData: unclippedVarData - } - }; - } - - default: { - return state; - } - } -}; - -export default WorldReducer; diff --git a/client/src/util/actionHelpers.js b/client/src/util/actionHelpers.js deleted file mode 100644 index c7e23267..00000000 --- a/client/src/util/actionHelpers.js +++ /dev/null @@ -1,119 +0,0 @@ -import _ from "lodash"; -/* XXX: cough, cough, ... */ -import { postNetworkErrorToast } from "../components/framework/toasters"; - -/* -dispatch an action error to the user. Currently we use -async toasts. -*/ -export const dispatchNetworkErrorMessageToUser = message => - postNetworkErrorToast(message); - -/* -Catch unexpected errors and make sure we don't lose them! -*/ -export function catchErrorsWrap(fn, dispatchToUser = false) { - return (dispatch, getState) => { - fn(dispatch, getState).catch(error => { - console.error(error); - if (dispatchToUser) { - dispatchNetworkErrorMessageToUser(error.message); - } - dispatch({ type: "UNEXPECTED ERROR", error }); - }); - }; -} - -/* -Wrapper to perform async fetch with some modest error handling -and decoding. -*/ -const doFetch = async (url, acceptType) => { - const res = await fetch(url, { - method: "get", - headers: new Headers({ - Accept: acceptType - }) - }); - if (res.ok && res.headers.get("Content-Type").includes(acceptType)) { - return res; - } - // else an error - let msg = `Unexpected HTTP response ${res.status}, ${res.statusText}`; - const body = await res.text(); - if (body && body.length > 0) { - msg = `${msg} -- ${body}`; - } - dispatchNetworkErrorMessageToUser(msg); - throw new Error(msg); -}; - -/* -Wrapper to perform an async fetch and JSON decode response. -*/ -export const doJsonRequest = async url => { - const res = await doFetch(url, "application/json"); - return res.json(); -}; - -/* -Wrapper to perform an async fetch for binary data. -*/ -export const doBinaryRequest = async url => { - const res = await doFetch(url, "application/octet-stream"); - return res.arrayBuffer(); -}; - -/* -This function "packs" filter index lists into the more efficient -"range" form specified in the REST 0.2 spec. - -Specifically, it turns an array of indices [0, 1, 2, 10, 11, 14, ...] -into a form that encodes runs of consecutive numbers as [min, max]. -Array may not be sorted, but will only contain uniq values. - -Parameters: - indices - input array of numbers (index) - minRangeLength - hint, min range length before it is encoded into range format. - sorted - boolean hint indicating array is presorted, ascending order - -So [1, 2, 3, 4, 10, 11, 14] -> [ [1, 4], [10, 11], 14] -*/ -export const rangeEncodeIndices = ( - indices, - minRangeLength = 3, - sorted = false -) => { - if (indices.length === 0) { - return indices; - } - - if (!sorted) { - indices = _.sortBy(indices); - } - - const result = new Array(indices.length); - let resultTail = 0; - - let i = 0; - while (i < indices.length) { - const begin = indices[i]; - let current; - do { - current = indices[i]; - i += 1; - } while (i < indices.length && indices[i] === current + 1); - - if (current - begin + 1 >= minRangeLength) { - result[resultTail] = [begin, current]; - resultTail += 1; - } else { - for (let j = begin; j <= current; j += 1, resultTail += 1) { - result[resultTail] = j; - } - } - } - - result.length = resultTail; - return result; -}; diff --git a/client/src/util/camera.js b/client/src/util/camera.js deleted file mode 100644 index 44980d83..00000000 --- a/client/src/util/camera.js +++ /dev/null @@ -1,66 +0,0 @@ -// jshint esversion: 6 -const createCamera = require("orbit-camera"); -const createScroll = require("scroll-speed"); -const mp = require("mouse-position"); -const mb = require("mouse-pressed"); -const key = require("key-pressed"); - -const panSpeed = 1.0; // changed from 0.4 to 1.0 per issue #722 -const scaleSpeed = 0.5; -const scaleMax = 3; -const scaleMin = 1.03; - -function attachCamera(canvas, opts) { - opts = opts || {}; - opts.pan = opts.pan !== false; - opts.scale = opts.scale !== false; - opts.rotate = opts.rotate !== false; - - const scroll = createScroll(canvas, opts.scale); - const mbut = mb(canvas, opts.rotate); - const mpos = mp(canvas); - const camera = createCamera([0, 0, 1], [0, 0, -1], [0, 1, 0]); - - camera.tick = tick; - - return camera; - - function tick() { - const ctrl = key("") || key(""); - const alt = key(""); - const { height, width } = canvas; - - if (opts.rotate && mbut.left && ctrl && !alt) { - camera.rotate( - [mpos.x / width - 0.5, mpos.y / height - 0.5], - [mpos.prevX / width - 0.5, mpos.prevY / height - 0.5] - ); - } - - if ((opts.pan && mbut.right) || (mbut.left && !ctrl && !alt)) { - camera.pan([ - ((panSpeed * (mpos[0] - mpos.prev[0])) / width) * camera.distance, - ((panSpeed * (mpos[1] - mpos.prev[1])) / height) * camera.distance - ]); - } - - if (opts.scale && scroll[1]) { - camera.distance *= Math.exp((scroll[1] * scaleSpeed) / height); - } - - if (opts.scale && (mbut.middle || (mbut.left && !ctrl && alt))) { - const d = mpos.y - mpos.prevY; - if (!d) return; - - camera.distance *= Math.exp(d / height); - } - - if (camera.distance > scaleMax) camera.distance = scaleMax; - if (camera.distance < scaleMin) camera.distance = scaleMin; - - scroll.flush(); - mpos.flush(); - } -} - -export default attachCamera; diff --git a/client/src/util/centroid.js b/client/src/util/centroid.js deleted file mode 100644 index 2cb1b60e..00000000 --- a/client/src/util/centroid.js +++ /dev/null @@ -1,58 +0,0 @@ -import quantile from "./quantile"; - -/* - Centroid coordinate calculation -*/ -const calcMeanCentroid = (world, annoName, annoValue, layoutDimNames) => { - const centroid = { x: 0, y: 0, size: 0 }; - const annoArray = world.obsAnnotations.col(annoName).asArray(); - const layoutXArray = world.obsLayout.col(layoutDimNames[0]).asArray(); - const layoutYArray = world.obsLayout.col(layoutDimNames[1]).asArray(); - - for (let i = 0, len = annoArray.length; i < len; i += 1) { - if (annoArray[i] === annoValue) { - centroid.x += layoutXArray[i]; - centroid.y += layoutYArray[i]; - centroid.size += 1; - } - } - - if (centroid[2] !== 0) { - centroid.x /= centroid.size; - centroid.y /= centroid.size; - } - - return [centroid.x, centroid.y]; -}; - -const calcMedianCentroid = (world, annoName, annoValue, layoutDimNames) => { - const centroidX = []; - const centroidY = []; - let hasFinite = false; - - const annoArray = world.obsAnnotations.col(annoName).asArray(); - const layoutXArray = world.obsLayout.col(layoutDimNames[0]).asArray(); - const layoutYArray = world.obsLayout.col(layoutDimNames[1]).asArray(); - - for (let i = 0, len = annoArray.length; i < len; i += 1) { - if (annoArray[i] === annoValue) { - hasFinite = - Number.isFinite(layoutXArray[i]) || Number.isFinite(layoutYArray[i]) - ? true - : hasFinite; - centroidX.push(layoutXArray[i]); - centroidY.push(layoutYArray[i]); - } - } - - if (hasFinite) { - const medianX = quantile([0.5], Float64Array.from(centroidX)); - const medianY = quantile([0.5], Float64Array.from(centroidY)); - - return [medianX, medianY]; - } - - return null; -}; - -export default calcMedianCentroid; diff --git a/client/src/util/clip.js b/client/src/util/clip.js deleted file mode 100644 index 5cf915ad..00000000 --- a/client/src/util/clip.js +++ /dev/null @@ -1,25 +0,0 @@ -/* -clip - clip all values in a Array or TypedArray, IN PLACE. - -Values in array are clipped if less than `lower` or greater than `upper`. - -If `setTo` is undefined, values less than `lower` will be set to `lower`, -and values greater than `upper` will be set to `upper`. - -If `setTo` is not undefined, values outside the [lower, upper] range will be set to -`setTo`. - -*/ -export default function clip(arr, lower, upper, setTo) { - const lowerSet = setTo === undefined ? lower : setTo; - const upperSet = setTo === undefined ? upper : setTo; - for (let i = 0, l = arr.length; i < l; i += 1) { - const v = arr[i]; - if (v < lower) { - arr[i] = lowerSet; - } else if (v > upper) { - arr[i] = upperSet; - } - } - return arr; -} diff --git a/client/src/util/dataframe/dataframe.js b/client/src/util/dataframe/dataframe.js deleted file mode 100644 index d7ad7f52..00000000 --- a/client/src/util/dataframe/dataframe.js +++ /dev/null @@ -1,629 +0,0 @@ -import { IdentityInt32Index, isLabelIndex } from "./labelIndex"; -// weird cross-dependency that we should clean up someday... -import { sortArray } from "../typedCrossfilter/sort"; -import { isTypedArray, isArrayOrTypedArray, callOnceLazy } from "./util"; -import { summarizeContinuous, summarizeCategorical } from "./summarize"; - -/* -Dataframe is an immutable 2D matrix similiar to Python Pandas Dataframe, -but (currently) without all of the surrounding support functions. -Data is stored in column-major layout, and each column is monomorphic. - -It supports: -* Relatively efficient creation, cloning and subsetting -* Very efficient columnar access (eg, sum down a column), and access - to the underlying column arrays. -* Data access by row/col offset or label. Labels are reasonably well - optimized for both numeric lables and arbitrary (eg, sting) labels. - -It does not currently support: -* Views on matrix subset - for currently known access patterns, - it is more effiicent to copy on subsetting, optimizing for access - speed over memory use. -* JS iterators - they are too slow. Use explicit iteration over - offest or labels. - -Important assumptions embedded in the API: -* Columns are implicitly categorical if they are a JS Array and numeric - (aka continuous) if they are a TypedArray. - -There are three index types for row/col indexing: -* IdentityInt32Index - noop index, where the index label is the offset. -* KeyIndex - index arbitrary JS objects. -* DenseInt32Index - integer indexing. Optimization over KeyIndex as it uses - Int32Array as a back-map to offsets. This means that the index array - must be sized to [minLabel, maxLabel), so this is only useful when the label - range is relatively close the underlying offset range [minOffset, maxOffset). - -All private functions/methods/fields are prefixed by '__', eg, __compile(). -Don't use them outside of this file. - -Simple example: - - // default indexing is integer offset. - const df = Dataframe.create([2,2], [['a', 'b'], [0, 1]]) - console.log(df.at(0,0)); // outputs: a - console.log(df.col(1).asArray()); // outputs: [0, 1] - - // KeyIndex - const df = new Dataframe([1,2], [['a'], ['b']], null, new KeyIndex(['A', 'B'])) - console.log(df.at(0, 'A')); // outputs: a - console.log(df.col('A').asArray(); // outputs: ['a'] - -Performance tuning is primarily focused on columnar access patterns, which is the -dominant pattern in cellxgene. -*/ - -/** -Dataframe -**/ - -class Dataframe { - /** - Constructors & factories - **/ - - constructor( - dims, - columnarData, - rowIndex = null, - colIndex = null, - __columnsAccessor = [] // private interface - ) { - /* - The base constructor is relatively hard to use - as an alternative, - see factory methods and clone/slice, below. - - Parameters: - * dims - 2D array describing intendend dimensionality: [nRows,nCols]. - * columnarData - JS array, nCols in length, containing array - or TypedArray of length nRows. - * rowIndex/colIndex - null (create default index using offsets as key), - or a caller-provided index. - * __columnsAccessor - private interface, do not specify. Used internally - to improve caching of column accessors when possible (eg, clone(), - dropCol(), withCol()). - All columns and indices must have appropriate dimensionality. - */ - const [nRows, nCols] = dims; - if (nRows < 0 || nCols < 0) { - throw new RangeError("Dataframe dimensions must be positive"); - } - if (!rowIndex) { - rowIndex = new IdentityInt32Index(nRows); - } - if (!colIndex) { - colIndex = new IdentityInt32Index(nCols); - } - Dataframe.__errorChecks(dims, columnarData, rowIndex, colIndex); - - this.__columns = Array.from(columnarData); - this.dims = dims; - this.length = nRows; // convenience accessor for row dimension - this.rowIndex = rowIndex; - this.colIndex = colIndex; - - this.__compile(__columnsAccessor); - } - - static __errorChecks(dims, columnarData, rowIndex, colIndex) { - const [nRows, nCols] = dims; - - /* check for expected types */ - if (!Array.isArray(columnarData)) { - throw new TypeError("Dataframe constructor requires array of columns"); - } - if (!columnarData.every(c => isArrayOrTypedArray(c))) { - throw new TypeError("Dataframe columns must all be Array or TypedArray"); - } - if (!isLabelIndex(rowIndex)) { - throw new TypeError("Dataframe rowIndex is an unsupported type."); - } - if (!isLabelIndex(colIndex)) { - throw new TypeError("Dataframe colIndex is an unsupported type."); - } - - /* check for expected dimensionality / size */ - if ( - nCols !== columnarData.length || - !columnarData.every(c => c.length === nRows) - ) { - throw new RangeError( - "Dataframe dimension does not match provided data shape" - ); - } - if (nRows !== rowIndex.size()) { - throw new RangeError( - "Dataframe rowIndex must have same size as underlying data" - ); - } - if (nCols !== colIndex.size()) { - throw new RangeError( - "Dataframe colIndex must have same size as underlying data" - ); - } - } - - static __compileColumn(column, getOffset, getLabel) { - /* - Each column accessor is a function which will lookup data by - index (ie, is equivalent to dataframe.get(row, col), where 'col' - is fixed. - - In addition, each column accessor has several functions: - - asArray() -- return the entire column as a native Array or TypedArray. - Crucially, this native array only supports label indexing. - Example: - const arr = df.col('a').asArray(); - - has(rlabel) -- return boolean indicating of the row label - is contained within the column. Example: - const isInColumn = df.col('a').includes(99) - For the default offset indexing, this is identical to: - const isInColumn = (99 > 0) && (99 < df.nRows); - - ihas(roffset) -- same as has(), but accepts a row offset - instead of a row label. - - indexOf(value) -- return the label (not offset) of the first instance of - 'value' in the column. If you want the offset, just use the builtin JS - indexOf() function, available on both Array and TypedArray. - - iget(offset) -- return the value at 'offset' - - */ - const { length } = column; - - /* get value by row label */ - const get = function get(rlabel) { - return column[getOffset(rlabel)]; - }; - - /* get value by row offset */ - const iget = function iget(roffset) { - return column[roffset]; - }; - - /* full column array access */ - const asArray = function asArray() { - return column; - }; - - /* test for row label inclusion in column */ - const has = function has(rlabel) { - const offset = getOffset(rlabel); - return offset >= 0 && offset < length; - }; - - const ihas = function ihas(offset) { - return offset >= 0 && offset < length; - }; - - /* - return first label (index) at which the value is found in this column, - or undefined if not found. - - NOTE: not found return is DIFFERENT than the default Array.indexOf as - -1 is a plausible Dataframe row/col label. - */ - const indexOf = function indexOf(value) { - const offset = column.indexOf(value); - if (offset === -1) { - return undefined; - } - return getLabel(offset); - }; - - /* - Summarize the column data. Lazy eval; - */ - const summarize = callOnceLazy(() => - isTypedArray(column) - ? summarizeContinuous(column) - : summarizeCategorical(column) - ); - - get.summarize = summarize; - get.asArray = asArray; - get.has = has; - get.ihas = ihas; - get.indexOf = indexOf; - get.iget = iget; - return get; - } - - __compile(accessors) { - /* - Compile data accessors for each column. - - Use an existing accessor if provided, else compile a new one. - */ - const { getOffset, getLabel } = this.rowIndex; - this.__columnsAccessor = this.__columns.map((column, idx) => { - if (accessors[idx]) { - return accessors[idx]; - } - return Dataframe.__compileColumn(column, getOffset, getLabel); - }); - } - - clone() { - /* - Clone this dataframe - */ - return new this.constructor( - this.dims, - [...this.__columns], - this.rowIndex, - this.colIndex, - [...this.__columnsAccessor] - ); - } - - withCol(label, colData, withRowIndex = null) { - /* - Create a new DF, which is `this` plus the new column. Example: - const newDf = df.withCol("foo", [1,2,3]); - - Dimensionality of new column must match existing dataframe. - - Special case: empty dataframe will accept any size column. Example: - const newDf = Dataframe.empty().withCol("foo", [1,2,3]); - - If `withRowIndex` specified, the provided index will become the - rowIndex for the newly created dataframe. If not specified, - the rowIndex from `this` will be used (ie, the rowIndex is - unchanged). - */ - let dims; - let rowIndex; - if (this.isEmpty()) { - dims = [colData.length, 1]; - rowIndex = null; - } else { - dims = [this.dims[0], this.dims[1] + 1]; - ({ rowIndex } = this); - } - - if (withRowIndex) { - rowIndex = withRowIndex; - } - - const columns = [...this.__columns]; - columns.push(colData); - const colIndex = this.colIndex.withLabel(label); - const columnsAccessor = [...this.__columnsAccessor]; - return new this.constructor( - dims, - columns, - rowIndex, - colIndex, - columnsAccessor - ); - } - - dropCol(label) { - /* - Create a new dataframe, omitting one columns. - - const newDf = df.dropCol("colors"); - */ - const dims = [this.dims[0], this.dims[1] - 1]; - const coffset = this.colIndex.getOffset(label); - const columns = [...this.__columns]; - columns.splice(coffset, 1); - const colIndex = this.colIndex.dropLabel(label); - const columnsAccessor = [...this.__columnsAccessor]; - columnsAccessor.splice(coffset, 1); - return new this.constructor( - dims, - columns, - this.rowIndex, - colIndex, - columnsAccessor - ); - } - - static empty(rowIndex = null, colIndex = null) { - return new Dataframe([0, 0], [], rowIndex, colIndex); - } - - static create(dims, columnarData) { - /* - Create a dataframe from raw columnar data. All column arrays - must have the same length. Identity indexing will be used. - - Example: - const df = Dataframe.create([2,2], [new Uint32Array(2), new Float32Array(2)]); - */ - return new Dataframe(dims, columnarData, null, null); - } - - __subset(rowOffsets, colOffsets, withRowIndex) { - const dims = [...this.dims]; - - const getSortedLabelAndOffsets = (offsets, index) => { - /* - Given offsets, return both offsets and associated lables, - sorted by offset. - */ - if (!offsets) { - return [null, null]; - } - const sortedOffsets = sortArray(offsets); - const sortedLabels = new Array(sortedOffsets.length); - for (let i = 0, l = sortedOffsets.length; i < l; i += 1) { - sortedLabels[i] = index.getLabel(sortedOffsets[i]); - } - return [sortedLabels, sortedOffsets]; - }; - - let { colIndex } = this; - if (colOffsets) { - let colLabels; - [colLabels, colOffsets] = getSortedLabelAndOffsets( - colOffsets, - this.colIndex - ); - dims[1] = colOffsets.length; - colIndex = this.colIndex.subsetLabels(colLabels); - } - - let { rowIndex } = this; - if (withRowIndex) rowIndex = withRowIndex; - if (rowOffsets) { - let rowLabels; - [rowLabels, rowOffsets] = getSortedLabelAndOffsets( - rowOffsets, - this.rowIndex - ); - dims[0] = rowLabels.length; - if (!withRowIndex) rowIndex = this.rowIndex.subsetLabels(rowLabels); - } - - /* subset columns */ - let columns = this.__columns; - if (colOffsets) { - columns = new Array(colOffsets.length); - for (let i = 0, l = colOffsets.length; i < l; i += 1) { - columns[i] = this.__columns[colOffsets[i]]; - } - } - - /* subset rows */ - if (rowOffsets) { - columns = columns.map(col => { - const newCol = new col.constructor(rowOffsets.length); - for (let i = 0, l = rowOffsets.length; i < l; i += 1) { - newCol[i] = col[rowOffsets[i]]; - } - return newCol; - }); - } - return new Dataframe(dims, columns, rowIndex, colIndex); - } - - subset(rowLabels, colLabels = null, withRowIndex = null) { - /* - Subset by row/col labels. - - withRowIndex allows assignment of new row index during subset operation. - If withRowIndex === null, it will reset the index to identity (offset) - indexing. if withRowIndex is a label index object, it will be used - for the new dataframe. - */ - const toOffsets = (labels, index) => { - if (!labels) { - return null; - } - return labels.map(label => { - const off = index.getOffset(label); - if (off === undefined) { - throw new RangeError(`unknown label: ${label}`); - } - return off; - }); - }; - - const rowOffsets = toOffsets(rowLabels, this.rowIndex); - const colOffsets = toOffsets(colLabels, this.colIndex); - return this.__subset(rowOffsets, colOffsets, withRowIndex); - } - - isubset(rowOffsets, colOffsets = null, withRowIndex = null) { - /* - Subset by row/col offset. - - withRowIndex allows assignment of new row index during subset operation. - If withRowIndex === null, it will reset the index to identity (offset) - indexing. if withRowIndex is a label index object, it will be used - for the new dataframe. - */ - return this.__subset(rowOffsets, colOffsets, withRowIndex); - } - - isubsetMask(rowMask, colMask = null, withRowIndex = null) { - /* - Subset on row/column based upon a truthy/falsey array (a mask). - - withRowIndex allows assignment of new row index during subset operation. - If withRowIndex === null, it will reset the index to identity (offset) - indexing. if withRowIndex is a label index object, it will be used - for the new dataframe. - */ - const [nRows, nCols] = this.dims; - if ( - (rowMask && rowMask.length !== nRows) || - (colMask && colMask.length !== nCols) - ) { - throw new RangeError("boolean arrays must match row/col dimensions"); - } - - /* convert masks to lists - method wastes space, but is fast */ - const toList = (mask, maxSize) => { - if (!mask) { - return null; - } - const list = new Int32Array(maxSize); - let elems = 0; - for (let i = 0, l = mask.length; i < l; i += 1) { - if (mask[i]) { - list[elems] = i; - elems += 1; - } - } - return new Int32Array(list.buffer, 0, elems); - }; - const rowOffsets = toList(rowMask, nRows); - const colOffsets = toList(colMask, nCols); - return this.__subset(rowOffsets, colOffsets, withRowIndex); - } - - /** - Data access with row/col. - **/ - - col(columnLabel) { - /* - Return accessor bound to a column. Allows random row access - based upon the row indexing. Returns undefined if the - columnLabel is not present in the dataframe. - - Example for a dataframe with string labeled columns, and - default (offset) indices for rows (eg, [0, 'foo']) - - const getValue = df.col('foo'); - for (let r = 0; r < df.nRows; r += 1) { - console.log(r, getValue(r)); - } - - See __compile() for the functions available in a column accessor. - */ - const coff = this.colIndex.getOffset(columnLabel); - return this.__columnsAccessor[coff]; - } - - icol(columnOffset) { - /* - Return column accessor by offset. - */ - return this.__columnsAccessor[columnOffset]; - } - - at(r, c) { - /* - Access a single value, for a row/col label pair. - - For performance reasons, there are no bounds or existance - checks on labels, and no defined behavior when these are supplied. - May return undefined, throw an Error, or do something else for - non-existant labels. If you want predictable out-of-bounds - behavior, use has(), eg, - - const myVal = df.has(r,l) ? df.at(r,l) : undefined; - */ - const coff = this.colIndex.getOffset(c); - const roff = this.rowIndex.getOffset(r); - return this.__columns[coff][roff]; - } - - iat(r, c) { - /* - Access a single value, for a row/col offset (integer) position. - - For performance reasons, there are no bounds checks on row/col offsets - or other well-defined behavior for out-of-bounds values. If you want - well-defined bounds checking, use ihas(), eg, - - const myVal = df.ihas(r, c) ? df.iat(r, c) : undefined; - */ - return this.__columns[c][r]; - } - - has(r, c) { - /* - Test if row/col labels exist in the dataframe - returns true/false - */ - const [nRows, nCols] = this.dims; - const coff = this.colIndex.getOffset(c); - const roff = this.rowIndex.getOffset(r); - return coff >= 0 && coff < nCols && roff >= 0 && roff < nRows; - } - - ihas(r, c) { - /* - Test if row/col offset (integer) position exists in the - dataframe - returns true/false - */ - const [nRows, nCols] = this.dims; - return c >= 0 && c < nCols && r >= 0 && r < nRows; - } - - hasCol(c) { - /* - Test if col label exists - return true/false - */ - return !!this.col(c); - } - - isEmpty() { - /* - Return true if this is an empty dataframe, ie, has dimensions [0,0] - */ - const [rows, cols] = this.dims; - return rows === 0 && cols === 0; - } - - /**** - Functional (map/reduce/etc) data access - - TODO: most are not yet implemented, as there is no clear use case. Can easily - add these as useful. - ****/ - - mapColumns(callback) { - /* - map all columns in the dataframe, returning a new dataframe comprised of the - return values, with the same index as the original dataframe. - - callback MUST not modify the column, but instead return a mutated copy. - */ - const columns = this.__columns.map(callback); - const columnsAccessor = columns.map((c, idx) => - this.__columns[idx] === c ? this.__columnsAccessor[idx] : undefined - ); - return new this.constructor( - this.dims, - columns, - this.rowIndex, - this.colIndex, - columnsAccessor - ); - } - - /* - Map & reduce of column or row - - TODO remainder of map/reduce functions: mapCol, mapRow, reduceRow, ... - */ - /* comment out until we have a use for this - - reduceCol(clabel, callback, initialValue) { - const coff = this.colIndex.getOffset(clabel); - const column = this.__columns[coff]; - let start = 0; - let acc = initialValue; - if (initialValue === undefined) { - acc = column[0]; - start = 1; - } - for (let i = start, l = column.length; i < l; i += 1) { - acc = callback(acc, column[i]); - } - return acc; - } - */ -} - -export default Dataframe; diff --git a/client/src/util/dataframe/index.js b/client/src/util/dataframe/index.js deleted file mode 100644 index 68ccba29..00000000 --- a/client/src/util/dataframe/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export { default as Dataframe } from "./dataframe"; -export { DenseInt32Index, IdentityInt32Index, KeyIndex } from "./labelIndex"; diff --git a/client/src/util/dataframe/labelIndex.js b/client/src/util/dataframe/labelIndex.js deleted file mode 100644 index b10a8824..00000000 --- a/client/src/util/dataframe/labelIndex.js +++ /dev/null @@ -1,238 +0,0 @@ -/** -Label indexing - map a label to & from an integer offset. See Dataframe -for how this is used. -**/ - -import { rangeFill as fillRange } from "../range"; - -/* -Private utility functions -*/ -function extent(tarr) { - let min = 0x7fffffff; - let max = ~min; // eslint-disable-line no-bitwise - for (let i = 0, l = tarr.length; i < l; i += 1) { - const v = tarr[i]; - if (v < min) { - min = v; - } - if (v > max) { - max = v; - } - } - return [min, max]; -} - -/* eslint-disable class-methods-use-this */ -class IdentityInt32Index { - /* - identity/noop index, with small assumptions that labels are int32 - */ - constructor(maxOffset) { - this.maxOffset = maxOffset; - } - - keys() { - // memoize - const k = fillRange(new Int32Array(this.maxOffset)); - this.keys = function keys() { - return k; - }; - return k; - } - - getOffset(i) { - // label to offset - return i; - } - - getLabel(i) { - // offset to label - return i; - } - - size() { - return this.maxOffset; - } - - __promote(labelArray) { - /* - time/space decision - based on the resulting density - */ - const [minLabel, maxLabel] = extent(labelArray); - const labelSpaceSize = maxLabel - minLabel + 1; - const density = labelSpaceSize / this.maxOffset; - /* 0.1 is a magic number, that needs testing to optimize */ - if (density < 0.1) { - return new KeyIndex(labelArray); - } - return new DenseInt32Index(labelArray, [minLabel, maxLabel]); - } - - subsetLabels(labelArray) { - return this.__promote(labelArray); - } - - withLabel(label) { - if (label === this.maxOffset) { - return new IdentityInt32Index(label + 1); - } - return this.__promote([...this.keys(), label]); - } - - dropLabel(label) { - if (label === this.maxOffset - 1) { - return new IdentityInt32Index(label); - } - const labelArray = [...this.keys()]; - labelArray.splice(labelArray.indexOf(label), 1); - return this.__promote(labelArray); - } -} -/* eslint-enable class-methods-use-this */ - -/* eslint-disable class-methods-use-this */ -class DenseInt32Index { - /* - DenseInt32Index indexes integer labels, and uses Int32Array typed arrays - for both forward and reverse indexing. This means that the min/max range - of the forward index labels must be known a priori (so that the index - array can be pre-allocated). - */ - constructor(labels, labelRange = null) { - if (labels.constructor !== Int32Array) { - labels = new Int32Array(labels); - } - - if (!labelRange) { - labelRange = extent(labels); - } - const [minLabel, maxLabel] = labelRange; - const labelSpaceSize = maxLabel - minLabel + 1; - const index = new Int32Array(labelSpaceSize).fill(-1); - for (let i = 0, l = labels.length; i < l; i += 1) { - const label = labels[i]; - index[label - minLabel] = i; - } - - this.minLabel = minLabel; - this.rindex = labels; - this.index = index; - this.__compile(); - } - - __compile() { - const { minLabel, index, rindex } = this; - this.getOffset = function getOffset(l) { - return index[l - minLabel]; - }; - this.getLabel = function getLabel(i) { - return rindex[i]; - }; - } - - keys() { - return this.rindex; - } - - size() { - return this.rindex.length; - } - - __promote(labelArray) { - /* - time/space decision - if we are going to use less than 10% of the - dense index space, switch to a KeyIndex (which is slower, but uses - less memory for sparse label spaces). - */ - const [minLabel, maxLabel] = extent(labelArray); - const labelSpaceSize = maxLabel - minLabel + 1; - const density = labelSpaceSize / this.rindex.length; - /* 0.1 is a magic number, that needs testing to optimize */ - if (density < 0.1) { - return new KeyIndex(labelArray); - } - return new DenseInt32Index(labelArray, [minLabel, maxLabel]); - } - - subsetLabels(labelArray) { - return this.__promote(labelArray); - } - - withLabel(label) { - return this.__promote([...this.keys(), label]); - } - - dropLabel(label) { - const labelArray = [...this.keys()]; - labelArray.splice(labelArray.indexOf(label), 1); - return this.__promote(labelArray); - } -} -/* eslint-enable class-methods-use-this */ - -/* eslint-disable class-methods-use-this */ -class KeyIndex { - /* - KeyIndex indexes arbitrary JS primitive types, and uses a Map() - as its core data structure. - */ - constructor(labels) { - const index = new Map(); - if (labels === undefined) { - labels = []; - } - const rindex = labels; - labels.forEach((v, i) => { - index.set(v, i); - }); - - this.index = index; - this.rindex = rindex; - this.__compile(); - } - - __compile() { - const { index, rindex } = this; - this.getOffset = function getOffset(k) { - return index.get(k); - }; - this.getLabel = function getLabel(i) { - return rindex[i]; - }; - } - - keys() { - return this.rindex; - } - - size() { - return this.rindex.length; - } - - subsetLabels(labelArray) { - return new KeyIndex(labelArray); - } - - withLabel(label) { - return new KeyIndex([...this.rindex, label]); - } - - dropLabel(label) { - const idx = this.rindex.indexOf(label); - const labelArray = [...this.rindex]; - labelArray.splice(idx, 1); - return new KeyIndex(labelArray); - } -} -/* eslint-enable class-methods-use-this */ - -function isLabelIndex(i) { - return ( - i instanceof IdentityInt32Index || - i instanceof DenseInt32Index || - i instanceof KeyIndex - ); -} - -export { DenseInt32Index, IdentityInt32Index, KeyIndex, isLabelIndex }; diff --git a/client/src/util/dataframe/summarize.js b/client/src/util/dataframe/summarize.js deleted file mode 100644 index 12648bf2..00000000 --- a/client/src/util/dataframe/summarize.js +++ /dev/null @@ -1,82 +0,0 @@ -/* -Private dataframe support functions - -TODO / XXX: for scalar/continuous data, this uses a naive method -of computing quantiles. Would be good to switch from sort to -partition at some point. -*/ - -import quantile from "../quantile"; -import { sortArray } from "../typedCrossfilter/sort"; - -// [ 0, 0.01, 0.02, ..., 1.0] -const centileNames = new Array(101).fill(0).map((v, idx) => idx / 100); - -export function summarizeContinuous(col) { - let min; - let max; - let nan = 0; - let pinf = 0; - let ninf = 0; - let percentiles; - if (col) { - // -Inf < finite < Inf < NaN - const sortedCol = sortArray(new col.constructor(col)); - - // count non-finites, which are at each end of sorted data - for (let i = sortedCol.length - 1; i >= 0; i -= 1) { - if (!Number.isNaN(sortedCol[i])) { - nan = sortedCol.length - i - 1; - break; - } - } - for (let i = 0, l = sortedCol.length; i < l; i += 1) { - if (sortedCol[i] !== Number.NEGATIVE_INFINITY) { - ninf = i; - break; - } - } - for (let i = sortedCol.length - nan - 1; i >= 0; i -= 1) { - if (sortedCol[i] !== Number.POSITIVE_INFINITY) { - pinf = sortedCol.length - i - nan - 1; - break; - } - } - - // compute percentiles on finite data ONLY - const sortedColFiniteOnly = sortedCol.slice( - ninf, - sortedCol.length - nan - pinf - ); - percentiles = quantile(centileNames, sortedColFiniteOnly, true); - min = percentiles[0]; - max = percentiles[100]; - } - return { - categorical: false, - min, - max, - nan, - pinf, - ninf, - percentiles - }; -} - -export function summarizeCategorical(col) { - const categoryCounts = new Map(); - if (col) { - for (let r = 0, l = col.length; r < l; r += 1) { - const val = col[r]; - let curCount = categoryCounts.get(val); - if (curCount === undefined) curCount = 0; - categoryCounts.set(val, curCount + 1); - } - } - return { - categorical: true, - categories: [...categoryCounts.keys()], - categoryCounts, - numCategories: categoryCounts.size - }; -} diff --git a/client/src/util/dataframe/util.js b/client/src/util/dataframe/util.js deleted file mode 100644 index 8fa1353e..00000000 --- a/client/src/util/dataframe/util.js +++ /dev/null @@ -1,19 +0,0 @@ -/* -Private utility code for dataframe -*/ - -export { isTypedArray, isArrayOrTypedArray } from "../typeHelpers"; - -export function callOnceLazy(f) { - let value; - let calledOnce = false; - const result = function result(...args) { - if (!calledOnce) { - value = f(...args); - calledOnce = true; - } - return value; - }; - - return result; -} diff --git a/client/src/util/finiteExtent.js b/client/src/util/finiteExtent.js deleted file mode 100644 index 24239113..00000000 --- a/client/src/util/finiteExtent.js +++ /dev/null @@ -1,33 +0,0 @@ -/* -Return the [minimum, maximum] extent, of the given typed array, ignoring -non-finite values (ie, +Infinity, -Infinity). - -If undefined or empty array, or array contains only non-finite numbers, -will return [undefined, undefined] -*/ - -function finiteExtent(tarr) { - let min; - let max; - let i; - - for (i = 0; i < tarr.length; i += 1) { - const val = tarr[i]; - if (Number.isFinite(val)) { - min = val; - max = val; - i += 1; - break; - } - } - for (; i < tarr.length; i += 1) { - const val = tarr[i]; - if (Number.isFinite(val)) { - if (min > val) min = val; - if (max < val) max = val; - } - } - return [min, max]; -} - -export default finiteExtent; diff --git a/client/src/util/fromEntries.js b/client/src/util/fromEntries.js deleted file mode 100644 index a86a7050..00000000 --- a/client/src/util/fromEntries.js +++ /dev/null @@ -1,13 +0,0 @@ -export default function fromEntries(arr) { - /* - Similar to Object.fromEntries, but only handles array. - This could be replaced with the standard fucnction once it - is widely available. As of 3/20/2019, it has not yet - been released in the Chrome stable channel. - */ - const obj = {}; - for (let i = 0, l = arr.length; i < l; i += 1) { - obj[arr[i][0]] = arr[i][1]; - } - return obj; -} diff --git a/client/src/util/nameCreators.js b/client/src/util/nameCreators.js deleted file mode 100644 index a4237c58..00000000 --- a/client/src/util/nameCreators.js +++ /dev/null @@ -1,45 +0,0 @@ -/* - -The original motivation for this file is name collision. It's possible -to have two of the same gene, user defined and diffexpressed, on the client -simultaneously, looking at the same data in the cache but with two -dimensions and two crossfilters. It's also possible that someone could -have a obsAnnotation named X, but we are using that for layout. So -we namespace, and abstract to avoid proliferating strings throughout the -codebase. - -*/ - -const makeDimensionName = (namespace, key) => `${namespace}_${key}`; - -export const layoutDimensionName = key => makeDimensionName("layout", key); -export const obsAnnoDimensionName = key => makeDimensionName("obsAnno", key); -export const diffexpDimensionName = key => - makeDimensionName("varData_diffexp", key); -export const userDefinedDimensionName = key => - makeDimensionName("varData_userDefined", key); - -/* - continuousNamespace = { - isObs: true, - isDiffExp: false, - isUserDefined: false - } - - ie., makeContinuousDimensionName(continuousNamespace = {isObs: true}, "total_reads") - see: histogram brush, as it doesn't know what type of continuous it was with only field -*/ -export const makeContinuousDimensionName = (continuousNamespace, key) => { - let name; - if (continuousNamespace.isObs) { - name = obsAnnoDimensionName(key); - } else if (continuousNamespace.isDiffExp) { - name = diffexpDimensionName(key); - } else if (continuousNamespace.isUserDefined) { - name = userDefinedDimensionName(key); - } else { - throw new Error("unknown continuous dimension"); - } - - return name; -}; diff --git a/client/src/util/parseRGB.js b/client/src/util/parseRGB.js deleted file mode 100644 index 2c6b4997..00000000 --- a/client/src/util/parseRGB.js +++ /dev/null @@ -1,30 +0,0 @@ -// jshint esversion: 6 -import scaleRGB from "./scaleRGB"; - -// maintain a cache of already parsed RGB names, as it is reasonably expensive -// to do this operation. This lets us have speed, but keep the pleasant ability -// to talk about colors by their text description eg, 'rgb(0,0,1)' -// -const colorCache = {}; - -function parseColorName(c) { - if (c[0] !== "#") { - const _c = c.replace(/[^\d,.]/g, "").split(","); - return [scaleRGB(+_c[0]), scaleRGB(+_c[1]), scaleRGB(+_c[2])]; - } - const parsedHex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(c); - return [ - scaleRGB(parseInt(parsedHex[1], 16)), - scaleRGB(parseInt(parsedHex[2], 16)), - scaleRGB(parseInt(parsedHex[3], 16)) - ]; -} - -export default c => { - let cv = colorCache[c]; - if (!cv) { - cv = parseColorName(c); - colorCache[c] = cv; - } - return cv; -}; diff --git a/client/src/util/quantile.js b/client/src/util/quantile.js deleted file mode 100644 index fefbcd26..00000000 --- a/client/src/util/quantile.js +++ /dev/null @@ -1,29 +0,0 @@ -/* -quantiles - calculate quantiles for the typed array. - -Currently interpolates to 'lower' value. - -Arguments: - - * quantArr - array of quantiles to compute, where values: 0 <= value <= 1.0 - * tarr - a typed array - * sorted - option bool. If false (default), will assume array is not sorted. - If true, will assume it is sorted. - -*/ - -import { sortArray } from "./typedCrossfilter/sort"; - -export default function quantile(quantArr, tarr, sorted = false) { - /* - start with the naive (sort) implementation. Later, use a faster partition - */ - const arr = sorted ? tarr : sortArray(new tarr.constructor(tarr)); // copy - const len = arr.length; - return quantArr.map(q => { - if (q === 1) { - return arr[len - 1]; - } - return arr[Math.floor(q * len)]; - }); -} diff --git a/client/src/util/range.js b/client/src/util/range.js deleted file mode 100644 index 972a6276..00000000 --- a/client/src/util/range.js +++ /dev/null @@ -1,45 +0,0 @@ -/* -Array range creation - -range(start, stop, step) -> Array - This is identical to https://docs.python.org/3/library/functions.html#func-range - Returns new array filled with a range of numbers. - - Usage: - - range(stop) - start defaults to zero, step defaults to 1 - range(start, stop, [step]) - step defaults to 1 - - Examples: - range(3) -> [0, 1, 2] - range(1, 3) -> [1, 2] - range(1, 5, 2) -> [1, 3] - - -rangeFill(array, start, step) -> array - Fill entire array with values, from start, by step. Returns first array. - start defaults to zero, step defaults to one. - -*/ - -function _doFill(arr, start, step, count) { - for (let idx = 0, val = start; idx < count; idx += 1, val += step) { - arr[idx] = val; - } - return arr; -} - -export function rangeFill(arr, start = 0, step = 1) { - return _doFill(arr, start, step, arr.length); -} - -export function range(start, stop, step) { - if (start === undefined) return []; - if (stop === undefined) { - stop = start; - start = 0; - } - step = step || 1; // catch undefind and zero - const len = Math.max(Math.ceil((stop - start) / step), 0); - return _doFill(new Array(len), start, step, len); -} diff --git a/client/src/util/scaleLinear.js b/client/src/util/scaleLinear.js deleted file mode 100644 index a6042e0e..00000000 --- a/client/src/util/scaleLinear.js +++ /dev/null @@ -1,22 +0,0 @@ -// jshint esversion: 6 - -// Substitute for a d3 linear scale - less flexible, more performant. -// Returns a function which will scale a value. -// -// Example will scale [0,1] to [-1,1] -// var myScale = scaleLinear([0, 1], [-1, 1]); -// myScale(0) === -1 -// this is is equivalent to d3.scaleLinear().domain([0,1]).range([-1,1]) - -export default (domain, range) => { - const domainStart = domain[0]; - const scale = (range[1] - range[0]) / (domain[1] - domain[0]); - const invScale = 1 / scale; - const rangeStart = range[0]; - const f = value => (value - domainStart) * scale + rangeStart; - - // inverter - f.invert = value => (value - rangeStart) * invScale + domainStart; - - return f; -}; diff --git a/client/src/util/scaleRGB.js b/client/src/util/scaleRGB.js deleted file mode 100644 index b705e201..00000000 --- a/client/src/util/scaleRGB.js +++ /dev/null @@ -1,11 +0,0 @@ -// jshint esversion: 6 -export default input => { - const outputMax = 1; - const outputMin = 0; - - const inputMax = 255; - const inputMin = 0; - - const percent = (input - inputMin) / (inputMax - inputMin); - return percent * (outputMax - outputMin) + outputMin; -}; diff --git a/client/src/util/stateManager/colorHelpers.js b/client/src/util/stateManager/colorHelpers.js deleted file mode 100644 index f41bae7b..00000000 --- a/client/src/util/stateManager/colorHelpers.js +++ /dev/null @@ -1,120 +0,0 @@ -/* -Helper functions for the embedded graph colors -*/ -import * as d3 from "d3"; -import { interpolateRainbow, interpolateCool } from "d3-scale-chromatic"; -import * as globals from "../../globals"; -import parseRGB from "../parseRGB"; -import finiteExtent from "../finiteExtent"; -import { range } from "../range"; - -/* -create new colors state object. Paramters: - - world - current world object - - mode - color-by mode. One of: null, "color by expression", - "color by continuous metadata", "color by categorical metadata" - - -*/ -function createColors(world, colorMode = null, colorAccessor = null) { - switch (colorMode) { - case "color by categorical metadata": { - return createColorsByCategoricalMetadata(world, colorAccessor); - } - case "color by continuous metadata": { - return createColorsByContinuousMetadata(world, colorAccessor); - } - case "color by expression": { - return createColorsByExpression(world, colorAccessor); - } - default: { - const defaultCellColor = parseRGB(globals.defaultCellColor); - return { - rgb: new Array(world.nObs).fill(defaultCellColor), - scale: undefined - }; - } - } -} - -function createColorsByCategoricalMetadata(world, accessor) { - const { categories } = world.schema.annotations.obsByName[accessor]; - - const scale = d3 - .scaleSequential(interpolateRainbow) - .domain([0, categories.length]); - - /* pre-create colors - much faster than doing it for each obs */ - const colors = categories.reduce((acc, cat, idx) => { - acc[cat] = parseRGB(scale(idx)); - return acc; - }, {}); - - const rgb = new Array(world.nObs); - const data = world.obsAnnotations.col(accessor).asArray(); - for (let i = 0, len = world.obsAnnotations.length; i < len; i += 1) { - const cat = data[i]; - rgb[i] = colors[cat]; - } - return { rgb, scale }; -} - -function createColorsByContinuousMetadata(world, accessor) { - const colorBins = 100; - const col = world.obsAnnotations.col(accessor); - const { min, max } = col.summarize(); - const scale = d3 - .scaleQuantile() - .domain([min, max]) - .range(range(colorBins - 1, -1, -1)); - - /* pre-create colors - much faster than doing it for each obs */ - const colors = new Array(colorBins); - for (let i = 0; i < colorBins; i += 1) { - colors[i] = parseRGB(interpolateCool(i / colorBins)); - } - - const nonFiniteColor = parseRGB(globals.nonFiniteCellColor); - const rgb = new Array(world.nObs); - const data = col.asArray(); - for (let i = 0, len = world.obsAnnotations.length; i < len; i += 1) { - const val = data[i]; - if (Number.isFinite(val)) { - const c = scale(val); - rgb[i] = colors[c]; - } else { - rgb[i] = nonFiniteColor; - } - } - return { rgb, scale }; -} - -function createColorsByExpression(world, accessor) { - const expression = world.varData.col(accessor).asArray(); - const colorBins = 100; - const [min, max] = finiteExtent(expression); - const scale = d3 - .scaleQuantile() - .domain([min, max]) - .range(range(colorBins - 1, -1, -1)); - - /* pre-create colors - much faster than doing it for each obs */ - const colors = new Array(colorBins); - for (let i = 0; i < colorBins; i += 1) { - colors[i] = parseRGB(interpolateCool(i / colorBins)); - } - const nonFiniteColor = parseRGB(globals.nonFiniteCellColor); - - const rgb = new Array(world.nObs); - for (let i = 0, len = expression.length; i < len; i += 1) { - const e = expression[i]; - if (Number.isFinite(e)) { - const c = scale(e); - rgb[i] = colors[c]; - } else { - rgb[i] = nonFiniteColor; - } - } - return { rgb, scale }; -} - -export default createColors; diff --git a/client/src/util/stateManager/controlsHelpers.js b/client/src/util/stateManager/controlsHelpers.js deleted file mode 100644 index 3f2e08ea..00000000 --- a/client/src/util/stateManager/controlsHelpers.js +++ /dev/null @@ -1,187 +0,0 @@ -/* -Helper functions for the controls reducer -*/ - -import _ from "lodash"; - -import * as globals from "../../globals"; -import { rangeFill as fillRange } from "../range"; -import { - userDefinedDimensionName, - diffexpDimensionName -} from "../nameCreators"; - -/* -Selection state for categoricals are tracked in an Object that -has two main components for each category: -1. mapping of option value to an index -2. array of bool selection state by index -Remember that option values can be ANY js type, except undefined/null. - - { - _category_name_1: { - // map of option value to index - categoryValueIndices: Map([ - catval1: index, - ... - ]) - - // index->selection true/false state - categoryValueSelected: [ true/false, true/false, ... ] - - // number of options - numCategoryValues: number, - - // isTruncated - true if the options for selection has - // been truncated (ie, was too large to implement) - } - } -*/ -function topNCategories(summary) { - const counts = _.map(summary.categories, cat => - summary.categoryCounts.get(cat) - ); - const sortIndex = fillRange(new Array(summary.numCategories)).sort( - (a, b) => counts[b] - counts[a] - ); - const sortedCategories = _.map(sortIndex, i => summary.categories[i]); - const sortedCounts = _.map(sortIndex, i => counts[i]); - const N = globals.maxCategoricalOptionsToDisplay; - - if (sortedCategories.length < N) { - return [sortedCategories, sortedCounts]; - } - return [sortedCategories.slice(0, N), sortedCounts.slice(0, N)]; -} - -export function createCategoricalSelection(maxCategoryItems, world) { - const res = {}; - const obsIndexName = world.schema.annotations.obs.index; - _.forEach(world.obsAnnotations.colIndex.keys(), key => { - const summary = world.obsAnnotations.col(key).summarize(); - if (summary.categories) { - const isColorField = key.includes("color") || key.includes("Color"); - const isSelectableCategory = - !isColorField && - key !== obsIndexName && - summary.categories.length < maxCategoryItems; - if (isSelectableCategory) { - const [categoryValues, categoryValueCounts] = topNCategories(summary); - const categoryValueIndices = new Map( - categoryValues.map((v, i) => [v, i]) - ); - const numCategoryValues = categoryValueIndices.size; - const categoryValueSelected = new Array(numCategoryValues).fill(true); - const isTruncated = categoryValues.length < summary.numCategories; - res[key] = { - categoryValues, // array: of natively typed category values - categoryValueIndices, // map: category value (native type) -> category index - categoryValueSelected, // array: t/f selection state - numCategoryValues, // number: of values in the category - isTruncated, // bool: true if list was truncated - categoryValueCounts, // array: cardinality of each category, - categorySelected: true // bool - default state for entire category - }; - } - } - }); - return res; -} - -/* -given a categoricalSelection, return the list of all category values -where selection state is true (ie, they are selected). -*/ -export function selectedValuesForCategory(categorySelectionState, dfColumn) { - const { - categorySelected, - categoryValueSelected, - categoryValueIndices - } = categorySelectionState; - let selectedValues; - if (categorySelected) { - selectedValues = new Set(dfColumn.summarize().categories); - } else { - selectedValues = new Set(); - } - categoryValueIndices.forEach((catIndex, catValue) => { - if (!categoryValueSelected[catIndex]) { - selectedValues.delete(catValue); - } else { - selectedValues.add(catValue); - } - }); - return [...selectedValues.values()]; -} - -/* -build a crossfilter dimensions for all gene expression related dimensions. -*/ -export function createGeneDimensions( - userDefinedGenes, - diffexpGenes, - world, - crossfilter -) { - crossfilter = userDefinedGenes.reduce( - (xflt, gene) => - xflt.addDimension( - userDefinedDimensionName(gene), - "scalar", - world.varData.col(gene).asArray(), - Float32Array - ), - crossfilter - ); - crossfilter = diffexpGenes.reduce( - (xflt, gene) => - xflt.addDimension( - diffexpDimensionName(gene), - "scalar", - world.varData.col(gene).asArray(), - Float32Array - ), - crossfilter - ); - return crossfilter; -} - -export function pruneVarDataCache(varData, needed) { - /* - Remove any unneeded columns from the varData dataframe. Will only - prune / remove if the total column count exceeds VarDataCacheLowWatermark - - Note: this code leverages the fact that dataframe offsets indicate - the order in which the columns were added. This crudely provides - LRU semantics, so we can delete "older" columns first. - */ - - /* - VarDataCacheLowWatermark - this cofig value sets the minimum cache size, - in columns, below which we don't throw away data. - - The value should be high enough so we are caching the maximum which will - "typically" be used in the UI (currently: 10 for diffexp, and N for user- - specified genes), and low enough to account for memory use (any single - column size is 4 bytes * numObs, so a column can be multi-megabyte in common - use cases). - */ - const VarDataCacheLowWatermark = 32; - - const numOverWatermark = varData.dims[1] - VarDataCacheLowWatermark; - if (numOverWatermark <= 0) return varData; - - const { colIndex } = varData; - const all = colIndex.keys(); - const unused = _.difference(all, needed); - if (unused.length > 0) { - // sort by offset in the dataframe - ie, psuedo-LRU - unused.sort((a, b) => colIndex.getOffset(a) - colIndex.getOffset(b)); - const numToDrop = - unused.length < numOverWatermark ? unused.length : numOverWatermark; - for (let i = 0; i < numToDrop; i += 1) { - varData = varData.dropCol(unused[i]); - } - } - return varData; -} diff --git a/client/src/util/stateManager/index.js b/client/src/util/stateManager/index.js deleted file mode 100644 index 151d48de..00000000 --- a/client/src/util/stateManager/index.js +++ /dev/null @@ -1,21 +0,0 @@ -// jshint esversion: 6 - -/* -Model manager providing an abstraction for the use of the reducer code. -This module provides several buckets of functionality: - - schema and config driven tranformation of the wire protocol - into a format that is easy for the UI code to use. - - manage the universe/world abstraction: - + universe: all of the server-provided, read-only data - + world: subset of universe - - lazy access and caching of dataframe contents as needed - -This is all VERY tightly integrated with reducers and actions, and -exists to support those concepts. -*/ - -export { default as createColors } from "./colorHelpers"; -export * as Universe from "./universe"; -export * as World from "./world"; -export * as WorldUtil from "./worldUtil"; -export * as ControlsHelpers from "./controlsHelpers"; diff --git a/client/src/util/stateManager/matrix.js b/client/src/util/stateManager/matrix.js deleted file mode 100644 index bfaf6b87..00000000 --- a/client/src/util/stateManager/matrix.js +++ /dev/null @@ -1,75 +0,0 @@ -import { flatbuffers } from "flatbuffers"; -import { NetEncoding } from "./matrix_generated"; - -const utf8Decoder = new TextDecoder("utf-8"); - -/* -Matrix flatbuffer decoding support. See fbs/matrix.fbs -*/ - -/* -Decode NetEncoding.TypedArray -*/ -function decodeTypedArray(uType, uValF, inplace = false) { - if (uType === NetEncoding.TypedArray.NONE) { - return null; - } - - // Convert to a JS class that supports this type - const TypeClass = NetEncoding[NetEncoding.TypedArray[uType]]; - // Create a TypedArray that references the underlying buffer - let arr = uValF(new TypeClass()).dataArray(); - if (uType === NetEncoding.TypedArray.JSONEncodedArray) { - const json = utf8Decoder.decode(arr); - arr = JSON.parse(json); - } else if (!inplace) { - /* force copy to release underlying FBS buffer */ - arr = new arr.constructor(arr); - } - return arr; -} - -/* -Parameter: Uint8Array or ArrayBuffer containing raw flatbuffer Matrix -Returns: object containing decoded Matrix: -{ - nRows: num, - nCols: num, - columns: [ - each column, which will be a TypedArray or Array - ] - colIdx: []|null -} -*/ -function decodeMatrixFBS(arrayBuffer, inplace = false) { - const bb = new flatbuffers.ByteBuffer(new Uint8Array(arrayBuffer)); - const df = NetEncoding.Matrix.getRootAsMatrix(bb); - - const nRows = df.nRows(); - const nCols = df.nCols(); - - /* decode columns */ - const columnsLength = df.columnsLength(); - const columns = Array(columnsLength).fill(null); - for (let c = 0; c < columnsLength; c += 1) { - const col = df.columns(c); - columns[c] = decodeTypedArray(col.uType(), col.u.bind(col), inplace); - } - - /* decode col_idx */ - const colIdx = decodeTypedArray( - df.colIndexType(), - df.colIndex.bind(df), - inplace - ); - - return { - nRows, - nCols, - columns, - colIdx, - rowIdx: null - }; -} - -export default decodeMatrixFBS; diff --git a/client/src/util/stateManager/matrix_generated.js b/client/src/util/stateManager/matrix_generated.js deleted file mode 100644 index c1da4048..00000000 --- a/client/src/util/stateManager/matrix_generated.js +++ /dev/null @@ -1,835 +0,0 @@ -// automatically generated by the FlatBuffers compiler, do not modify - -/** - * @const - * @namespace - */ -var NetEncoding = NetEncoding || {}; - -/** - * @enum - */ -NetEncoding.TypedArray = { - NONE: 0, 0: 'NONE', - Float32Array: 1, 1: 'Float32Array', - Int32Array: 2, 2: 'Int32Array', - Uint32Array: 3, 3: 'Uint32Array', - Float64Array: 4, 4: 'Float64Array', - JSONEncodedArray: 5, 5: 'JSONEncodedArray' -}; - -/** - * @constructor - */ -NetEncoding.Float32Array = function() { - /** - * @type {flatbuffers.ByteBuffer} - */ - this.bb = null; - - /** - * @type {number} - */ - this.bb_pos = 0; -}; - -/** - * @param {number} i - * @param {flatbuffers.ByteBuffer} bb - * @returns {NetEncoding.Float32Array} - */ -NetEncoding.Float32Array.prototype.__init = function(i, bb) { - this.bb_pos = i; - this.bb = bb; - return this; -}; - -/** - * @param {flatbuffers.ByteBuffer} bb - * @param {NetEncoding.Float32Array=} obj - * @returns {NetEncoding.Float32Array} - */ -NetEncoding.Float32Array.getRootAsFloat32Array = function(bb, obj) { - return (obj || new NetEncoding.Float32Array).__init(bb.readInt32(bb.position()) + bb.position(), bb); -}; - -/** - * @param {number} index - * @returns {number} - */ -NetEncoding.Float32Array.prototype.data = function(index) { - var offset = this.bb.__offset(this.bb_pos, 4); - return offset ? this.bb.readFloat32(this.bb.__vector(this.bb_pos + offset) + index * 4) : 0; -}; - -/** - * @returns {number} - */ -NetEncoding.Float32Array.prototype.dataLength = function() { - var offset = this.bb.__offset(this.bb_pos, 4); - return offset ? this.bb.__vector_len(this.bb_pos + offset) : 0; -}; - -/** - * @returns {Float32Array} - */ -NetEncoding.Float32Array.prototype.dataArray = function() { - var offset = this.bb.__offset(this.bb_pos, 4); - return offset ? new Float32Array(this.bb.bytes().buffer, this.bb.bytes().byteOffset + this.bb.__vector(this.bb_pos + offset), this.bb.__vector_len(this.bb_pos + offset)) : null; -}; - -/** - * @param {flatbuffers.Builder} builder - */ -NetEncoding.Float32Array.startFloat32Array = function(builder) { - builder.startObject(1); -}; - -/** - * @param {flatbuffers.Builder} builder - * @param {flatbuffers.Offset} dataOffset - */ -NetEncoding.Float32Array.addData = function(builder, dataOffset) { - builder.addFieldOffset(0, dataOffset, 0); -}; - -/** - * @param {flatbuffers.Builder} builder - * @param {Array.} data - * @returns {flatbuffers.Offset} - */ -NetEncoding.Float32Array.createDataVector = function(builder, data) { - builder.startVector(4, data.length, 4); - for (var i = data.length - 1; i >= 0; i--) { - builder.addFloat32(data[i]); - } - return builder.endVector(); -}; - -/** - * @param {flatbuffers.Builder} builder - * @param {number} numElems - */ -NetEncoding.Float32Array.startDataVector = function(builder, numElems) { - builder.startVector(4, numElems, 4); -}; - -/** - * @param {flatbuffers.Builder} builder - * @returns {flatbuffers.Offset} - */ -NetEncoding.Float32Array.endFloat32Array = function(builder) { - var offset = builder.endObject(); - return offset; -}; - -/** - * @constructor - */ -NetEncoding.Uint32Array = function() { - /** - * @type {flatbuffers.ByteBuffer} - */ - this.bb = null; - - /** - * @type {number} - */ - this.bb_pos = 0; -}; - -/** - * @param {number} i - * @param {flatbuffers.ByteBuffer} bb - * @returns {NetEncoding.Uint32Array} - */ -NetEncoding.Uint32Array.prototype.__init = function(i, bb) { - this.bb_pos = i; - this.bb = bb; - return this; -}; - -/** - * @param {flatbuffers.ByteBuffer} bb - * @param {NetEncoding.Uint32Array=} obj - * @returns {NetEncoding.Uint32Array} - */ -NetEncoding.Uint32Array.getRootAsUint32Array = function(bb, obj) { - return (obj || new NetEncoding.Uint32Array).__init(bb.readInt32(bb.position()) + bb.position(), bb); -}; - -/** - * @param {number} index - * @returns {number} - */ -NetEncoding.Uint32Array.prototype.data = function(index) { - var offset = this.bb.__offset(this.bb_pos, 4); - return offset ? this.bb.readUint32(this.bb.__vector(this.bb_pos + offset) + index * 4) : 0; -}; - -/** - * @returns {number} - */ -NetEncoding.Uint32Array.prototype.dataLength = function() { - var offset = this.bb.__offset(this.bb_pos, 4); - return offset ? this.bb.__vector_len(this.bb_pos + offset) : 0; -}; - -/** - * @returns {Uint32Array} - */ -NetEncoding.Uint32Array.prototype.dataArray = function() { - var offset = this.bb.__offset(this.bb_pos, 4); - return offset ? new Uint32Array(this.bb.bytes().buffer, this.bb.bytes().byteOffset + this.bb.__vector(this.bb_pos + offset), this.bb.__vector_len(this.bb_pos + offset)) : null; -}; - -/** - * @param {flatbuffers.Builder} builder - */ -NetEncoding.Uint32Array.startUint32Array = function(builder) { - builder.startObject(1); -}; - -/** - * @param {flatbuffers.Builder} builder - * @param {flatbuffers.Offset} dataOffset - */ -NetEncoding.Uint32Array.addData = function(builder, dataOffset) { - builder.addFieldOffset(0, dataOffset, 0); -}; - -/** - * @param {flatbuffers.Builder} builder - * @param {Array.} data - * @returns {flatbuffers.Offset} - */ -NetEncoding.Uint32Array.createDataVector = function(builder, data) { - builder.startVector(4, data.length, 4); - for (var i = data.length - 1; i >= 0; i--) { - builder.addInt32(data[i]); - } - return builder.endVector(); -}; - -/** - * @param {flatbuffers.Builder} builder - * @param {number} numElems - */ -NetEncoding.Uint32Array.startDataVector = function(builder, numElems) { - builder.startVector(4, numElems, 4); -}; - -/** - * @param {flatbuffers.Builder} builder - * @returns {flatbuffers.Offset} - */ -NetEncoding.Uint32Array.endUint32Array = function(builder) { - var offset = builder.endObject(); - return offset; -}; - -/** - * @constructor - */ -NetEncoding.Int32Array = function() { - /** - * @type {flatbuffers.ByteBuffer} - */ - this.bb = null; - - /** - * @type {number} - */ - this.bb_pos = 0; -}; - -/** - * @param {number} i - * @param {flatbuffers.ByteBuffer} bb - * @returns {NetEncoding.Int32Array} - */ -NetEncoding.Int32Array.prototype.__init = function(i, bb) { - this.bb_pos = i; - this.bb = bb; - return this; -}; - -/** - * @param {flatbuffers.ByteBuffer} bb - * @param {NetEncoding.Int32Array=} obj - * @returns {NetEncoding.Int32Array} - */ -NetEncoding.Int32Array.getRootAsInt32Array = function(bb, obj) { - return (obj || new NetEncoding.Int32Array).__init(bb.readInt32(bb.position()) + bb.position(), bb); -}; - -/** - * @param {number} index - * @returns {number} - */ -NetEncoding.Int32Array.prototype.data = function(index) { - var offset = this.bb.__offset(this.bb_pos, 4); - return offset ? this.bb.readInt32(this.bb.__vector(this.bb_pos + offset) + index * 4) : 0; -}; - -/** - * @returns {number} - */ -NetEncoding.Int32Array.prototype.dataLength = function() { - var offset = this.bb.__offset(this.bb_pos, 4); - return offset ? this.bb.__vector_len(this.bb_pos + offset) : 0; -}; - -/** - * @returns {Int32Array} - */ -NetEncoding.Int32Array.prototype.dataArray = function() { - var offset = this.bb.__offset(this.bb_pos, 4); - return offset ? new Int32Array(this.bb.bytes().buffer, this.bb.bytes().byteOffset + this.bb.__vector(this.bb_pos + offset), this.bb.__vector_len(this.bb_pos + offset)) : null; -}; - -/** - * @param {flatbuffers.Builder} builder - */ -NetEncoding.Int32Array.startInt32Array = function(builder) { - builder.startObject(1); -}; - -/** - * @param {flatbuffers.Builder} builder - * @param {flatbuffers.Offset} dataOffset - */ -NetEncoding.Int32Array.addData = function(builder, dataOffset) { - builder.addFieldOffset(0, dataOffset, 0); -}; - -/** - * @param {flatbuffers.Builder} builder - * @param {Array.} data - * @returns {flatbuffers.Offset} - */ -NetEncoding.Int32Array.createDataVector = function(builder, data) { - builder.startVector(4, data.length, 4); - for (var i = data.length - 1; i >= 0; i--) { - builder.addInt32(data[i]); - } - return builder.endVector(); -}; - -/** - * @param {flatbuffers.Builder} builder - * @param {number} numElems - */ -NetEncoding.Int32Array.startDataVector = function(builder, numElems) { - builder.startVector(4, numElems, 4); -}; - -/** - * @param {flatbuffers.Builder} builder - * @returns {flatbuffers.Offset} - */ -NetEncoding.Int32Array.endInt32Array = function(builder) { - var offset = builder.endObject(); - return offset; -}; - -/** - * @constructor - */ -NetEncoding.Float64Array = function() { - /** - * @type {flatbuffers.ByteBuffer} - */ - this.bb = null; - - /** - * @type {number} - */ - this.bb_pos = 0; -}; - -/** - * @param {number} i - * @param {flatbuffers.ByteBuffer} bb - * @returns {NetEncoding.Float64Array} - */ -NetEncoding.Float64Array.prototype.__init = function(i, bb) { - this.bb_pos = i; - this.bb = bb; - return this; -}; - -/** - * @param {flatbuffers.ByteBuffer} bb - * @param {NetEncoding.Float64Array=} obj - * @returns {NetEncoding.Float64Array} - */ -NetEncoding.Float64Array.getRootAsFloat64Array = function(bb, obj) { - return (obj || new NetEncoding.Float64Array).__init(bb.readInt32(bb.position()) + bb.position(), bb); -}; - -/** - * @param {number} index - * @returns {number} - */ -NetEncoding.Float64Array.prototype.data = function(index) { - var offset = this.bb.__offset(this.bb_pos, 4); - return offset ? this.bb.readFloat64(this.bb.__vector(this.bb_pos + offset) + index * 8) : 0; -}; - -/** - * @returns {number} - */ -NetEncoding.Float64Array.prototype.dataLength = function() { - var offset = this.bb.__offset(this.bb_pos, 4); - return offset ? this.bb.__vector_len(this.bb_pos + offset) : 0; -}; - -/** - * @returns {Float64Array} - */ -NetEncoding.Float64Array.prototype.dataArray = function() { - var offset = this.bb.__offset(this.bb_pos, 4); - return offset ? new Float64Array(this.bb.bytes().buffer, this.bb.bytes().byteOffset + this.bb.__vector(this.bb_pos + offset), this.bb.__vector_len(this.bb_pos + offset)) : null; -}; - -/** - * @param {flatbuffers.Builder} builder - */ -NetEncoding.Float64Array.startFloat64Array = function(builder) { - builder.startObject(1); -}; - -/** - * @param {flatbuffers.Builder} builder - * @param {flatbuffers.Offset} dataOffset - */ -NetEncoding.Float64Array.addData = function(builder, dataOffset) { - builder.addFieldOffset(0, dataOffset, 0); -}; - -/** - * @param {flatbuffers.Builder} builder - * @param {Array.} data - * @returns {flatbuffers.Offset} - */ -NetEncoding.Float64Array.createDataVector = function(builder, data) { - builder.startVector(8, data.length, 8); - for (var i = data.length - 1; i >= 0; i--) { - builder.addFloat64(data[i]); - } - return builder.endVector(); -}; - -/** - * @param {flatbuffers.Builder} builder - * @param {number} numElems - */ -NetEncoding.Float64Array.startDataVector = function(builder, numElems) { - builder.startVector(8, numElems, 8); -}; - -/** - * @param {flatbuffers.Builder} builder - * @returns {flatbuffers.Offset} - */ -NetEncoding.Float64Array.endFloat64Array = function(builder) { - var offset = builder.endObject(); - return offset; -}; - -/** - * @constructor - */ -NetEncoding.JSONEncodedArray = function() { - /** - * @type {flatbuffers.ByteBuffer} - */ - this.bb = null; - - /** - * @type {number} - */ - this.bb_pos = 0; -}; - -/** - * @param {number} i - * @param {flatbuffers.ByteBuffer} bb - * @returns {NetEncoding.JSONEncodedArray} - */ -NetEncoding.JSONEncodedArray.prototype.__init = function(i, bb) { - this.bb_pos = i; - this.bb = bb; - return this; -}; - -/** - * @param {flatbuffers.ByteBuffer} bb - * @param {NetEncoding.JSONEncodedArray=} obj - * @returns {NetEncoding.JSONEncodedArray} - */ -NetEncoding.JSONEncodedArray.getRootAsJSONEncodedArray = function(bb, obj) { - return (obj || new NetEncoding.JSONEncodedArray).__init(bb.readInt32(bb.position()) + bb.position(), bb); -}; - -/** - * @param {number} index - * @returns {number} - */ -NetEncoding.JSONEncodedArray.prototype.data = function(index) { - var offset = this.bb.__offset(this.bb_pos, 4); - return offset ? this.bb.readUint8(this.bb.__vector(this.bb_pos + offset) + index) : 0; -}; - -/** - * @returns {number} - */ -NetEncoding.JSONEncodedArray.prototype.dataLength = function() { - var offset = this.bb.__offset(this.bb_pos, 4); - return offset ? this.bb.__vector_len(this.bb_pos + offset) : 0; -}; - -/** - * @returns {Uint8Array} - */ -NetEncoding.JSONEncodedArray.prototype.dataArray = function() { - var offset = this.bb.__offset(this.bb_pos, 4); - return offset ? new Uint8Array(this.bb.bytes().buffer, this.bb.bytes().byteOffset + this.bb.__vector(this.bb_pos + offset), this.bb.__vector_len(this.bb_pos + offset)) : null; -}; - -/** - * @param {flatbuffers.Builder} builder - */ -NetEncoding.JSONEncodedArray.startJSONEncodedArray = function(builder) { - builder.startObject(1); -}; - -/** - * @param {flatbuffers.Builder} builder - * @param {flatbuffers.Offset} dataOffset - */ -NetEncoding.JSONEncodedArray.addData = function(builder, dataOffset) { - builder.addFieldOffset(0, dataOffset, 0); -}; - -/** - * @param {flatbuffers.Builder} builder - * @param {Array.} data - * @returns {flatbuffers.Offset} - */ -NetEncoding.JSONEncodedArray.createDataVector = function(builder, data) { - builder.startVector(1, data.length, 1); - for (var i = data.length - 1; i >= 0; i--) { - builder.addInt8(data[i]); - } - return builder.endVector(); -}; - -/** - * @param {flatbuffers.Builder} builder - * @param {number} numElems - */ -NetEncoding.JSONEncodedArray.startDataVector = function(builder, numElems) { - builder.startVector(1, numElems, 1); -}; - -/** - * @param {flatbuffers.Builder} builder - * @returns {flatbuffers.Offset} - */ -NetEncoding.JSONEncodedArray.endJSONEncodedArray = function(builder) { - var offset = builder.endObject(); - return offset; -}; - -/** - * @constructor - */ -NetEncoding.Column = function() { - /** - * @type {flatbuffers.ByteBuffer} - */ - this.bb = null; - - /** - * @type {number} - */ - this.bb_pos = 0; -}; - -/** - * @param {number} i - * @param {flatbuffers.ByteBuffer} bb - * @returns {NetEncoding.Column} - */ -NetEncoding.Column.prototype.__init = function(i, bb) { - this.bb_pos = i; - this.bb = bb; - return this; -}; - -/** - * @param {flatbuffers.ByteBuffer} bb - * @param {NetEncoding.Column=} obj - * @returns {NetEncoding.Column} - */ -NetEncoding.Column.getRootAsColumn = function(bb, obj) { - return (obj || new NetEncoding.Column).__init(bb.readInt32(bb.position()) + bb.position(), bb); -}; - -/** - * @returns {NetEncoding.TypedArray} - */ -NetEncoding.Column.prototype.uType = function() { - var offset = this.bb.__offset(this.bb_pos, 4); - return offset ? /** @type {NetEncoding.TypedArray} */ (this.bb.readUint8(this.bb_pos + offset)) : NetEncoding.TypedArray.NONE; -}; - -/** - * @param {flatbuffers.Table} obj - * @returns {?flatbuffers.Table} - */ -NetEncoding.Column.prototype.u = function(obj) { - var offset = this.bb.__offset(this.bb_pos, 6); - return offset ? this.bb.__union(obj, this.bb_pos + offset) : null; -}; - -/** - * @param {flatbuffers.Builder} builder - */ -NetEncoding.Column.startColumn = function(builder) { - builder.startObject(2); -}; - -/** - * @param {flatbuffers.Builder} builder - * @param {NetEncoding.TypedArray} uType - */ -NetEncoding.Column.addUType = function(builder, uType) { - builder.addFieldInt8(0, uType, NetEncoding.TypedArray.NONE); -}; - -/** - * @param {flatbuffers.Builder} builder - * @param {flatbuffers.Offset} uOffset - */ -NetEncoding.Column.addU = function(builder, uOffset) { - builder.addFieldOffset(1, uOffset, 0); -}; - -/** - * @param {flatbuffers.Builder} builder - * @returns {flatbuffers.Offset} - */ -NetEncoding.Column.endColumn = function(builder) { - var offset = builder.endObject(); - return offset; -}; - -/** - * @constructor - */ -NetEncoding.Matrix = function() { - /** - * @type {flatbuffers.ByteBuffer} - */ - this.bb = null; - - /** - * @type {number} - */ - this.bb_pos = 0; -}; - -/** - * @param {number} i - * @param {flatbuffers.ByteBuffer} bb - * @returns {NetEncoding.Matrix} - */ -NetEncoding.Matrix.prototype.__init = function(i, bb) { - this.bb_pos = i; - this.bb = bb; - return this; -}; - -/** - * @param {flatbuffers.ByteBuffer} bb - * @param {NetEncoding.Matrix=} obj - * @returns {NetEncoding.Matrix} - */ -NetEncoding.Matrix.getRootAsMatrix = function(bb, obj) { - return (obj || new NetEncoding.Matrix).__init(bb.readInt32(bb.position()) + bb.position(), bb); -}; - -/** - * @returns {number} - */ -NetEncoding.Matrix.prototype.nRows = function() { - var offset = this.bb.__offset(this.bb_pos, 4); - return offset ? this.bb.readUint32(this.bb_pos + offset) : 0; -}; - -/** - * @returns {number} - */ -NetEncoding.Matrix.prototype.nCols = function() { - var offset = this.bb.__offset(this.bb_pos, 6); - return offset ? this.bb.readUint32(this.bb_pos + offset) : 0; -}; - -/** - * @param {number} index - * @param {NetEncoding.Column=} obj - * @returns {NetEncoding.Column} - */ -NetEncoding.Matrix.prototype.columns = function(index, obj) { - var offset = this.bb.__offset(this.bb_pos, 8); - return offset ? (obj || new NetEncoding.Column).__init(this.bb.__indirect(this.bb.__vector(this.bb_pos + offset) + index * 4), this.bb) : null; -}; - -/** - * @returns {number} - */ -NetEncoding.Matrix.prototype.columnsLength = function() { - var offset = this.bb.__offset(this.bb_pos, 8); - return offset ? this.bb.__vector_len(this.bb_pos + offset) : 0; -}; - -/** - * @returns {NetEncoding.TypedArray} - */ -NetEncoding.Matrix.prototype.colIndexType = function() { - var offset = this.bb.__offset(this.bb_pos, 10); - return offset ? /** @type {NetEncoding.TypedArray} */ (this.bb.readUint8(this.bb_pos + offset)) : NetEncoding.TypedArray.NONE; -}; - -/** - * @param {flatbuffers.Table} obj - * @returns {?flatbuffers.Table} - */ -NetEncoding.Matrix.prototype.colIndex = function(obj) { - var offset = this.bb.__offset(this.bb_pos, 12); - return offset ? this.bb.__union(obj, this.bb_pos + offset) : null; -}; - -/** - * @returns {NetEncoding.TypedArray} - */ -NetEncoding.Matrix.prototype.rowIndexType = function() { - var offset = this.bb.__offset(this.bb_pos, 14); - return offset ? /** @type {NetEncoding.TypedArray} */ (this.bb.readUint8(this.bb_pos + offset)) : NetEncoding.TypedArray.NONE; -}; - -/** - * @param {flatbuffers.Table} obj - * @returns {?flatbuffers.Table} - */ -NetEncoding.Matrix.prototype.rowIndex = function(obj) { - var offset = this.bb.__offset(this.bb_pos, 16); - return offset ? this.bb.__union(obj, this.bb_pos + offset) : null; -}; - -/** - * @param {flatbuffers.Builder} builder - */ -NetEncoding.Matrix.startMatrix = function(builder) { - builder.startObject(7); -}; - -/** - * @param {flatbuffers.Builder} builder - * @param {number} nRows - */ -NetEncoding.Matrix.addNRows = function(builder, nRows) { - builder.addFieldInt32(0, nRows, 0); -}; - -/** - * @param {flatbuffers.Builder} builder - * @param {number} nCols - */ -NetEncoding.Matrix.addNCols = function(builder, nCols) { - builder.addFieldInt32(1, nCols, 0); -}; - -/** - * @param {flatbuffers.Builder} builder - * @param {flatbuffers.Offset} columnsOffset - */ -NetEncoding.Matrix.addColumns = function(builder, columnsOffset) { - builder.addFieldOffset(2, columnsOffset, 0); -}; - -/** - * @param {flatbuffers.Builder} builder - * @param {Array.} data - * @returns {flatbuffers.Offset} - */ -NetEncoding.Matrix.createColumnsVector = function(builder, data) { - builder.startVector(4, data.length, 4); - for (var i = data.length - 1; i >= 0; i--) { - builder.addOffset(data[i]); - } - return builder.endVector(); -}; - -/** - * @param {flatbuffers.Builder} builder - * @param {number} numElems - */ -NetEncoding.Matrix.startColumnsVector = function(builder, numElems) { - builder.startVector(4, numElems, 4); -}; - -/** - * @param {flatbuffers.Builder} builder - * @param {NetEncoding.TypedArray} colIndexType - */ -NetEncoding.Matrix.addColIndexType = function(builder, colIndexType) { - builder.addFieldInt8(3, colIndexType, NetEncoding.TypedArray.NONE); -}; - -/** - * @param {flatbuffers.Builder} builder - * @param {flatbuffers.Offset} colIndexOffset - */ -NetEncoding.Matrix.addColIndex = function(builder, colIndexOffset) { - builder.addFieldOffset(4, colIndexOffset, 0); -}; - -/** - * @param {flatbuffers.Builder} builder - * @param {NetEncoding.TypedArray} rowIndexType - */ -NetEncoding.Matrix.addRowIndexType = function(builder, rowIndexType) { - builder.addFieldInt8(5, rowIndexType, NetEncoding.TypedArray.NONE); -}; - -/** - * @param {flatbuffers.Builder} builder - * @param {flatbuffers.Offset} rowIndexOffset - */ -NetEncoding.Matrix.addRowIndex = function(builder, rowIndexOffset) { - builder.addFieldOffset(6, rowIndexOffset, 0); -}; - -/** - * @param {flatbuffers.Builder} builder - * @returns {flatbuffers.Offset} - */ -NetEncoding.Matrix.endMatrix = function(builder) { - var offset = builder.endObject(); - return offset; -}; - -/** - * @param {flatbuffers.Builder} builder - * @param {flatbuffers.Offset} offset - */ -NetEncoding.Matrix.finishMatrixBuffer = function(builder, offset) { - builder.finish(offset); -}; - -// Exports for ECMAScript6 Modules -export {NetEncoding}; diff --git a/client/src/util/stateManager/universe.js b/client/src/util/stateManager/universe.js deleted file mode 100644 index 35c586cf..00000000 --- a/client/src/util/stateManager/universe.js +++ /dev/null @@ -1,222 +0,0 @@ -// jshint esversion: 6 - -import _ from "lodash"; - -import decodeMatrixFBS from "./matrix"; -import * as Dataframe from "../dataframe"; -import fromEntries from "../fromEntries"; -import { isFpTypedArray } from "../typeHelpers"; - -/* -Private helper function - create and return a template Universe -*/ -function templateUniverse() { - /* default universe template */ - return { - nObs: 0, - nVar: 0, - schema: {}, - - /* - Annotations - */ - obsAnnotations: Dataframe.Dataframe.empty(), - varAnnotations: Dataframe.Dataframe.empty(), - obsLayout: Dataframe.Dataframe.empty(), - - /* - Var data columns - subset of all - */ - varData: Dataframe.Dataframe.empty(null, new Dataframe.KeyIndex()) - }; -} - -/* -This module implements functions that support storage of "Universe", -aka all of the var/obs data and annotations. - -These functions are used exclusively by the actions and reducers to -build an internal POJO for use by the rendering components. -*/ - -function promoteTypedArray(o) { - /* - Decide what internal data type to use for the data returned from - the server. - - TODO - future optimization: not all int32/uint32 data series require - promotion to float64. We COULD simply look at the data to decide. - */ - if (isFpTypedArray(o) || Array.isArray(o)) return o; - - let TyepdArrayCtor; - switch (o.constructor) { - case Int8Array: - case Uint8Array: - case Uint8ClampedArray: - case Int16Array: - case Uint16Array: - TyepdArrayCtor = Float32Array; - break; - - case Int32Array: - case Uint32Array: - TyepdArrayCtor = Float64Array; - break; - - default: - throw new Error("Unexpected data type returned from server."); - } - if (o.constructor === TyepdArrayCtor) return o; - return new TyepdArrayCtor(o); -} - -function AnnotationsFBSToDataframe(arrayBuffer) { - /* - Convert a Matrix FBS to a Dataframe. - - The application has strong assumptions that all scalar data will be - stored as a float32 or float64 (regardless of underlying data types). - For example, clipping of value ranges (eg, user-selected percentiles) - depends on the ability to use NaN in any numeric type. - - All float data from the server is left as is. All non-float is promoted - to an appropriate float. - */ - const fbs = decodeMatrixFBS(arrayBuffer, true); // leave in place - const columns = fbs.columns.map(c => { - if (isFpTypedArray(c) || Array.isArray(c)) return c; - return promoteTypedArray(c); - }); - const df = new Dataframe.Dataframe( - [fbs.nRows, fbs.nCols], - columns, - null, - new Dataframe.KeyIndex(fbs.colIdx) - ); - return df; -} - -function LayoutFBSToDataframe(arrayBuffer) { - const fbs = decodeMatrixFBS(arrayBuffer, true); - if (fbs.columns.length < 2 || !fbs.columns.every(isFpTypedArray)) { - // We have strong assumptions about the shape & type of layout data. - throw new Error("Unexpected layout data type returned from server"); - } - - const df = new Dataframe.Dataframe( - [fbs.nRows, fbs.nCols], - fbs.columns, - null, - new Dataframe.KeyIndex(fbs.colIdx) - ); - return df; -} - -function reconcileSchemaCategoriesWithSummary(universe) { - /* - where we treat types as (essentially) categorical metadata, update - the schema with data-derived categories (in addition to those in - the server declared schema). - - For example, boolean defined fields in the schema do not contain - explicit declaration of categories (nor do string fields). In these - cases, add a 'categories' field to the schema so it is accessible. - */ - - universe.schema.annotations.obs.columns.forEach(s => { - if ( - s.type === "string" || - s.type === "boolean" || - s.type === "categorical" - ) { - const categories = _.union( - s.categories ?? [], - universe.obsAnnotations.col(s.name).summarize().categories ?? [] - ); - s.categories = categories; - } - }); -} - -export function createUniverseFromResponse( - configResponse, - schemaResponse, - annotationsObsResponse, - annotationsVarResponse, - layoutFBSResponse -) { - /* - build & return universe from a REST 0.2 /config, /schema and /annotations/obs response - */ - const { schema } = schemaResponse; - const universe = templateUniverse(); - - /* schema related */ - universe.schema = schema; - universe.nObs = schema.dataframe.nObs; - universe.nVar = schema.dataframe.nVar; - /* add defaults, as we can't assume back-end will fully populate schema */ - if (!schema.layout.var) schema.layout.var = []; - if (!schema.layout.obs) schema.layout.obs = []; - - /* annotations */ - universe.obsAnnotations = AnnotationsFBSToDataframe(annotationsObsResponse); - universe.varAnnotations = AnnotationsFBSToDataframe(annotationsVarResponse); - /* layout */ - universe.obsLayout = LayoutFBSToDataframe(layoutFBSResponse); - - /* sanity check */ - if ( - universe.nObs !== universe.obsLayout.length || - universe.nObs !== universe.obsAnnotations.length || - universe.nVar !== universe.varAnnotations.length - ) { - throw new Error("Universe dimensionality mismatch - failed to load"); - } - - reconcileSchemaCategoriesWithSummary(universe); - - /* Index schema for ease of use */ - universe.schema.annotations.obsByName = fromEntries( - universe.schema.annotations.obs.columns.map(v => [v.name, v]) - ); - universe.schema.annotations.varByName = fromEntries( - universe.schema.annotations.var.columns.map(v => [v.name, v]) - ); - universe.schema.layout.obsByName = fromEntries( - universe.schema.layout.obs.map(v => [v.name, v]) - ); - universe.schema.layout.varByName = fromEntries( - universe.schema.layout.var.map(v => [v.name, v]) - ); - return universe; -} - -export function convertDataFBStoObject(universe, arrayBuffer) { - /* - /data/var returns a flatbuffer (FBS) as described by cellxgene/fbs/matrix.fbs - - This routine converts the binary wire encoding into a JS object: - - { - gene: Float32Array, - ... - } - */ - const fbs = decodeMatrixFBS(arrayBuffer); - const { colIdx, columns } = fbs; - const result = {}; - - if (!columns.every(isFpTypedArray)) { - // We have strong assumptions that all var data is float - throw new Error("Unexpected non-floating point response from server."); - } - - const varIndexName = universe.schema.annotations.var.index; - for (let c = 0; c < colIdx.length; c += 1) { - const varName = universe.varAnnotations.at(colIdx[c], varIndexName); - result[varName] = columns[c]; - } - return result; -} diff --git a/client/src/util/stateManager/world.js b/client/src/util/stateManager/world.js deleted file mode 100644 index 1cb03f3c..00000000 --- a/client/src/util/stateManager/world.js +++ /dev/null @@ -1,318 +0,0 @@ -// jshint esversion: 6 - -import clip from "../clip"; -import { - layoutDimensionName, - obsAnnoDimensionName, - diffexpDimensionName, - userDefinedDimensionName -} from "../nameCreators"; -import * as Dataframe from "../dataframe"; -import ImmutableTypedCrossfilter from "../typedCrossfilter/crossfilter"; - -/* - -World is a subset of universe. Most code should use world, and should -(generally) not use Universe. World contains any per-obs or per-var data -that must be consistent acorss the app when we view/manipulate subsets -of Universe. - -Private API indicated by leading underscore in key name (eg, _foo). Anything else -is public. - -Notable keys in the world object: - -* nObs, nVar: dimensions - -* schema: data schema from the server - -* clipQuantiles: the quantiles used to clip all data in world. - -* obsAnnotations: - - Dataframe containing obs annotations. Columns are indexed by annotation - name (eg, 'tissue type'), and rows are indexed by the REST API obsIndex - (ie, the offset into the underlying server-side dataframe). - - This indexing means that you can access data by _either_ the server's - obxIndex, or the offset into the client-side column array . Be careful - to know which you want and are using. - -* obsLayout: - - A dataframe containing the X/Y layout for all obs. Columns are named - 'X' and 'Y', and rows are indexed in the same way as obsAnnotation. - -* varData: a cache of expression columns, stored in a Dataframe. Cache - managed by controls reducer. - -* unclipped: will contain unclipped variants of all potentiall clipped - dataframes (obsAnnotations, varData). - -*/ - -function templateWorld() { - const obsAnnotations = Dataframe.Dataframe.empty(); - const varAnnotations = Dataframe.Dataframe.empty(); - const obsLayout = Dataframe.Dataframe.empty(); - const varData = Dataframe.Dataframe.empty(null, new Dataframe.KeyIndex()); - return { - /* schema/version related */ - schema: null, - nObs: 0, - nVar: 0, - clipQuantiles: { min: 0, max: 1 }, - - /* annotations */ - obsAnnotations, - varAnnotations, - - /* layout of graph. Dataframe. */ - obsLayout, - - /* Var data columns - subset of all data (may be empty) */ - varData, - - /* unclipped dataframes - subset, but not value clipped */ - unclipped: { - obsAnnotations, - varData - } - }; -} - -function clipDataframe( - df, - lowerQuantile, - upperQuantile, - quantileF, - clipPredicate = () => true, - value = Number.NaN -) { - /* - For all columns in the dataframe, clip all values above or below specified - quantiles to `value` if clipPredicate returns True for that column (if it - returns false, skip the column entirely). - - Returns a clipped copy - does not mutate original. - - clipPredicate must have signature: (dataframe, colIndex, colLabel) => boolean - True signifies that the column should be clipped; false indicates that the - column should be left intact/unchanged. - - quantileF must have signature: (label, qval) => number - */ - if (lowerQuantile < 0) lowerQuantile = 0; - if (upperQuantile > 1) upperQuantile = 1; - if (lowerQuantile === 0 && upperQuantile === 1) return df; - - const keys = df.colIndex.keys(); - return df.mapColumns((col, colIdx) => { - const colLabel = keys[colIdx]; - if (!clipPredicate(df, colIdx, colLabel)) return col; - - const colMin = quantileF(colLabel, lowerQuantile); - const colMax = quantileF(colLabel, upperQuantile); - const newCol = clip(col.slice(), colMin, colMax, value); - return newCol; - }); -} - -/* -Create World with contents eq entire universe. Commonly used to initialize World. -If clipQuantiles -*/ -export function createWorldFromEntireUniverse(universe) { - const world = templateWorld(); - - /* Schema related */ - world.schema = universe.schema; - world.nObs = universe.nObs; - world.nVar = universe.nVar; - world.clipQuantiles = { min: 0, max: 1 }; - - /* dataframes: annotations and layout */ - world.obsAnnotations = universe.obsAnnotations.clone(); - world.varAnnotations = universe.varAnnotations.clone(); - world.obsLayout = universe.obsLayout.clone(); - - /* Var dataframe - contains a subset of all var columns */ - world.varData = universe.varData.clone(); - - /* save unclipped copies of potentially clipped dataframes */ - world.unclipped = { - obsAnnotations: world.obsAnnotations.clone(), - varData: world.varData.clone() - }; - - return world; -} - -/* -clip dataframes based on quantiles. - -This is an in-place operation on the world object provided as an argument. -The values in world.unclipped are clipped and assigned to world.obsAnnotations -and world.varData. -*/ -function setClippedDataframes(world) { - const { schema } = world; - const isContinuousObsAnnotation = (df, idx, label) => - deduceDimensionType(schema.annotations.obsByName[label], label) !== "enum"; - const obsQuantile = (label, q) => - world.unclipped.obsAnnotations.col(label).summarize().percentiles[100 * q]; - world.obsAnnotations = clipDataframe( - world.unclipped.obsAnnotations, - world.clipQuantiles.min, - world.clipQuantiles.max, - obsQuantile, - isContinuousObsAnnotation - ); - - const varDataQuantile = (label, q) => - world.unclipped.varData.col(label).summarize().percentiles[100 * q]; - world.varData = clipDataframe( - world.unclipped.varData, - world.clipQuantiles.min, - world.clipQuantiles.max, - varDataQuantile, - () => true - ); -} - -/* -Subset the current world based upon the current selection, maintaining any existing -clip. Returns new world. Parameters: - * unvierse - * world - the current world - * crossfilter - the selection state -*/ -export function createWorldBySelection(universe, world, crossfilter) { - const newWorld = { ...world, obsLayout: null, unclipped: {}, varData: null }; - - /* subset unclipped dataframes based upon current selection */ - const mask = crossfilter.allSelectedMask(); - newWorld.obsLayout = world.obsLayout.isubsetMask(mask); - newWorld.unclipped.obsAnnotations = world.unclipped.obsAnnotations.isubsetMask( - mask - ); - if (world.unclipped.varData.isEmpty()) { - newWorld.unclipped.varData = world.unclipped.varData.clone(); - } else { - newWorld.unclipped.varData = world.unclipped.varData.isubsetMask(mask); - } - /* subsetting changings dimension size */ - newWorld.nObs = newWorld.unclipped.obsAnnotations.dims[0]; - - /* and now clip */ - setClippedDataframes(newWorld); - return newWorld; -} - -/* -Change clip quantiles on the current world, returning a new world. -Parameters: - * universe - * world - current world - * clipQuantiles - new clip -*/ -export function createWorldWithNewClip( - universe, - world, - crossfilter, - clipQuantiles -) { - const newWorld = { ...world, obsAnnotation: null, varData: null }; - newWorld.clipQuantiles = clipQuantiles; - newWorld.obsLayout = world.obsLayout.clone(); - newWorld.unclipped = { - obsAnnotations: world.unclipped.obsAnnotations.clone(), - varData: world.unclipped.varData.clone() - }; - - /* and now clip */ - setClippedDataframes(newWorld); - return newWorld; -} - -/* - Deduce the correct crossfilter dimension type from a metadata - schema description. -*/ -function deduceDimensionType(attributes, fieldName) { - let dimensionType; - const { type } = attributes; - if (type === "string" || type === "categorical" || type === "boolean") { - dimensionType = "enum"; - } else if (type === "int32") { - dimensionType = Int32Array; - } else if (type === "float32") { - dimensionType = Float32Array; - } else { - /* - Currently not supporting boolean and categorical types. - */ - console.error( - `Warning - REST API returned unknown metadata schema (${type}) for field ${fieldName}.` - ); - // skip it - we don't know what to do with this type - } - return dimensionType; -} - -export function createObsDimensions(crossfilter, world, XYdimNames) { - /* - create and return a crossfilter with a dimension for every obs annotation - for which we have a supported type, *except* for the index column, indicated - by schema.annotations.obs.index. - */ - const { schema, obsLayout, obsAnnotations } = world; - const indexName = schema.annotations.obs.index; - const annoList = schema.annotations.obs.columns.filter( - anno => anno.name !== indexName - ); - crossfilter = annoList.reduce((xfltr, anno) => { - const dimType = deduceDimensionType(anno, anno.name); - const colData = obsAnnotations.col(anno.name).asArray(); - const name = obsAnnoDimensionName(anno.name); - if (dimType === "enum") { - return xfltr.addDimension(name, "enum", colData); - } - if (dimType) { - return xfltr.addDimension(name, "scalar", colData, dimType); - } - return xfltr; - }, crossfilter); - - return crossfilter.addDimension( - layoutDimensionName("XY"), - "spatial", - obsLayout.col(XYdimNames[0]).asArray(), - obsLayout.col(XYdimNames[1]).asArray() - ); -} - -export function worldEqUniverse(world, universe) { - return ( - world.obsAnnotations === universe.obsAnnotations || - world.obsAnnotations.rowIndex === universe.obsAnnotations.rowIndex - ); -} - -export function getSelectedByIndex(crossfilter) { - /* - return array of obsIndex, containing all selected obs/cells. - */ - const selected = crossfilter.allSelectedMask(); // array of bool-ish - const keys = crossfilter.data.rowIndex.keys(); // row keys, aka universe rowIndex - - const set = new Int32Array(selected.length); - let numElems = 0; - for (let i = 0, l = selected.length; i < l; i += 1) { - if (selected[i]) { - set[numElems] = keys[i]; - numElems += 1; - } - } - return new Int32Array(set.buffer, 0, numElems); -} diff --git a/client/src/util/stateManager/worldUtil.js b/client/src/util/stateManager/worldUtil.js deleted file mode 100644 index d51eb638..00000000 --- a/client/src/util/stateManager/worldUtil.js +++ /dev/null @@ -1,75 +0,0 @@ -/* eslint-disable import/prefer-default-export */ -import _ from "lodash"; - -/* -Various utility functions operating on World/Universe -*/ - -/* -Count unique category values, binning first by dim1 then by dim2 -Return: - -Map { - dim1_val1: Map { - dim2_val1: number, - dim2_val2: number, - ... - }, - ... -} - -Parameters are: -- dim1: dimension 1 name/label -- dim2: dimension 2 name/label -- df: dataframe containing dim1 and dim2 on the column axis - -*/ -function _countCategoryValues2D(dim1, dim2, df) { - const dimMap = new Map(); - const col1 = df.col(dim1) ? df.col(dim1).asArray() : null; - const col2 = df.col(dim2) ? df.col(dim2).asArray() : null; - if (!col1 || !col2) { - return dimMap; - } - - for (let r = 0, l = df.length; r < l; r += 1) { - const val1 = col1[r]; - const val2 = col2[r]; - let d2Map = dimMap.get(val1); - if (d2Map === undefined) { - d2Map = new Map(); - dimMap.set(val1, d2Map); - } - let curCount = d2Map.get(val2); - if (curCount === undefined) { - curCount = 0; - } - d2Map.set(val2, curCount + 1); - } - return dimMap; -} - -let __worldUtilMemoId__ = 0; -function _memoizedId(x) { - if (!x.__worldUtilMemoId__) { - __worldUtilMemoId__ += 1; - x.__worldUtilMemoId__ = __worldUtilMemoId__; - } - return x.__worldUtilMemoId__; -} -function _countCategoryValues2DResolver(...args) { - const id = args[0] + args[1] + _memoizedId(args[2]); - return id; -} - -export const countCategoryValues2D = _.memoize( - _countCategoryValues2D, - _countCategoryValues2DResolver -); - -/* -Clear any cached data within WorldUtil caches, eg, memoized functions -*/ -export function clearCaches() { - countCategoryValues2D.cache.clear(); -} diff --git a/client/src/util/statemachine/index.js b/client/src/util/statemachine/index.js deleted file mode 100644 index a37b2a3f..00000000 --- a/client/src/util/statemachine/index.js +++ /dev/null @@ -1,95 +0,0 @@ -/* -Very simple FSM for use in reducer, etc. - -To create a state machine: - new StateMachine(initialState, transitions, onErrorCallback) -> statemachine - -Where: - * initialState - a caller-specified value that represents the initial state of - the FSM. - * transitions - an array of objects, representing FSM transitions (graph edges), - having the form: - { - to: state_name_transitioning_to, - from: state_name_transitioning_from, - event: value_that_will_cause_transition, - action: optional_callback_upon_transition - } - The transition will be provided to the action callback, so other data - may be stored in the transition object for use by the action callback. - * onErrorCallback - a callback function called if the FSM receives an event - for which it has no defined transition. - - -Interface: - * states - property containing the state names. A Set(), contianing the - union of to: and from: values. - * events - property containing all of the accepted event values. Set(). - * graph - a Map of Maps, organized as graph[eventValue][fromStateValue] - * clone() - clone the entire statemachine. - * next(eventValue) - drive the FSM to the next state. If the event - matches a transition with a defined action, the action callback is - called, and the action return value is returned by next(). If no - transition is defined, onErrorCallback is called. - -Example: - - const transitions = [ - { from: "A", to: "B", event: "yo", action: () => 42 } - ]; - const fsm = new StateMachine("A", transitions, () => { throw new Error("oops") }); - fsm.next("yo"); // returns 42 - - -*/ -export default class StateMachine { - constructor(initState, transitions, onError) { - this.onError = onError || (() => undefined); - this.state = initState; - - // all states - this.states = new Set( - transitions.reduce((names, tsn) => { - names.push(tsn.from); - names.push(tsn.to); - return names; - }, []) - ); - - // all transition names (aka events) - this.events = new Set(transitions.map(tsn => tsn.event)); - - // the transition graph. - // graph[event][from] -> transition - this.graph = transitions.reduce((graph, tsn) => { - const { event, from } = tsn; - if (!graph.has(event)) graph.set(event, new Map()); - const tsnMap = graph.get(event); - tsnMap.set(from, tsn); - return graph; - }, new Map()); - } - - clone(initState) { - const fsm = new StateMachine(initState, []); - fsm.onError = this.onError; - fsm.states = this.states; - fsm.events = this.events; - fsm.graph = this.graph; - return fsm; - } - - next(event, data) { - const { graph, state } = this; - const tsnMap = graph.get(event); - if (!tsnMap) return this.onError(this, event, state, undefined); - - const transition = tsnMap.get(state); - if (!transition) return this.onError(this, event, state, undefined); - - this.state = transition.to; - return transition.action - ? transition.action(this, transition, data) - : undefined; - } -} diff --git a/client/src/util/typeHelpers.js b/client/src/util/typeHelpers.js deleted file mode 100644 index d0dbb1bb..00000000 --- a/client/src/util/typeHelpers.js +++ /dev/null @@ -1,29 +0,0 @@ -/* -Various type and schema related helper functions. -*/ - -/* -Utility function to test for a typed array -*/ -export function isTypedArray(x) { - return ( - ArrayBuffer.isView(x) && - Object.prototype.toString.call(x) !== "[object DataView]" - ); -} - -/* -Test for float typed array, ie, Float32TypedArray or Float64TypedArray -*/ -export function isFpTypedArray(x) { - let constructor; - const isFloatArray = - x && - ({ constructor } = x) && - (constructor === Float32Array || constructor === Float64Array); - return isFloatArray; -} - -export function isArrayOrTypedArray(x) { - return Array.isArray(x) || isTypedArray(x); -} diff --git a/client/src/util/typedCrossfilter/bitArray.js b/client/src/util/typedCrossfilter/bitArray.js deleted file mode 100644 index 51ff809a..00000000 --- a/client/src/util/typedCrossfilter/bitArray.js +++ /dev/null @@ -1,291 +0,0 @@ -// jshint esversion: 6 -/* eslint no-bitwise: "off" */ - -// BitArray is a 2D bitarray with size [length, nBitWidth]. -// Each bit is referred to as a `dimension`. Dimensions may be -// dynamically allocated and deallocated. The overall length -// of the BitArray is fixed at creation time (for simplicity). -// -// Organization of the bitarray is dimension-major. As dimensions -// are added, the underlying store is grown 32 bits at a time. -// NOTE: currently does not deallocate / shrink. -// -// Primary operations on the BitArray are: -// - set & clear dimension -// - test dimension -// - various performance or convenience operations to optimize bulk ops -// -// The underlying data structure uses TypedArrays for performance. -// -class BitArray { - constructor(length) { - // Initially allocate a 32 bit wide array. allocDimension() will expand - // as necessary. - // - // Int32Array is (counterintuitively) used to accomadate JS numeric casting - // (to/from primitive number type). - // - - // Fixed for the life of this object. - this.length = length; - - // Bitarray width. width is always greater than dimensionCount/32. - this.width = 1; // underlying number of 32 bit arrays - this.dimensionCount = 0; // num allocated dimensions - - this.bitmask = new Int32Array(this.width); // dimension allocation mask - this.bitarray = new Int32Array(this.width * this.length); - } - - // Return the number of records that are selected, ie, have a one bit in - // all allocated dimensions. - // - selectionCount() { - return this.countAllOnes(); - } - - // Count all records that have a 'one' bit in allocated dimensions. - // - countAllOnes() { - let count = 0; - const { bitarray, length, width } = this; - if (width === 1) { - // special case, width === 1, for performance - const bitmask = this.bitmask[0]; - for (let l = 0; l < length; l += 1) { - if (bitarray[l] === bitmask) { - count += 1; - } - } - } else { - const { bitmask } = this; - for (let l = 0; l < length; l += 1) { - let dimensionsSet = 0; - for (let w = 0; w < width; w += 1) { - if (bitarray[w * length + l] === bitmask[w]) { - dimensionsSet += 1; - } - } - if (dimensionsSet === width) { - count += 1; - } - } - } - return count; - } - - // count trailing zeros - hard to do fast in JS! - // https://en.wikipedia.org/wiki/Find_first_set#CTZ - static ctz(av) { - let c = 32; - let v = av; - v &= -v; // isolate lowest non-zero bit - if (v) c -= 1; - if (v & 0x0000ffff) c -= 16; - if (v & 0x00ff00ff) c -= 8; - if (v & 0x0f0f0f0f) c -= 4; - if (v & 0x33333333) c -= 2; - if (v & 0x55555555) c -= 1; - return c; - } - - // find a free dimension. Return undefined if none - _findFreeDimension() { - let dim; - for (let col = 0; col < this.width; col += 1) { - const lowestZeroBit = ~this.bitmask[col] & -~this.bitmask[col]; - if (lowestZeroBit) { - this.bitmask[col] |= lowestZeroBit; - dim = 32 * col + BitArray.ctz(lowestZeroBit); - } - } - return dim; - } - - // allocate and return the dimension ID (bit position) - // - allocDimension() { - let dim = this._findFreeDimension(); - - // if we did not find free dimension, expand the bitarray. - if (dim === undefined) { - this.width += 1; - - const biggerBitArray = new Int32Array(this.width * this.length); - biggerBitArray.set(this.bitarray); - this.bitarray = biggerBitArray; - - const biggerBitmask = new Int32Array(this.width); - biggerBitmask.set(this.bitmask); - this.bitmask = biggerBitmask; - - dim = this._findFreeDimension(); - } - - this.dimensionCount += 1; - return dim; - } - - // free a dimension for later use. MUST deselect the dimension, as other - // code assume the column will be zero valued. - // - freeDimension(dim) { - // all selection tests assume unallocated dimensions are zero valued. - this.deselectAll(dim); - const col = dim >>> 5; - this.bitmask[col] &= ~(1 << dim % 32); - this.dimensionCount -= 1; - } - - // return true if this index is selected in ALL dimensions. - // - isSelected(index) { - const { width, length, bitarray } = this; - - for (let w = 0; w < width; w += 1) { - const bitmask = this.bitmask[w]; - if (!bitmask || bitarray[w * length + index] !== bitmask) return false; - } - return true; - } - - // return true if this index is selected in ALL dimensions IGNORING dim - // - isSelectedIgnoringDim(index, dim) { - const ignoreOffset = dim >>> 5; - const ignoreMask = ~(1 << dim % 32); - - const { width, length, bitarray } = this; - - for (let w = 0; w < width; w += 1) { - const bitmask = this.bitmask[w]; - if (w === ignoreOffset) { - if ( - bitmask && - (bitarray[w * length + index] & ignoreMask) !== (bitmask & ignoreMask) - ) { - return false; - } - } else if (bitmask && bitarray[w * length + index] !== bitmask) { - return false; - } - } - return true; - } - - // select index on dimension - // - selectOne(dim, index) { - const col = dim >>> 5; - const before = this.bitarray[col * this.length + index]; - const after = before | (1 << dim % 32); - this.bitarray[col * this.length + index] = after; - } - - // deselect index on dimension - // - deselectOne(dim, index) { - const col = dim >>> 5; - const before = this.bitarray[col * this.length + index]; - const after = before & ~(1 << dim % 32); - this.bitarray[col * this.length + index] = after; - } - - // select all indices on dimension. - // - selectAll(dim) { - const col = dim >> 5; - const one = 1 << dim % 32; - for (let i = col * this.length, len = i + this.length; i < len; i += 1) { - this.bitarray[i] |= one; - } - } - - // deselect all indices on dimension - // - deselectAll(dim) { - const col = dim >> 5; - const zero = ~(1 << dim % 32); - for (let i = col * this.length, len = i + this.length; i < len; i += 1) { - this.bitarray[i] &= zero; - } - } - - // select range of indices on a dimension - // - selectFromRange(dim, range) { - const col = dim >>> 5; - const first = range[0]; - const last = range[1]; - const one = 1 << dim % 32; - const offset = col * this.length; - for (let i = first; i < last; i += 1) { - this.bitarray[offset + i] |= one; - } - } - - // select range of indices on a dimension, indirect through a sort map. - // Indirect functions are used to map between sort and natural order. - // - selectIndirectFromRange(dim, indirect, range) { - const col = dim >>> 5; - const first = range[0]; - const last = range[1]; - const one = 1 << dim % 32; - const offset = col * this.length; - for (let i = first; i < last; i += 1) { - this.bitarray[offset + indirect[i]] |= one; - } - } - - // deselect range of indices on a dimension - // - deselectFromRange(dim, range) { - const col = dim >>> 5; - const first = range[0]; - const last = range[1]; - const zero = ~(1 << dim % 32); - const offset = col * this.length; - for (let i = first; i < last; i += 1) { - this.bitarray[offset + i] &= zero; - } - } - - // deselect range of indices on a dimension, indirect through a sort map. - // - deselectIndirectFromRange(dim, indirect, range) { - const col = dim >>> 5; - const first = range[0]; - const last = range[1]; - const zero = ~(1 << dim % 32); - const offset = col * this.length; - for (let i = first; i < last; i += 1) { - this.bitarray[offset + indirect[i]] &= zero; - } - } - - // Fill the array with selected|deselected value based upon the - // current selection state. - // - fillBySelection(result, selectedValue, deselectedValue) { - // special case (width === 1) for performance - if (this.width === 1) { - const { bitmask, bitarray } = this; - const mask = bitmask[0]; - if (!mask) { - result.fill(deselectedValue); - } else { - for (let i = 0, len = this.length; i < len; i += 1) { - result[i] = bitarray[i] === mask ? selectedValue : deselectedValue; - } - } - } else { - for (let i = 0, len = this.length; i < len; i += 1) { - result[i] = this.isSelected(i) ? selectedValue : deselectedValue; - } - } - return result; - } -} - -export default BitArray; diff --git a/client/src/util/typedCrossfilter/crossfilter.js b/client/src/util/typedCrossfilter/crossfilter.js deleted file mode 100644 index 7439c2d3..00000000 --- a/client/src/util/typedCrossfilter/crossfilter.js +++ /dev/null @@ -1,598 +0,0 @@ -import { polygonContains } from "d3"; - -import PositiveIntervals from "./positiveIntervals"; -import BitArray from "./bitArray"; -import { - sortArray, - lowerBound, - lowerBoundIndirect, - upperBoundIndirect -} from "./sort"; -import { makeSortIndex } from "./util"; - -class NotImplementedError extends Error { - constructor(...params) { - super(...params); - - // Maintains proper stack trace for where our error was thrown (only available on V8) - if (Error.captureStackTrace) { - Error.captureStackTrace(this, NotImplementedError); - } - } -} - -export default class ImmutableTypedCrossfilter { - constructor(data, dimensions = {}, selectionCache = null) { - /* - Typically, parameter 'data' is one of: - - Array of objects/records - - Dataframe (util/dataframe) - Other parameters are only used internally. - - Object field description: - - data: reference to the array of records in the crossfilter - - selectionBitArray: bit array containing the flatted selection state - of all dimensions. This is lazily created and is effectively - a perfomance cache. Methods which return a new crossfilter, - such as select(), addDimention() and delDimension(), will pass - the cache forward to the new object, as the typical "immutable API" - usage pattern is to retain the new crossfilter and discard the old. - - dimensions: contains each dimension and its current state: - - id: bit offset in the cached bit array - - dim: the dimension object - - name: the dimension name - - selection: the dimension's current selection - */ - this.data = data; - this.selectionCache = selectionCache; /* BitArray */ - this.dimensions = dimensions; /* name: { id, dim, name, selection } */ - } - - size() { - return this.data.length; - } - - all() { - return this.data; - } - - dimensionNames() { - /* return array of all dimensions (by name) */ - return Object.keys(this.dimensions); - } - - hasDimension(name) { - return !!this.dimensions[name]; - } - - addDimension(name, type, ...rest) { - /* - Add a new dimension to this crossfilter, of type DimensionType. - Remainder of parameters are dimension-type-specific. - */ - const { data, selectionCache } = this; - - if (this.dimensions[name] !== undefined) { - throw new Error(`Adding duplicate dimension name ${name}`); - } - - this.selectionCache = null; // pass ownership to new crossfilter - - let id; - if (selectionCache) { - id = selectionCache.allocDimension(); - selectionCache.selectAll(id); - } - const DimensionType = DimTypes[type]; - const dim = new DimensionType(name, data, ...rest); - const dimensions = { - ...this.dimensions, - [name]: { - id, - dim, - name, - selection: dim.select({ mode: "all" }) - } - }; - return new ImmutableTypedCrossfilter(data, dimensions, selectionCache); - } - - delDimension(name) { - const { data, selectionCache } = this; - const dimensions = { ...this.dimensions }; - if (dimensions[name] === undefined) { - throw new ReferenceError(`Unable to delete unknown dimension ${name}`); - } - - const { id } = dimensions[name]; - delete dimensions[name]; - this.selectionCache = null; // pass ownership to new crossfilter - if (selectionCache) { - selectionCache.freeDimension(id); - } - return new ImmutableTypedCrossfilter(data, dimensions, selectionCache); - } - - select(name, spec) { - /* - select on named dimension, as indicated by `spec`. Spec is an object - specifying the selection, and must contain at least a `mode` field. - - Examples: - select("foo", {mode: "all"}); - select("bar", {mode: "none"}); - select("mumble", {mode: "exact", values: "blue"}); - select("mumble", {mode: "exact", values: ["red", "green", "blue"]}); - select("blort", {mode: "range", lo: 0, hi: 999.99}); - */ - const { data, selectionCache } = this; - this.selectionCache = null; - const dimensions = { ...this.dimensions }; - const { dim, id, selection: oldSelection } = dimensions[name]; - const newSelection = dim.select(spec); - newSelection.ranges = PositiveIntervals.canonicalize(newSelection.ranges); - dimensions[name] = { id, dim, name, selection: newSelection }; - ImmutableTypedCrossfilter._dimSelnHasUpdated( - selectionCache, - id, - newSelection, - oldSelection - ); - return new ImmutableTypedCrossfilter(data, dimensions, selectionCache); - } - - static _dimSelnHasUpdated(selectionCache, id, newSeln, oldSeln) { - /* - Selection has updated from oldSeln to newSeln. Update the - bit array if it exists. If not, we will lazy create it when - needed. - */ - if (selectionCache) { - /* - if both new and old selection use the same index, we can - perform an incremental update. If the index changed, we have - to do a suboptimal full deselect/select. - */ - let adds; - let dels; - if (newSeln.index === oldSeln.index) { - adds = PositiveIntervals.difference(newSeln.ranges, oldSeln.ranges); - dels = PositiveIntervals.difference(oldSeln.ranges, newSeln.ranges); - } else { - // console.log("suboptimal selection update - index changed"); - adds = newSeln.ranges; - dels = oldSeln.ranges; - } - - /* - allow dimensions to return selected ranges in either dimension sort - order (indirect via index), or in original record order. - - If sort index exists in the dimension, assume sort ordered ranges. - */ - if (oldSeln.index) { - dels.forEach(interval => - selectionCache.deselectIndirectFromRange(id, oldSeln.index, interval) - ); - } else { - dels.forEach(interval => - selectionCache.deselectFromRange(id, interval) - ); - } - - if (newSeln.index) { - adds.forEach(interval => - selectionCache.selectIndirectFromRange(id, newSeln.index, interval) - ); - } else { - adds.forEach(interval => selectionCache.selectFromRange(id, interval)); - } - } - } - - _getSelectionCache() { - if (!this.selectionCache) { - // console.log("...rebuilding crossfilter cache..."); - const selectionCache = new BitArray(this.data.length); - Object.keys(this.dimensions).forEach(name => { - const { selection } = this.dimensions[name]; - const id = selectionCache.allocDimension(); - this.dimensions[name].id = id; - const { ranges, index } = selection; - ranges.forEach(range => { - if (index) { - selectionCache.selectIndirectFromRange(id, index, range); - } else { - selectionCache.selectFromRange(id, range); - } - }); - }); - this.selectionCache = selectionCache; - } - return this.selectionCache; - } - - allSelected() { - /* - return array of all records currently selected by all dimensions - */ - const selectionCache = this._getSelectionCache(); - const { data } = this; - if (Array.isArray(data)) { - const res = []; - for (let i = 0, len = data.length; i < len; i += 1) { - if (selectionCache.isSelected(i)) { - res.push(data[i]); - } - } - return res; - } - /* else, Dataframe-like */ - return data.isubsetMask(this.allSelectedMask()); - } - - allSelectedMask() { - /* - return Uint8Array containing selection state (truthy/falsey) for each record. - */ - const selectionCache = this._getSelectionCache(); - return selectionCache.fillBySelection( - new Uint8Array(this.data.length), - 1, - 0 - ); - } - - countSelected() { - /* - return number of records selected on all dimensions - */ - const selectionCache = this._getSelectionCache(); - return selectionCache.selectionCount(); - } - - isElementSelected(i) { - /* - return truthy/falsey if this record is selected on all dimensions - */ - const selectionCache = this._getSelectionCache(); - return selectionCache.isSelected(i); - } - - fillByIsSelected(array, selectedValue, deselectedValue) { - /* - fill array with one of two values, based upon selection state. - */ - const selectionCache = this._getSelectionCache(); - return selectionCache.fillBySelection( - array, - selectedValue, - deselectedValue - ); - } -} - -/* -Base dimension object. - -A Dimension is an index, accessed via a select() method. The protocol -for a dimension: - - constructor - first param is name, remainder is whatever params are - required to initialize the dimension. - - select - one and only param is the selection specifier. Returns an - array of record IDs. - - name - the dimension name/label. -*/ -class _ImmutableBaseDimension { - constructor(name) { - this.name = name; - } - - select(spec) { - const { mode } = spec; - if (mode === undefined) { - throw new Error("select spec does not contain 'mode'"); - } - throw new Error( - `select mode ${mode} not implemented by dimension ${this.name}` - ); - } -} - -class ImmutableScalarDimension extends _ImmutableBaseDimension { - constructor(name, data, value, ValueArrayType) { - super(name); - - // Three modes - caller can provide a pre-created value array, - // a map function which will create it, or another array which - // will used with an identity map function. - let array; - if (value instanceof ValueArrayType) { - // user has provided the final typed array - just use it - if (value.length !== data.length) { - throw new RangeError( - "ScalarDimension values length must equal crossfilter data record count" - ); - } - array = value; - } else if (value instanceof Function) { - // Create value array from user-provided map function. - array = this._createValueArray( - data, - value, - new ValueArrayType(data.length) - ); - } else if (isArrayOrTypedArray(value)) { - // Create value array from user-provided array. Typically used - // only by enumerated dimensions - array = this._createValueArray( - data, - i => value[i], - new ValueArrayType(data.length) - ); - } else { - throw new NotImplementedError( - "dimension value must be function or value array type" - ); - } - this.value = array; - - // create sort index - this.index = makeSortIndex(array); - } - - /* eslint-disable class-methods-use-this */ - _createValueArray(data, mapf, array) { - // create dimension value array - const len = data.length; - const larray = array; - for (let i = 0; i < len; i += 1) { - larray[i] = mapf(i, data); - } - return larray; - } - /* eslint-enable class-methods-use-this */ - - select(spec) { - const { mode } = spec; - const { index } = this; - switch (mode) { - case "all": - return { ranges: [[0, this.value.length]], index }; - case "none": - return { ranges: [], index }; - case "exact": - return this.selectExact(spec); - case "range": - return this.selectRange(spec); - default: - return super.select(spec); - } - } - - selectExact(spec) { - const { value, index } = this; - let { values } = spec; - if (!Array.isArray(values)) { - values = [values]; - } - const ranges = []; - for (let v = 0, len = values.length; v < len; v += 1) { - const r = [ - lowerBoundIndirect(value, index, values[v], 0, value.length), - upperBoundIndirect(value, index, values[v], 0, value.length) - ]; - if (r[0] <= r[1]) { - ranges.push(r); - } - } - return { ranges, index }; - } - - selectRange(spec) { - const { value, index } = this; - /* [lo, hi) */ - const { lo, hi } = spec; - const ranges = []; - const r = [ - lowerBoundIndirect(value, index, lo, 0, value.length), - lowerBoundIndirect(value, index, hi, 0, value.length) - ]; - if (r[0] < r[1]) ranges.push(r); - return { ranges, index }; - } -} - -class ImmutableEnumDimension extends ImmutableScalarDimension { - constructor(name, data, value) { - super(name, data, value, Uint32Array); - } - - _createValueArray(data, mapf, array) { - const len = data.length; - const larray = array; - - // create enumeration table - mapping between the value - // and the enum. - const s = new Set(); - for (let i = 0; i < len; i += 1) { - s.add(mapf(i, data)); - } - const enumIndex = sortArray(Array.from(s)); - this.enumIndex = enumIndex; - - // create dimension value array - const enumLen = enumIndex.length; - for (let i = 0; i < len; i += 1) { - const v = mapf(i, data); - const e = lowerBound(enumIndex, v, 0, enumLen); - larray[i] = e; - } - return larray; - } - - selectExact(spec) { - const { enumIndex } = this; - const { values } = spec; - return super.selectExact({ - mode: spec.mode, - values: values.map(v => lowerBound(enumIndex, v, 0, enumIndex.length)) - }); - } - - /* eslint-disable class-methods-use-this */ - selectRange() { - throw new Error("range selection unsupported on Enumerated dimension"); - } - /* eslint-enable class-methods-use-this */ -} - -class ImmutableSpatialDimension extends _ImmutableBaseDimension { - constructor(name, data, X, Y) { - super(name); - - if (X.length !== Y.length && X.length !== data.length) { - throw new RangeError( - "SpatialDimension values must have same dimensionality as crossfilter" - ); - } - this.X = X; - this.Y = Y; - - this.Xindex = makeSortIndex(X); - this.Yindex = makeSortIndex(Y); - } - - select(spec) { - const { mode } = spec; - switch (mode) { - case "all": - return { ranges: [[0, this.X.length]], index: null }; - case "none": - return { ranges: [], index: null }; - case "within-rect": - return this.selectWithinRect(spec); - case "within-polygon": - return this.selectWithinPolygon(spec); - default: - return super.select(spec); - } - } - - selectWithinRect(spec) { - /* - { mode: "within-rect", minX: 1, minY: 0, maxX: 3, maxY: 9 } - */ - const { minX, minY, maxX, maxY } = spec; - const { X, Y } = this; - const ranges = []; - let start = -1; - for (let i = 0, l = X.length; i < l; i += 1) { - const x = X[i]; - const y = Y[i]; - const inside = minX <= x && x < maxX && minY <= y && y < maxY; - if (inside && start === -1) start = i; - if (!inside && start !== -1) { - ranges.push([start, i]); - start = -1; - } - } - if (start !== -1) ranges.push([start, X.length]); - return { ranges, index: null }; - } - - /* - Relatively brute force filter by polygon. - - Currently uses d3.polygonContains() to test for polygon inclusion, which itself - uses a ray casting (crossing number) algorithm. There are a series of optimizations - to make this faster: - * first sliced by X or Y, using an index on the axis - * then the polygon bounding box is used for trivial rejection - * then the polygon test is applied - */ - - selectWithinPolygon(spec) { - /* - { mode: "within-polygon", polygon: [ [x0, y0], ... ] } - */ - const { polygon } = spec; - const [minX, minY, maxX, maxY] = polygonBoundingBox(polygon); - const { X, Y, Xindex, Yindex } = this; - const { length } = X; - let slice; - let index; - if (maxY - minY > maxX - minX) { - slice = [ - lowerBoundIndirect(X, Xindex, minX, 0, length), - lowerBoundIndirect(X, Xindex, maxX, 0, length) - ]; - index = Xindex; - } else { - slice = [ - lowerBoundIndirect(Y, Yindex, minY, 0, length), - lowerBoundIndirect(Y, Yindex, maxY, 0, length) - ]; - index = Yindex; - } - - const ranges = []; - let start = -1; - for (let i = slice[0], e = slice[1]; i < e; i += 1) { - const rid = index[i]; - const x = X[rid]; - const y = Y[rid]; - const inside = - minX <= x && - x < maxX && - minY <= y && - y < maxY && - withinPolygon(polygon, x, y); - - if (inside && start === -1) start = i; - if (!inside && start !== -1) { - ranges.push([start, i]); - start = -1; - } - } - if (start !== -1) ranges.push([start, slice[1]]); - return { ranges, index }; - } -} - -/* Helpers */ -export const DimTypes = { - scalar: ImmutableScalarDimension, - enum: ImmutableEnumDimension, - spatial: ImmutableSpatialDimension -}; - -function isArrayOrTypedArray(x) { - return ( - Array.isArray(x) || - (ArrayBuffer.isView(x) && - Object.prototype.toString.call(x) !== "[object DataView]") - ); -} - -/* return bounding box of the polygon */ -function polygonBoundingBox(polygon) { - let minX = Number.MAX_VALUE; - let minY = Number.MAX_VALUE; - let maxX = Number.MIN_VALUE; - let maxY = Number.MIN_VALUE; - for (let i = 0, l = polygon.length; i < l; i += 1) { - const point = polygon[i]; - const [x, y] = point; - if (x < minX) minX = x; - if (y < minY) minY = y; - if (x > maxX) maxX = x; - if (y > maxY) maxY = y; - } - return [minX, minY, maxX, maxY]; -} - -function withinPolygon(polygon, x, y) { - // TODO XXX replace - return polygonContains(polygon, [x, y]); -} diff --git a/client/src/util/typedCrossfilter/index.js b/client/src/util/typedCrossfilter/index.js deleted file mode 100644 index 9c66e56e..00000000 --- a/client/src/util/typedCrossfilter/index.js +++ /dev/null @@ -1,34 +0,0 @@ -/* -Crossfilter - a re-implementation of a subset of crossfilter, with -time/space optimizations predicated upon the following assumptions: - - dimensions are uniformly typed, and all values must be of that type - - dimension values must be a primitive type (int, float, string). Arrays - or other complex types not supported. - - dimension creation requires call-provided type declaration - - no support for adding/removing data to an existing crossfilter. If you - want to do that, you have to create the new crossfilter, using the new - data, from scratch. - -In addition, this implementation is easier to use with a "redux" style -app, as all operations on the crossfilter are immutable (ie, return a -new crossfilter). - -The actual backing store for a dimension is a TypedArray, enabling -performance improvements over the original crossfilter. - -There are also a handful of new methods, primarily to take advantage of the -performance (eg, crossfilter.fillBySelection) - -Helpful documents (this module follows similar concepts as the original, -but deviates from the API): - https://github.com/square/crossfilter/ - http://square.github.io/crossfilter/ - -There is also a newer, community supported fork of crossfilter, with a -more complex API. In a few cases, elements of that API were incorporated. - https://github.com/square/crossfilter/ - -See test cases for some concrete examples. -*/ - -export { default } from "./crossfilter"; diff --git a/client/src/util/typedCrossfilter/positiveIntervals.js b/client/src/util/typedCrossfilter/positiveIntervals.js deleted file mode 100644 index bafc68d1..00000000 --- a/client/src/util/typedCrossfilter/positiveIntervals.js +++ /dev/null @@ -1,127 +0,0 @@ -// jshint esversion: 6 - -// Interval operations - very simple version of interval set relationship -// operators. An interval is a multi-interval list of [min, max), -// where min and max are mandatory. Constraints: -// * min <= max, min >= 0 -// * empty interval groups are OK, ie, [] -// * Legal intervals: [], [ [0, 1], ... ] -// * Not legal: [ [] ] -// -// All intervals are represented by simple JS arrays/numbers. -// -// Code assumes intervals have a low cardinality; many operations are done -// with a brute force scan. Little attempt to reduce GC pressure. -// -class PositiveIntervals { - // Canonicalize - ensure that: - // 1. no overlapping intervals - // 2. sorted in order of interval min. - // - static canonicalize(A) { - if (A.length <= 1) return A; - const copy = A.slice(); - copy.sort((a, b) => a[0] - b[0]); - const res = []; - res.push(copy[0]); - for (let i = 1, len = copy.length; i < len; i += 1) { - if (copy[i][0] > res[res.length - 1][1]) { - // non-overlapping, add to result - res.push(copy[i]); - } else if (copy[i][1] > res[res.length - 1][1]) { - // merge this into previous - res[res.length - 1][1] = copy[i][1]; // eslint-disable-line prefer-destructuring - } - } - return res; - } - - // Return interval with values belonging to both A and B. Essentially - // a set union operation. - // - static union(A, B) { - return PositiveIntervals.canonicalize([...A, ...B]); - } - - static _flatten(A, B) { - const points = []; /* point, A, start */ - for (let a = 0; a < A.length; a += 1) { - points.push([A[a][0], true, true]); - points.push([A[a][1], true, false]); - } - for (let b = 0; b < B.length; b += 1) { - points.push([B[b][0], false, true]); - points.push([B[b][1], false, false]); - } - // Sort order: point, then start - points.sort((a, b) => (a[0] !== b[0] ? a[0] - b[0] : a[2] ? 1 : -1)); - return points; - } - - // A - B, ie, the interval with all values in A that are not in B. Essentially - // a set difference operation. - // - static difference(A, B) { - // Corner cases - if (A.length === 0 || B.length === 0) { - return PositiveIntervals.canonicalize(A); - } - - const cA = PositiveIntervals.canonicalize(A); - const cB = PositiveIntervals.canonicalize(B); - - const points = PositiveIntervals._flatten(cA, cB); - const res = []; - let aDepth = 0; - let depth = 0; - let intervalStart; - for (let i = 0; i < points.length; i += 1) { - const p = points[i]; - const delta = p[2] ? 1 : -1; - depth += delta; - if (p[1]) aDepth += delta; - - if (i === points.length - 1 || p[0] !== points[i + 1][0]) { - if (aDepth === 1 && depth === 1) { - [intervalStart] = p; - } else if (intervalStart !== undefined) { - res.push([intervalStart, p[0]]); - intervalStart = undefined; - } - } - } - // guaranteed to be in canonical form - return res; - } - - // Return interval with values belonging to A or B. Essentially a set - // intersection. - // - static intersection(A, B) { - if (A.length === 0 || B.length === 0) { - return []; - } - - const cA = PositiveIntervals.canonicalize(A); - const cB = PositiveIntervals.canonicalize(B); - - const points = PositiveIntervals._flatten(cA, cB); - const res = []; - let depth = 0; - let intervalStart; - for (let i = 0; i < points.length; i += 1) { - const p = points[i]; - depth += p[2] ? 1 : -1; - if (depth === 2) { - [intervalStart] = p; - } else if (intervalStart !== undefined) { - res.push([intervalStart, p[0]]); - intervalStart = undefined; - } - } - // guaranteed to be in canonical form - return res; - } -} - -export default PositiveIntervals; diff --git a/client/src/util/typedCrossfilter/sort.js b/client/src/util/typedCrossfilter/sort.js deleted file mode 100644 index a37eaff4..00000000 --- a/client/src/util/typedCrossfilter/sort.js +++ /dev/null @@ -1,415 +0,0 @@ -import { isTypedArray, isFpTypedArray } from "../typeHelpers"; - -/* eslint no-bitwise: "off" */ - -/* - ** fast sort and search, with separate code paths for floats (NaN ordering), - ** indirect and direct search/sort. - */ - -/* -Comparators for float sort. -Infinity < finite < Infinity < NaN -*/ -function lt(a, b) { - if (Number.isNaN(b)) return !Number.isNaN(a); - return a < b; -} - -function gt(a, b) { - if (Number.isNaN(a)) return !Number.isNaN(b); - return a > b; -} - -/* -insertion sort, used for small arrays (controlled by SMALL_ARRAY constant) -*/ -const SMALL_ARRAY = 32; -function insertionsort(a, lo, hi) { - for (let i = lo + 1; i < hi + 1; i += 1) { - const x = a[i]; - let j; - for (j = i; j > lo && a[j - 1] > x; j -= 1) { - a[j] = a[j - 1]; - } - a[j] = x; - } - return a; -} - -function insertionsortFloats(a, lo, hi) { - for (let i = lo + 1; i < hi + 1; i += 1) { - const x = a[i]; - let j; - for (j = i; j > lo && gt(a[j - 1], x); j -= 1) { - a[j] = a[j - 1]; - } - a[j] = x; - } - return a; -} - -function insertionsortIndirect(a, s, lo, hi) { - for (let i = lo + 1; i < hi + 1; i += 1) { - const x = a[i]; - const t = s[x]; - let j; - for (j = i; j > lo && s[a[j - 1]] > t; j -= 1) { - a[j] = a[j - 1]; - } - a[j] = x; - } - return a; -} - -function insertionsortFloatsIndirect(a, s, lo, hi) { - for (let i = lo + 1; i < hi + 1; i += 1) { - const x = a[i]; - const t = s[x]; - let j; - for (j = i; j > lo && gt(s[a[j - 1]], t); j -= 1) { - a[j] = a[j - 1]; - } - a[j] = x; - } - return a; -} - -/* -Quicksort - used for larger arrays -*/ -function quicksort(a, lo, hi) { - if (hi - lo < SMALL_ARRAY) { - return insertionsort(a, lo, hi); - } - if (lo < hi) { - // partition - const mid = Math.floor((lo + hi) / 2); - const p = a[mid]; - let i = lo - 1; - let j = hi + 1; - while (i < j) { - do { - i += 1; - } while (a[i] < p); - do { - j -= 1; - } while (a[j] > p); - if (i < j) { - const tmp = a[i]; - a[i] = a[j]; - a[j] = tmp; - } - } - // sort - quicksort(a, lo, j); - quicksort(a, j + 1, hi); - } - return a; -} - -function quicksortFloats(a, lo, hi) { - if (hi - lo < SMALL_ARRAY) { - return insertionsortFloats(a, lo, hi); - } - if (lo < hi) { - // partition - const mid = Math.floor((lo + hi) / 2); - const p = a[mid]; - let i = lo - 1; - let j = hi + 1; - while (i < j) { - do { - i += 1; - } while (lt(a[i], p)); - do { - j -= 1; - } while (gt(a[j], p)); - if (i < j) { - const tmp = a[i]; - a[i] = a[j]; - a[j] = tmp; - } - } - // sort - quicksortFloats(a, lo, j); - quicksortFloats(a, j + 1, hi); - } - return a; -} - -function quicksortIndirect(a, s, lo, hi) { - if (hi - lo < SMALL_ARRAY) { - return insertionsortIndirect(a, s, lo, hi); - } - if (lo < hi) { - // partition - const mid = Math.floor((lo + hi) / 2); - const p = a[mid]; - const t = s[p]; - let i = lo - 1; - let j = hi + 1; - while (i < j) { - do { - i += 1; - } while (s[a[i]] < t); - do { - j -= 1; - } while (s[a[j]] > t); - if (i < j) { - const tmp = a[i]; - a[i] = a[j]; - a[j] = tmp; - } - } - // sort - quicksortIndirect(a, s, lo, j); - quicksortIndirect(a, s, j + 1, hi); - } - return a; -} - -function quicksortFloatsIndirect(a, s, lo, hi) { - if (hi - lo < SMALL_ARRAY) { - return insertionsortFloatsIndirect(a, s, lo, hi); - } - if (lo < hi) { - // partition - const mid = Math.floor((lo + hi) / 2); - const p = a[mid]; - const t = s[p]; - let i = lo - 1; - let j = hi + 1; - while (i < j) { - do { - i += 1; - } while (lt(s[a[i]], t)); - do { - j -= 1; - } while (gt(s[a[j]], t)); - if (i < j) { - const tmp = a[i]; - a[i] = a[j]; - a[j] = tmp; - } - } - // sort - quicksortFloatsIndirect(a, s, lo, j); - quicksortFloatsIndirect(a, s, j + 1, hi); - } - return a; -} - -/* -Convenience wrappers, handling optimization paths and default -handlers for NaN comparisons. Sorts in place. -*/ -export function sortArray(arr) { - if (Array.isArray(arr)) { - return quicksort(arr, 0, arr.length - 1); - } - if (isTypedArray(arr)) { - if (isFpTypedArray(arr)) { - return quicksortFloats(arr, 0, arr.length - 1); - } - return quicksort(arr, 0, arr.length - 1); - } - /* else unsupported */ - throw new Error("sortArray received unsupported object type"); -} - -export function sortIndex(index, source) { - if (isFpTypedArray(source)) - return quicksortFloatsIndirect(index, source, 0, index.length - 1); - return quicksortIndirect(index, source, 0, index.length - 1); -} - -// Search for `value` in the sorted array `arr`, in the range [first, last). -// Return the first (left most) index where arr[index] >= value. -// -// In other words, return array index I where: -// arr[i] < value for all tarr[lo:I] -// arr[i] >= value for all tarr[I:last] -// -// The same semantics/behavior as: -// C++: lower_bound() -// Python: bisect.bisect_left() -// -function lowerBoundNonFloat(valueArray, value, first, last) { - let lfirst = first; - let llast = last; - // this is just a binary search - while (lfirst < llast) { - const middle = (lfirst + llast) >>> 1; - if (valueArray[middle] < value) { - lfirst = middle + 1; - } else { - llast = middle; - } - } - return lfirst; -} - -// lowerBound, but with NaN handling -// -// If the underlying array is a Float32Array or Float64Array, will enforce -// the ordering -Infinity < finite < Infinity < NaN. -// -function lowerBoundFloat(valueArray, value, first, last) { - let lfirst = first; - let llast = last; - // this is just a binary search - while (lfirst < llast) { - const middle = (lfirst + llast) >>> 1; - if (lt(valueArray[middle], value)) { - lfirst = middle + 1; - } else { - llast = middle; - } - } - return lfirst; -} - -export function lowerBound(valueArray, value, first, last) { - if (isFpTypedArray(valueArray)) { - return lowerBoundFloat(valueArray, value, first, last); - } - return lowerBoundNonFloat(valueArray, value, first, last); -} - -// Inlined performance optimization - used to indirect through a sort map. -// -function lowerBoundNonFloatIndirect( - valueArray, - indexArray, - value, - first, - last -) { - let lfirst = first; - let llast = last; - // this is just a binary search - while (lfirst < llast) { - const middle = (lfirst + llast) >>> 1; - if (valueArray[indexArray[middle]] < value) { - lfirst = middle + 1; - } else { - llast = middle; - } - } - return lfirst; -} - -function lowerBoundFloatIndirect(valueArray, indexArray, value, first, last) { - let lfirst = first; - let llast = last; - // this is just a binary search - while (lfirst < llast) { - const middle = (lfirst + llast) >>> 1; - if (lt(valueArray[indexArray[middle]], value)) { - lfirst = middle + 1; - } else { - llast = middle; - } - } - return lfirst; -} - -export function lowerBoundIndirect(valueArray, indexArray, value, first, last) { - if (isFpTypedArray(valueArray)) { - return lowerBoundFloatIndirect(valueArray, indexArray, value, first, last); - } - return lowerBoundNonFloatIndirect(valueArray, indexArray, value, first, last); -} - -// Search for `value in the sorted array `arr`, in the range [first, last). -// Return the first value where arr[index] > value. -// -// In other words, return array index I, where: -// arr[i] <= value for all tarr[lo:I] -// arr[i] > value for all tarr[I:last] -// -// The same semantics/behavior as: -// C++: upper_bound() -// Python: bisect.bisect_right() -// -function upperBoundNonFloat(valueArray, value, first, last) { - let lfirst = first; - let llast = last; - // this is just a binary search - while (lfirst < llast) { - const middle = (lfirst + llast) >>> 1; - if (valueArray[middle] > value) { - llast = middle; - } else { - lfirst = middle + 1; - } - } - return lfirst; -} - -function upperBoundFloat(valueArray, value, first, last) { - let lfirst = first; - let llast = last; - // this is just a binary search - while (lfirst < llast) { - const middle = (lfirst + llast) >>> 1; - if (gt(valueArray[middle], value)) { - llast = middle; - } else { - lfirst = middle + 1; - } - } - return lfirst; -} - -export function upperBound(valueArray, value, first, last) { - if (isFpTypedArray(valueArray)) { - return upperBoundFloat(valueArray, value, first, last); - } - return upperBoundNonFloat(valueArray, value, first, last); -} - -// Inline performance optimization -// -function upperBoundNonFloatIndirect( - valueArray, - indexArray, - value, - first, - last -) { - let lfirst = first; - let llast = last; - // this is just a binary search - while (lfirst < llast) { - const middle = (lfirst + llast) >>> 1; - if (valueArray[indexArray[middle]] > value) { - llast = middle; - } else { - lfirst = middle + 1; - } - } - return lfirst; -} - -function upperBoundFloatIndirect(valueArray, indexArray, value, first, last) { - let lfirst = first; - let llast = last; - // this is just a binary search - while (lfirst < llast) { - const middle = (lfirst + llast) >>> 1; - if (gt(valueArray[indexArray[middle]], value)) { - llast = middle; - } else { - lfirst = middle + 1; - } - } - return lfirst; -} - -export function upperBoundIndirect(valueArray, indexArray, value, first, last) { - if (isFpTypedArray(valueArray)) { - return upperBoundFloatIndirect(valueArray, indexArray, value, first, last); - } - return upperBoundNonFloatIndirect(valueArray, indexArray, value, first, last); -} diff --git a/client/src/util/typedCrossfilter/util.js b/client/src/util/typedCrossfilter/util.js deleted file mode 100644 index 4bd62940..00000000 --- a/client/src/util/typedCrossfilter/util.js +++ /dev/null @@ -1,27 +0,0 @@ -// jshint esversion: 6 - -import { sortIndex } from "./sort"; -import { rangeFill as fillRange } from "../range"; - -/* - Utility functions, private to this module. -*/ - -// slice out of one array into another, using an index array -// -export function sliceByIndex(src, index) { - if (index === undefined || index === null) { - return src; - } - const dst = new src.constructor(index.length); - for (let i = 0; i < index.length; i += 1) { - dst[i] = src[index[i]]; - } - return dst; -} - -export function makeSortIndex(src) { - const index = fillRange(new Uint32Array(src.length)); - sortIndex(index, src); - return index; -} diff --git a/dev_docs/REST_API.md b/dev_docs/REST_API.md deleted file mode 100644 index ec6cc14b..00000000 --- a/dev_docs/REST_API.md +++ /dev/null @@ -1,731 +0,0 @@ -# cellxgene REST API 0.2 specification - -_Note:_ this document is increasingly divergent from the code base and should be read with great suspicion. For example, it lacks any information -about the binary encoding used by various routes, and has incorrect information about "required" routes and features. We may update it at a -later date when the protocol stabilizes a bit. - ---- - -Items marked as (_future_) are intended for future implementation, and are included in the design to round out the concept, and highlight what we would do when/if we needed more functionality. The (_future_) items are not currently used by the cellxgene web application, and may be omitted from any backend - see [Current Front-End Dependencies](#current-front-end-dependencies) for more details. - -_Caveat emptor, partial spec_: this is a sketch for a spec, not a full spec, and some shortcuts have been taken in the authorship. Best practices for a REST API are assumed but not documented here, such as API versioning, reasonable choices for HTTP response codes, etc. In addition, for clarity the JSON examples will not always have all required quoting (eg, on keys) - the actual implementation should use legal JSON/CSV. - -_Note to readers_: please submit bugs, errata, and requests for clarifications as github issues on this repo. - -## Terminology - -Terminology mapping between the proposed model and other others: - -- Data or dataframe - expression matrix -- Observation (**obs**) - cell, a column in Seurat -- Variable (**var**) - gene, a row in Seurat -- Annotation - metadata, which may be attached to either observations or variables, and will have a name -- Layout - N dimensional float coordinates (x,y,...) assigned to an obs or var -- Cluster - integer id assigned to a obs or var - -Reverse map: - -- Cell - observation or obs -- Gene - variable or var -- Metadata - annotation -- Expression - data -- Expression matrix - dataframe - -## Underlying Data Model - -The underlying data model is typical dataframe plus observation/variable annotations (aka metadata), and is more or less the same as the ScanPy [AnnData](http://anndata.readthedocs.io/en/latest/index.html) or Bioconductor dataframe model: - -- Dataframe: expression matrix [nObs, nVar] - - Observations are cells - - Variables are genes (or equivalent variable, eg, isoforms) - - Observations and variables are identified by an matrix coordinate pair (index, base 0) - - Dataframe values must be defined (dense matrix) - - The underlying type of the dataframe is IEEE single precision float (IEEE 754 "decimal32" data type, float32 in many language environments) -- Annotations (metadata): - - Observations/cells or variables/genes can be annotated - - Annotations are typed and described in a schema: - - Obs or var annotation - - Annotation name - - Annotation type (all encoding is JSON format): - - Float32 - scalar value, IEEE single precision float (decimal32), encoded as JSON number - - Int32 - scalar value, 2's complement signed Int 32 bit encoded as JSON number - - String - UTF-8 string, encoded as JSON string - - Boolean - boolean value, encoded as JSON true/false - - Categorical: value is an item from an _unordered_ enumeration. The enumeration schema is encoded as a JSON array. Enumeration categories may be a JSON number, string or boolean (or any combination thereof). Schema is encoded as a JSON array of enumeration category names. - - Annotation values will conform to their schema, _or_ be undefined. - - Annotation names are a string. - - There is one guaranteed annotation name on every dimension: "name". This should be used as the default display name, must be a string and must be unique for the dimension (ie, all observation names are unique and all variable names are unique). - -Missing from this model are ad hoc metadata/annotations not associated with a observation or variable - we can add that in the future if it is required (what ScanPy calls unstructured annotation). - -### Assumptions about the Server - -The following assumptions are made about server behavior: - -- The server will have a data schema for annotations -- The server will have default algorithms for layout, clustering and differential expression (may add additional endpoints in the future) -- The server will serve a default data set, including annotations, cluster assignment and 2D layout (put another way, the client can request this data without any specification about how it is generated). - -## Filters - -There are several common filters supported by various routes. Route descriptions supporting these filters will reference the filter types. - -### Annotation _Value_ Filters - -Filter by the value of specific observation or variable annotation values. For example, the `/data` routes support sub-selection by the value of the annotations, which might be used to filter by some specific metadata (eg, `tissue_type: lung`). - -Annotation value filters can be specified as a URL query parameter (using GET), or a JSON object in a request body (using PUT or POST)[1](#endnote-1). - -For a GET URL query parameter: - -- Annotation name is encoded as `obs:name` or `var:name`[2](#endnote-2). -- Enumerated values (string, categorical, boolean) are encoded as option lists, ie, `var:tissue=lung, obs:tumor=true` -- Scalar values (int32, float32) are encoded as ranges, ie, `obs:num_reads=1000,10000` where either min or max may be replaced with an asterisk to indicate a half-open range. -- Index filters are not allowed within GET URL query parameter filters -- Logically, filters are ANDed, except for repeated annotation names which are ORed. For example, `?X=A&X=B&Y=1` is evaluated as `((X==A or X==B) and Y==1)` - -Example selection for _lung_ and _heart_ tissue with more than 1000 reads: - -``` - GET /data?obs:tissue=lung&obs:tissue=heart&obs:num_reads=1000,* -``` - -For a PUT / POST request body, the filter encoding is a simple JSON object: - -- Annotation names are embedded within either a obs or var object -- Enumerated values (eg, string, boolean categorical) are encoded as an array of options -- Scalar values are encoded as an object with a min and max field - -Example selection for _lung_ and _heart_ tissue with more than 1000 reads: - -``` - PUT or POST - -- - { - "filter": { - "obs": { - "annotation_value": [ - { "name": "tissue", "values": ["lung", "heart"] }, - { "name": "num_reads", "min": 1000 }, - { "name": "tumor", "values": true } - ] - } - } - } -``` - -### Obs or Var _Index_ Filters - -Several PUT / POST routes allow filtering by observation or variable index, eg, access to a subset of the dataframe. Obs or var indexes may be specified individually or as a range. - -For example, the following filter requests observations 1, 99, 1000-2000 from the dataframe (all variables are implicitly requested because no var filter is provided): - -``` -PUT or POST --- -{ - filter: { - obs: { - index: [ - 1, 99, [1000, 2000] - ] - } - } -} -``` - -### Complex Filters - -Several routes support multiple filter types, which may be combined into a single PUT / POST body. Complex filters are additive. - -Example: - -``` -PUT or POST --- -{ - "filter": { - "obs": { - "annotation_value": [ - { "name": "sex", "values": [ "F" ] } - ], - "index": [ - [0, 10000] - ] - }, - "var": { - // ... - } - } -} -``` - -## Routes - -Summary (details follow): - -- `/config` - return application configuration -- `/schema` - data schema for the dataframe and dataframe annotations -- `/annotations/obs`, `/annotations/var` - fetch observation or variable annotation values, with ability to filter by annotation name (ie, fetch a subset of annotation data) -- `/data/obs`, `/data/var` - fetch dataframe values, with ability to subselect by annotation value and/or obs/var indices. -- `/cluster/obs`, `/cluster/var` - fetch cluster assignments, with ability to request re-clustering of a data subset -- `/layout/obs`, `/layout/var` - fetch layout information (N dimensional coordinates), with ability to request re-layout of a data subset -- `/diffexp` - fetch differential expression stats for a subset of data. -- `/saveLocal` - request that the server persist a copy of the caller-specified data to local storage, with caller-specified name. - -All routes will be implemented with a prefix: /api/vmajor.minor, where major.minor is a semver version number. Currently version is 0.2, so, for example, the config route will be /api/v0.2/config. Prefixes are omitted in this document for clarity and brevity (eg, /config should be read as /api/v0.2/config). - -The following routes are mandatory: - -- `/config` - GET -- `/schema` - GET -- `/annotations/obs`, `/annotations/var` - GET and PUT -- `/data/obs`, `/data/var` - GET and PUT - -All other routes are optional. Routes that are optional will advertise their presence or non-presence in the `/config` response. - -### Common Error Codes - -Routes that request potentially long-running (non-interactive speed) computation should provide threshold hints in /config response, and must return a 403 error immediately when a request exceeds, in the server's view, a reasonable interactive compute time. - -Erroneously specified filters (eg, non-existent annotation name or other malformed filter) should return a 400 HTTP error. - -### GET /config - -Configuration information to assist in front-end adaptation to underlying engine, available functionality, interactive time limits, etc. - -Information will include: - -- API endpoint availability (eg, differential expression not available) and related limitations, including: - - Feature enabled/disabled: - - POST /cluster (re-clustering) supported: true, false - - PUT /layout (re-layout) supported: true, false - - POST /diffexp (differential expression calculation) available: true, false - - Interactive compute speed hints; _approximate_ limitation on request size before it becomes non-interactive: - - Re-clustering: if supported, this is an integer number of obs/vars at which the algorithm is non-interactive. Omitted if POST /cluster not supported - - Re-layout: integer number of obs/vars at which the re-layout is non-interactive. Omitted if PUT /layout not supported - - Diff Exp: integer number of obs/vars at which the differential expression compute is non-interactive. Omitted if POST /diffexp not supported -- Human readable information: - - Data set name / title - for display purposes. - - Engine name and version - for display purposes. -- System-wide configuration parameters, eg, configuration the end user has specified via a CLI or server configuration system. - -**Response body:** - -- `features` - encoded as array of features. All optional features must be included. Each feature will be identified by an HTTP method and path **prefix**, and will have availability status. If not specified as unavailable, the feature must be implemented. - - Limitations are optional, and specified as an additional parameter on a feature, encoded as a number value with key `interactiveLimit`. By convention, this is the maximum number of inputs at which point the request is likely to return an error indicating non-interactive compute request. For example, this might be the maximum number of observations specified in the request to `PUT /layout/obs`. - - mandatory routes do not need to be specified -- `displayNames` - names will include `engine` and `dataset` display names, as a JSON string. These should be relatively short strings (eg, suitable for window titles or equivalent) -- `parameters` - system configuration parameters, specified as a key/value pair in a simple object. See [Currently Defined Parameters](#currently-defined-parameters) for additional information. - -``` -GET /config --- -200 -{ - "config": { - "features": [ - // all /cluster/* paths not implemented - { "method": "POST", "path": "/cluster/", "available": false }, - { - "method": "PUT", - "path": "/layout/obs", - "available": true, - "interactiveLimit": 10000 - }, - { "method": "PUT", "path": "/layout/var", "available": false } - ], - "displayNames": { - "engine": "ScanPy version 1.33", - "dataset": "/home/joe/mouse/blorth.csv" - }, - "parameters": { - "max-category-items": 1000, - "verbose": false - // name: value - } - } -} -``` - -### GET /schema - -Schema definition for dataframe and annotations. - -**URL Parameters:** none - -**Response body:** encoded as a JSON object: - -- Dataframe: - - Dimensionality and type of dataframe, including: - - nObs - number of observations - - nVar - number of variables - - type - dataframe element type (currently required to be float32) -- Dataframe annotations, both obs & var - - name - encoded as a string - - type - one of `float32`, `string`, `int32`, `boolean` or `categorical` - - categories - meaningful only for `categorical` type. Specifies the complete list of category names. - -Example: - -``` -GET /schema --- -200 -{ - "schema": { - "dataframe": { - "nObs": 383, - "nVar": 19944, - "type": "float32" - }, - "annotations": { - "obs": [ - { "name": "name", "type": "string" }, - { "name": "tissue_type", "type": "string" }, - { "name": "num_reads", "type": "int32" }, - { "name": "sample_name", "type": "string" }, - { - "name": "clusters", - "type": "categorical", - "categories"=[ 99, 1, "unknown cluster" ] - }, - { "name": "QScore", "type": "float32" } - ], - "var": [ - { "name": "name", "type": "string" }, - { "name": "gene", "type": "string" } - ] - } - } -} -``` - -### GET /annotations/obs, GET /annotations/var - -Fetch annotations (metadata) for all observations or all variables. A subset of annotations may be selected by providing a query parameter `annotation-name` with a URL query list of annotation names. - -Observations and variables are guaranteed to have a `name` annotation, which should contain a human-readable name for this element (eg. a gene or cell name). No other annotation names are required or may be assumed. The `name` annotation value must be unique for the dimension, ie, a given observation name is unique among all observation names and a given variable name is unique among all variables. - -**URL Query Parameters:** - -- annotations-name - URL query list of 1 or more annotation names. Only specified annotations are returned. If this query parameter is not specified, all annotations are returned. - -**Response code:** - -- 200 Success -- 400 Bad Request - one or more of the names specified with `annotation-name` are not associated with an annotation. - -**Response body:** annotation description and values. Values conform to the schema returned by `/schema` and each record begins with the observation or variable index. Where the specific value is not defined, `null`will be returned. Values will be sorted by index. - -Example: - -``` -GET /annotations/obs?annotation-name=tissue_type&annotation-name=sex&annotation-name=num_reads&annotation-name=clusters --- -200 -{ - "names": [ - "tissue_type", "sex", "num_reads", "clusters" - ], - "data": [ - [ 0, "lung", "F", 39844, 99 ], - [ 1, "heart", "M", 83, 1 ], - [ 49, "spleen", null, 2, "unknown cluster" ], - // [ obsOrVarIndex, value, value, value, value ], - // ... - ] -} -``` - -### PUT /annotations/obs, (_future_) PUT /annotations/var - -Same as the `GET /annotations` routes, with additional ability to filter by observation or variable index and annotation value (complex filter) - -**Fragment:** same as `GET /annotations/{obs|var}` - -**URL Query Parameters:** - -- annotations-name - URL query list of one or more annotation names. Only specified annotations are returned. If this query parameter is not specified, all annotations are returned. - -**Request body:** Complex filters, including - -- Annotation value filter -- Observation or (_future_) variable index filter - -**Response code:** - -- 200 Success -- 400 Bad Request - malformed filter or one or more of the annotation-name identifiers were not associated with an annotation name. - -**Response body:** - -- Same as `GET /annotations/{obs|var}` - -### GET /data/obs, (future) GET /data/var - -Get data from the dataframe (ie expression values), optionally sub-selected by annotation value filter. Supports JSON (default) and CSV responses, with annotation value filtering to enable basic deep linking. Response data must be organized according to path-specified primary dimension: - -- observation-major (for `/data/obs`) -- variable-major (for `/data/var`) - -**URL Query Parameters:** - -- Annotation value filter -- Accept-type: specifies an acceptable media type for simple content type negotiation. Semantics are similar to HTTP Accept header, but included in query params to make it easy to cut & paste URL. If parameter is present, MUST take precedence over the value(s) specified in the Accept request header. If the service does not support the requested format, it must reply with a 406 - Not Acceptable error response. This parameter differs from an Accept header in that it only accepts one media type value and not a priority list (it is named accept-type and not accept because the behavior differs slightly). Acceptable values are the MIME types (same semantics as Accept-Type HTTP header): - - application/json - - text/csv - -**Response code:** - -- 200 - Success -- 406 - Not Acceptable (accept-type unacceptable MIME type) -- 400 - Bad Request - malformed filter - -**Response body:** contents of the filtered dataframe, in primary-axis-major order, encoded as a dense matrix (eg, `/data/obs` will return data by observation). Data is preceded by a list of secondary axis indices, using the same index encoding as Index Filters (ie, an array of indices and/or index ranges). In JSON, this will be encoded as an array of indices with key obs or var; in CSV, this will be the header of the CSV response body. In both encodings, data will be sorted by index (both primary and secondary axis). - -Example JSON response to a request which has filtered by annotation value: - -``` -GET /data/obs?accept-type=application/json&obs:tissue=lung --- -200 -{ - "var": [ [ 0, 20000 ] ], - "obs": [ - [ 1, 39483, 3902, 203, 0, 0, ..., 28 ], // length == 20001 - // [ obsIndex, value1, value2, ..., value20000 ] - // ... - ] -} -``` - -### PUT /data/obs, (future) PUT /data/var - -Same as `GET /data/{obs|var}`, but also accepts sub-selection by obs/var index filter. Accept may be specified as an HTTP header, and will support JSON and CSV. JSON will be the default served if no Accept header is supplied. - -**URL Query Parameters:** none - -**Request body:** Complex filters, including - -- Annotation value filter -- Observation or (_future_) variable index filter - -**Response code:** - -- 200 Success -- 400 Bad Request - malformed filter -- 406 - Not Acceptable (accept-type missing or unacceptable MIME type) - -**Response body:** same as `GET /data/{obs|var}` - -### (future) POST /cluster/obs, (future) POST /cluster/var - -Generate cluster assignments for the caller-specified subset of data, as indicated by the filter. This operation implicitly requests a re-clustering operation to be performed on the specified data. - -If re-clustering is not supported by the server, must return an HTTP 501 response. If, in the view of the server, the request will exceed a reasonable interactive time period, must immediately return HTTP 403 error (error return _before_ attempting computation). - -**URL Query Parameters:** none - -**Request body:** complex filter: - -- Annotation value filter -- Obs/var index filter - -**Response code:** - -- 200 - Success -- 400 Bad Request - malformed filter -- 403 Forbidden - non-interactive request -- 501 - Re-clustering is not implemented - -**Response body:** - -- For 200 Success, returns numeric cluster ID for each observation or variable, organized as an array of `[index, clusterId]`, an sorted by index: - - ``` - { - "cluster": [ - [ 0, 24 ], - // [ obsOrVarIndex, clusterID ], - // ... - ] - } - ``` - -* For 403 Forbidden, must return an explanation of refusal in the response body to allow differentiation from an authorization failure (see 403 response definition in [RFC 7231](https://tools.ietf.org/html/rfc7231#section-6.5.3)) - -### GET /layout/obs, (_future_) GET /layout/var - -Get the _default_ layout for all observations or (_future_) all variables. Return dimensionality of layout and a coordinate list. The client must be prepared to accept dimensionality different than it natively displays, and should perform a best-effort attempt to display the higher dimensionality layout (eg, create a 2D view of a 3D layout). - -**Response body:** dimensionality and coordinate list for each observation or variable, where coordinates are encoded as floats [0,1], sorted by index. Example 2D response: - -``` -{ - "layout": { - "ndims": 2, - "coordinates": [ - [ 0, 0.284483, 0.983744 ], - [ 1, 0.038844, 0.739444 ], - // [ obsOrVarIndex, X_coord, Y_coord ], - // ... - ] - } -} -``` - -**Response code:** - -- 200 - success -- 500 Internal Server Error - unprepared data, layout has not been stored in the input data - -### (_future_) PUT /layout/obs, (_future_) PUT /layout/var - -Generate layout for the caller-specified subset of data, as indicated by the filter. This operation implicitly requests a re-layout operation to be performed on the specified data. This operation will _commonly_ return the same results for any given caller-specified filter, but this behavior is not guaranteed. - -If re-layout is not supported by the server, must return an HTTP 501 response. If, in the view of the server, the request will exceed a reasonable interactive time period, must immediately return HTTP 403 error (error return _before_ attempting computation). - -**URL Query Parameters:** none - -**Request Body:** complex filter - -- Annotation value filter -- Observation or variable index filter - -**Response code:** - -- 200 - success -- 400 Bad Request - malformed filter -- 403 Forbidden - non-interactive request -- 501 - re-layout is not implemented - -**Response body:** - -- For 200 Success, same as `GET /layout/{obs|var}` -- For 403 Forbidden, must return an explanation of refusal in the response body to allow differentiation from an authorization failure (see 403 response definition in [RFC 7231](https://tools.ietf.org/html/rfc7231#section-6.5.3)) - -### POST /diffexp/obs - -Generate differential expression (DE) statistics for two specified subsets of data, as indicated by the two provided observation complex filters (each filter provided either as annotation and/or obs/var index). **If only one subset of data is specified, the default assumption is that the second subset is all other observations (cells).** This operation implicitly requests a differential expression operation to be performed on the specified data. - -Two modes are provided: - -- `topN`: return top N differentially expressed variables (across all variables) -- `varFilter`: return DE for caller-provided variable filter (_future_) - -Both modes perform calculations using a subset of observations, where each subset is defined by an observation filter (`set1` and `set2`). These filters must not include a variable filter. - -If differential expression is not supported by the server, must return an HTTP 501 response. If, in the view of the server, the request will exceed a reasonable interactive time period, must immediately return HTTP 403 error (error return _before_ attempting computation). - -**Request body:** - -- Mode: `topN` or `varFilter` (specified with complex variable filter). `varFilter` must not include an "obs" section for the filter. Can be specified as one of: - - ``` - { "mode": "topN", "count": N } - ``` - - or - - ``` - { - "mode": "varFilter", - "varFilter": { - "filter": { - "var": { - "annotation_value": [ - { "name": "gene", "values": [ "FOXP2", "ACTN1" ] } - ] - } - } - } - } - ``` - -- count: required for `topN` mode, and specifies the number of results to return. -- varFilter: required for `varFilter` mode, and specifies a variable filter. -- set1 - Complex filter - defines observation "set1". - - ``` - { - "set1" { - "filter": { - "obs": { - "annotation_value": [ - { "name": "tissue", "values": [lung, heart] }, - { "name": "num_reads", "min": 1000 }, - { "name": "tumor", "values": true } - ] - } - } - } - } - ``` - -* set2 - Complex filter - defines observation "set2" (set 2 is optional, and defaults to all **other** observations, ie, those disjoint from set1) - -**Response code:** - -- 200 - success -- 400 Bad Request - malformed filter -- 403 Forbidden - non-interactive request -- 501 - differential expression is not implemented - -**Response body:** - -- For 200 Success, differential expression statistics returned as array of arrays, where each contains the following values: - - - **varIndex**: variable index for the computed results - - **logfoldchange**: log fold-change of the average expression between the two groups. Positive values indicate that the gene is more highly expressed in the first group, - - **pVal**: unadjusted p-value, - - **pValAdj**: adjusted p-value - - Values ordered as: - - _varIndex_, _logfoldchange_, _pVal_, _pValAdj_ - - For example: - - ``` - [ - [ 1720, 2.4679039, 2.3124478092035228e-175, 4.250279073316075e-172 ] - // ... - ] - ``` - -- For 403 Forbidden, must return an explanation of refusal in the response body to allow differentiation from an authorization failure (see 403 response definition in [RFC 7231](https://tools.ietf.org/html/rfc7231#section-6.5.3)) - -Example: - -``` -POST /diffexp/obs -{ - "diffexp": { - "set1": { - "filter": { - "obs": { "index": [ [0, 10000] ] } - } - }, - "set2": { - "filter": { - "obs": { "index": [28448, 4, [88888, 99999] ] } - } - }, - "mode": "topN", - "count": 5 - } -} ---- -200 - Success -{ - "diffexp": [ - [ 328, -2.569489, 2.655706e-63, 3.642036e-57 ], - // [ varIdx, logfoldchange, pVal, pValAdj ], - // ... - ] -} -``` - -### (future) POST /data/saveSelection - -Instruct the server to save or otherwise process the selected dataframe subset, as described the the filter. "Saved" data may optionally be given a name. If no name is provided, the server should pick a reasonable default. Action is synchronous - ie, a 200 return indicates completion of the request. Response will contain a human readable text message to inform the user the results of their action. - -The intent of this route is to provide the user with a means of "saving" or otherwise processing filtered/selected data, for other uses, in a server-local resource. The precise semantics and mechanism are server-dependent, and the client should make no assumptions about server-side UI, data formats or persistence mechanism. Additionally, the client should make no assumptions about the lifespan or long-term persistence of the "saved" data. - -For example, a server may provide: - -- Store data subset in a CSV for sharing with other toolchains -- Persist a selection view within an HDF5 dataset region reference -- Pipe the selected data to another toolchain for immediate processing (ie, Unix pipe-like behavior) - -(these are just examples - the server may implement any data export or integration UI, completely independent of client UI or client knowledge). - -**URL Query Parameters:** name parameter, which for portability should only contain characters legal in a Posix file name (`A–Z a–z 0–9 . _ -`). - -**Request body:** Complex filters, including - -- Annotation value filter -- Observation or (_future_) variable index filter - -**Response code:** - -- 200 Success -- 400 Bad Request - malformed filter -- 500 - unable to perform requested action - -**Response body:** - -For 200 Success and 400 Bad Request: - -``` -{ - message: "Your results have been saved to /tmp/foo.csv" -} -``` - -Example: - -``` -POST /data/saveSelection?name=myFavCluster -{ - "filter": { - "obs": { - "annotation_value": [ - { "name": "cluster", "values": [ "99" ] } - ], - } - } -} --- -200 Success -{ - "message": "OK" -} -``` - -## Current Front-End Dependencies - -The cellxgene web front-end currently uses the following routes & features. - -Routes: - -- `GET /config` -- `GET /schema` -- `GET /annotations/obs` -- `GET /annotations/var` -- `GET /layout/obs` - get the default layout -- `PUT /data/obs` - request will contain a filter by var `name` -- `POST /diffexp/obs` - mode `topN`, typically with a `count` of 10, and two sets defined by an obs index filter (`{ filter: { obs: { index: [...] } } }`) - -Requests include the following content negotiation headers: - -- `Accept: application/json` - CSV not currently used in any routes -- `Accept-Encoding: gzip, deflate, br` - -Currently unused routes: - -- `/data/var` -- `/cluster/*` -- `/layout/var` -- `/data/saveSelection` - -## Currently Defined Parameters - -The system currently defines the following parameters: - -- `max-category-items` - if any categorical annotation has a unique value count exceeding - this parameter, it will not be displayed to the user. Number. -- `layout` - algorithm to use for graph layout. String. -- `diffexp` - algorithm to used to calculate differential expression. String. -- `title` - data set name. String. -- `debug` - run in debug mode. Boolean. -- `verbose` - more verbose logging. Boolean. - - - -## Notes - -[1]: Filters can often be large and complex, and exceed the URL length limitations - the POST method provides a means for the client to accommodate these cases. The GET method is provided for those cases where this is not concern, and/or where the client requires deep linking (subject to this URL length limit). - -[2]: Note to implementers -- this scheme requires careful use of URL encoding to ensure that both annotation names and annotation values are properly escaped. It requires that both normal URL encoding is applied, as well as ensuring that the annotation name has any embedded colon characters escaped. diff --git a/dev_docs/Release_Validation_Recipe.md b/dev_docs/Release_Validation_Recipe.md deleted file mode 100644 index b8bed44d..00000000 --- a/dev_docs/Release_Validation_Recipe.md +++ /dev/null @@ -1,75 +0,0 @@ -# Pre-release Validation Plan -_This document contains a general purpose test plan for validating cellxgene prior to release_. - -All steps are expected to pass with no errors or malfunctions. Tester should check: -* CLI for errors (eg, build error, engine error) -* Browser console for errors -* Expected CLI and/or browser UI function - -## [Backend Test](#backend-test) - -### Getting started build & install validation: -Goal: validate build, install and example demo correctness, per Getting Started instructions. - -1. Clean clone of the cellxgene repo into a local directory -2. Follow build & install instructions from the Getting Started guide - * Confirm no build or install errors - * Confirm `cellxgene --help` functions correctly -3. Follow the example data set demo startup from Getting Started guide and confirm front-end data loads correctly -4. Confirm all package (and other) version dependencies are correct and match - -### Tabula Muris -Goal: basic functional validation of b/e functions using Tabula Muris data and the scanpy engine. - -1. Precondition: cellxgene built & installed. -2. Load the Tabula Muris data set: `cellxgene --title 'T. Muris' scanpy directory-name/` -3. Verify all metadata selectors display and have correct type/options: - * *TODO:* _need list of metadata and their type_ - * ... -4. Verify default graph display has expected layout. _need screen shot of expected layout_ -5. Verify selection controls work as expected: - * continuous metadata field - * categorical metadata field - * graph/lasso select -6. Verify color by metadata type -7. Select two cell sets and confirm differential expression compute succeeds -6. Verify expression scatter plot is correct - - -## [Front-end Compatibility Test](#frontend-compatability-test) -Goal: verify front-end UI compatibility with a given browser variant/version/platform. - -1. Start back-end on PBM3K data set -2. Load UI -3. Verify all major UI modes/functions: - * Graph display - * Metadata selector display - * Title display - * Selection - single and multiple fields - correctly display in cluster graph - * Regraph & reset function correctly - * Differential expression calc & scatter plot display - * All graphs maintain consistent selection state - * All selection widgets (eg, continuous metadata selector) maintain correct status (consistent with graph displays) -4. Verify overall performance is reasonable/interactive -5. Verify no errors on CLI or browser console - - -## [End-to-end Functional Test](#e2e-functional-test) -Goal: confirm end-to-end functional behavior is as expected. - -### ScanPy engine - -1. Basic data load and display - * ... -2. Select & multi-select of metadata and coordinates - * ... -3. Color by metadata - * ... -4. Regraph / reset - * ... -5. Differential expression: scatterplot, top-N genes, etc. - * ... -6. Color by expression - * ... -7. Arbitrary gene expression - * ... diff --git a/dev_docs/developer_guidelines.md b/dev_docs/developer_guidelines.md deleted file mode 100644 index c9ce3a8c..00000000 --- a/dev_docs/developer_guidelines.md +++ /dev/null @@ -1,91 +0,0 @@ -# Developer guidelines - -### Requirements -- npm -- Python 3.6+ -- Chrome - -[See dev section of README](../README.md) - -**All instructions are expected to be run from the top level cellxgene directory unless otherwise specified.** - -## Server dev -### Install -* Build the client and put static files in place: `make build-for-server-dev` -* Install from local files: `make install-dev` - -### Launch -* `cellxgene launch [options] ` - -### Reloading -If you install cellxgene using `make install-dev` the server will be restarted every time you make changes on the server code. If changes affects the client, the browser must be reloaded. - -### Linter -We use `flake8` to lint code. Travis CI runs `flake8 server`. - -### Test -1. Install development requirements `pip install -r server/requirements-dev.txt` -2. Run tests `pytest server/test` - -### Tips -* Install in a virtualenv -* May need to rebuild/reinstall when you make client changes - -## Client dev -### Install -1. Install prereqs for client: `npm install --prefix client/ client` -2. Install cellxgene server: `pip install -e .` Caveat: this will not build the production client package - you must use the [server install](#install) instructions above to serve web assets. - -### Launch -To launch with hot reloading you need to launch the server and the client separately. Node's hot reloading starts the client on its own node server and auto-refreshes when changes are made. -1. Launch server (the client relies on the REST API being available): `cellxgene launch [options] ` -2. Launch client: in `client/` directory run `npm run start` -3. Client will be served on localhost:3000 - -### Build -To build only the client: `make build-client` - -### Linter -We use `eslint` to lint the code and `prettier` as our code formatter. - -### Test -In `client/` directory run `npm run unit-test` - -### Tips -* You can also install/launch the server side code from npm scrips (requires python3.6 with virtualenv) in `client/` directory run `npm run backend-dev` - -## Running tests -Client and server tests run on Travis CI for every push, PR, and commit to master on github. End to end tests run nightly on master only. - -### Server unit tests -Install development requirements `pip install -r server/requirements-dev.txt` -Run tests `pytest server/test` - -### Client unit tests -In `client/` directory run `npm run unit-test` - -### End to end tests - -End to end tests use two env variables: -* `JEST_ENV` - environment to run end to end tests. Default `dev` - * `prod` - run headless with no slowdown, chromium will not open. - * `dev` - opens chromimum, runs tests with minimal slowdown, close on exit. - * `debug` - opens chromium, runs tests with 100ms slowdown, dev tools open, chrome stays open on exit. -* `JEST_CXG_PORT` - port that end to end tests are being run on. Default `3000` (client hosted port). - -On CI the end to end tests are run with `JEST_ENV` set to `prod` using the `smoke-test` npm script - -To run end to end tests as they will be run on CI -1. cellxgene should be built and installed as [specified in server dev](#install) -2. `export JEST_ENV='prod'` -3. `export JEST_CXG_PORT='5000'` -4. Run `npm run --prefix client/ smoke-test` - -Run end to end tests interactively during development -1. cellxgene should be installed as [specified in client dev](#install-1) -2. Follow [launch](#launch-1) instructions for client dev with dataset `example-dataset/pbmc3k` -3. Run `npm run --prefix client/ e2e` -4. To debug a failing test `export JEST_ENV='debug'` and re-run. - - - diff --git a/dev_docs/pull_request_guidelines.md b/dev_docs/pull_request_guidelines.md deleted file mode 100644 index 81b2aa8d..00000000 --- a/dev_docs/pull_request_guidelines.md +++ /dev/null @@ -1,35 +0,0 @@ -## Creating PR -1. Name [username]/branchname - 1. Branch name should be all lowercase - 2. Words separated by “-” -2. Code should address only one issue ideally, make a separate PR for each task -3. Description - 1. Clear explanation of issues solved - 2. Describe why and how, when appropriate - 3. Call out specific areas you want extra attention in review (optional) - 4. If your PR requires more than one reviewer tag those people in the description or comments and let them know that you specifically require them -4. Ensure that the PR updates tests and documentation and adds tests where appropriate -5. Use github’s issue keywords when PR is addressing an issue https://help.github.com/articles/closing-issues-using-keywords/ -6. Tags (add at beginning of title) - 1. [EASY] - small non-controversial change, easy to review - 2. [DO NOT MERGE] - PR is in progress, do not merge changes - -## Review -1. Assign at least one reviewer to submitted PRs. Reviewers should be selected based on expertise in areas affected by the PR (eg, web UI: Colin), and should include Comp Bio and PM as needed. -2. Reviewers should approve or request changes (not just comment) and put general and line level comments where appropriate -3. As a PR submitter respond to all comments (eg, comment, commit a change, etc) -4. External PRs - 1. For external PRs or PRs not from our core team, core team should assign a reviewer and make initial contact within 1 business day - 2. Build code on local environment and run smoke tests - -## Required to Merge -1. Travis CI Build passing -2. At least one reviewer approved - 1. Exceptions: - 1. Release PRs where version is just bumped should not need review - 2. Complex PRs which touch multiple parts of the codebase should have reviews from all relevant parties -3. License and Security checks (SNYK) passing. If their server is down and you didn’t add any new external npm or python packages, merge is OK - -## Merging -1. Use "squash and merge" option when merging -2. If you resolved conflicts, wait until the build passes to merge diff --git a/dev_docs/release_process.md b/dev_docs/release_process.md deleted file mode 100644 index b4e0830e..00000000 --- a/dev_docs/release_process.md +++ /dev/null @@ -1,95 +0,0 @@ -# cellxgene release process - -_This document defines the release process for cellxgene_ - -## Overview - -The goal of the release process is to publish an installable package -to PyPi, with a matching tagged release on github. - -The release process should result in the following side-effects: - -- Version number bump, using semantic versioning -- JS assets built & packaged, committed to the repo -- Tagged github release -- Publication to PyPi - -## Recipe - -Follow these steps to create a release. - -1. Preparation: - - python3.6 environment, and a cellxgene clone - - Define the release version number, using [semantic versioning](https://semver.org/), - and specifying all three digits (eg, 0.3.0) - - Write the release title and release notes and add to - [release notes document](https://docs.google.com/document/d/1KnHwkYfhyWO5H8BDcMu7y3ogjvq5Yi4OwpmZ8DB6w0Y/edit) -2. Create a release branch, eg, `release-version` -3. In the release branch: - - Run `make release-stage-1 PART=[major | minor | patch]` where you choose major/minor/patch depending on which part of the version - is being bumped (eg, 0.2.9->0.3 is minor). -4. Commit and push the new branch -5. Create a PR for the release. - - [optional] As needed, conduct PR review. -6. Merge to master -7. Create Github release using the version number and release notes - ([instructions](https://help.github.com/articles/creating-releases/)). - - Draft new release - - Type version name matching release version number from (1) - - Select `master` as release branch (ensure you merged the release PR) - - Type title `Release {version num}` - - [optional] Check pre-release if this release is not ready for production - - Publish Release -8. Publish to pypi by performing the following steps (assumes you that you have registered for pypi, -and that you have write access to the cellxgene pypi package): - - Build the distribution and upload to test pypi `make release-stage-2` - - Test the test installation in a fresh virtual environment using `make install-release-test` - - Upload the package to real pypi using `make release-stage-final` - - Test the installation in a fresh virtual environment using - `pip install cellxgene` - -The optional steps are for testing purposes, and are recommended -for publishing any major releases, and any releases that significantly -change the packaging (e.g. new bundled files, new dependencies, etc.) - -## Troubleshooting -### Fails to upload to test.pypi - - _PyPi doesn't allow you to reupload a release with the same version number_ -If you accidentally burned a release number you want to use on prod, you have a few options: -1) OPTION 1: Create distribution `make pydist`; test release locally `pip install dist/`; - then upload to prod `make release-stage-final`. -2) OPTION 2: (DANGER) release directly to prod: `make release-directly-to-prod`. -3) OPTION 3: If the release was burned on prod as well run from Step 3 again with option -PART=patch until you get to an unburned version. - -### The release doesn't install or fails your tests when you install it - -Delete it from pypi - Go to pypi.org -> sign in -> go to the cellxgene package -> click manage -> then in the options drop down click delete -> follow the instructions. You will not be able to use that release number again. If it is a minor bug and not a major regression, you can just release a patch. -### If you need to run stage final on a different computer than stage 2 - If you run stage final without running stage 2 first, the dist will not have been build on the computer running stage final. The solution is to run `make release-directly-to-prod`. This both builds the distribution files and then releases directly to prod pypi.org. - -## Stage Details -### Stage 1 - `make release-stage-1` - 1. Pip installs requirements-dev - 2. Bumps version by [PART] - 3. Deletes build directory, client/build, dist and cellxgene.egg-info - 4. Creates the package-lock.json - -### Stage 2 - `make release-stage-2` - 1. Pip installs requirements-dev - 2. Builds client and server - 3. Creates distribution release (sdist) - 4. Uploads to test.pypi.org - -### Stage final - `make release-stage-final` - ** Does not build distribution ** - 1. Uploads to pypi.org - -### (DANGER) Release directly to prod `make release-directly-to-prod` - ** builds distribution and uploads directly to prod ** - Only use this if you are directed to by the troubleshooting guide - 1. Pip installs requirements-dev - 2. Builds client and server - 3. Creates distribution release (sdist) - 4. Uploads to pypi.org diff --git a/dev_docs/testing_environment_for_web_hosting.md b/dev_docs/testing_environment_for_web_hosting.md deleted file mode 100644 index 324f6839..00000000 --- a/dev_docs/testing_environment_for_web_hosting.md +++ /dev/null @@ -1,69 +0,0 @@ -### How to set up a testing environment for changes related to web hosting. - -We often get PRs related to someone using a server to host cellxgene externally or on a local network (ex. https://github.com/chanzuckerberg/cellxgene/pull/568 ). Here is how you can test these changes locally. - -We are going to run docker containers for cellxgene and an apache server running a reverse proxy on a local docker network. We run the cellxgene container without exposing any ports so that we cannot access it directly, only through the apache server. We can also update our cellxgene Dockerfile so that we can install a local build instead of having to deploy to pypi. - -1 Create Docker network, this allows the containers to communicate with each other. - -``` -docker network create cxg -``` - -2 Create and run cellxgene container - -(optional) To install cellxgene from the local codebase - -a Create sdist file -`make pydist` - -b Update Dockerfile to install from dist - -``` -FROM ubuntu:bionic - -ENV LC_ALL=C.UTF-8 -ENV LANG=C.UTF-8 -COPY [ "dist/", "/cellxgene/dist/" ] - -RUN apt-get update && \ - apt-get install -y build-essential libxml2-dev python3-dev python3-pip zlib1g-dev && \ - pip3 install /cellxgene/dist/cellxgene-0.5.1.tar.gz - -ENTRYPOINT ["cellxgene"] -``` - -(required) Build container -`docker build . -t cellxgene` - -3 Create the proxy container - -In a separate directory create these two files - -Dockerfile - -``` -FROM rgoyard/apache-proxy:latest -ADD proxy.conf /conf/ -``` - -proxy.conf - -``` -ProxyPass "/data/" http://cellxgene:5005/ -ProxyPassReverse "/data/" http://cellxgene:5005/ -``` - -Build the container -`docker build -t proxy .` - -4 Run containers and attach to network - -``` -docker run -d -p 80:80 --network cxg --name proxy proxy -docker run -v "$PWD/example-dataset/:/data/" --name cellxgene --network cxg cellxgene launch --host 0.0.0.0 data/pbmc3k.h5ad -``` - -5 Go to served site - -http://localhost/data/ diff --git a/docs/Gemfile b/docs/Gemfile deleted file mode 100644 index e44e449c..00000000 --- a/docs/Gemfile +++ /dev/null @@ -1,2 +0,0 @@ -source 'https://rubygems.org' -gem "github-pages", group: :jekyll_plugins diff --git a/docs/_config.yml b/docs/_config.yml deleted file mode 100644 index 877d6186..00000000 --- a/docs/_config.yml +++ /dev/null @@ -1,11 +0,0 @@ -theme: jekyll-theme-cayman -show_downloads: false -baseurl: /cellxgene - -nav: - - title: Getting Started - url: getting-started.html - - title: Data - url: data.html - - title: FAQ - url: faq.html diff --git a/docs/_layouts/default.html b/docs/_layouts/default.html deleted file mode 100644 index 43f4e607..00000000 --- a/docs/_layouts/default.html +++ /dev/null @@ -1,49 +0,0 @@ - - - - - {% if site.google_analytics %} - - - {% endif %} - - -{% seo %} - - - - - - - - - -
- {{ content }} - - -
- - diff --git a/docs/assets/css/style.scss b/docs/assets/css/style.scss deleted file mode 100644 index 5742f930..00000000 --- a/docs/assets/css/style.scss +++ /dev/null @@ -1,4 +0,0 @@ ---- ---- - -@import "{{ site.theme }}"; diff --git a/docs/cellxgene-demo-1.gif b/docs/cellxgene-demo-1.gif deleted file mode 100644 index e985434b..00000000 Binary files a/docs/cellxgene-demo-1.gif and /dev/null differ diff --git a/docs/cellxgene-demo-2.gif b/docs/cellxgene-demo-2.gif deleted file mode 100644 index 104189f4..00000000 Binary files a/docs/cellxgene-demo-2.gif and /dev/null differ diff --git a/docs/cellxgene-demo-3.gif b/docs/cellxgene-demo-3.gif deleted file mode 100644 index 6fecaa17..00000000 Binary files a/docs/cellxgene-demo-3.gif and /dev/null differ diff --git a/docs/cellxgene-demo.gif b/docs/cellxgene-demo.gif deleted file mode 100644 index 8a04a852..00000000 Binary files a/docs/cellxgene-demo.gif and /dev/null differ diff --git a/docs/cellxgene-opening-screenshot.png b/docs/cellxgene-opening-screenshot.png deleted file mode 100644 index 7f16879b..00000000 Binary files a/docs/cellxgene-opening-screenshot.png and /dev/null differ diff --git a/docs/customGene.gif b/docs/customGene.gif deleted file mode 100644 index 70271a57..00000000 Binary files a/docs/customGene.gif and /dev/null differ diff --git a/docs/data.md b/docs/data.md deleted file mode 100644 index 721232ca..00000000 --- a/docs/data.md +++ /dev/null @@ -1,94 +0,0 @@ ---- -layout: default -title: data -description: Data ---- - -# Using `cellxgene prepare` - -#### What is `cellxgene prepare`? - -`prepare` offers an easy command line interface (CLI) to preliminarily wrangle your data into the required format for previewing it with `cellxgene`. - -#### What is `cellxgene prepare` _not_? - -`cellxgene prepare` is not meant as a way to formally process or analyze your data. It's simply a utility for quickly wrangling your data into cellxgene-compatible format and computing a "vanilla" embedding so you can try out `cellxgene` and get a general sense of a dataset. - -#### What input formats does it accept? - -Currently, we accept `h5ad` and `loom` files, as well as `10x` directories, and are hoping to accept more formats in the future. - -While we'd like to support quick conversion from seurat and bioconductor, these packages don't currently output a python-parseable intermediate file type. In the meantime, you might check out the [converters](https://satijalab.org/seurat/v3.0/conversion_vignette.html) that are under early development. - -#### What can `cellxgene prepare` do? - -`prepare` uses scanpy to: - -- Handle simple data normalization (from a [recipe](https://www.pydoc.io/pypi/scanpy-0.2.3/autoapi/preprocessing/recipes/index.html)) -- Do basic preprocessing to run PCA and compute the neighbor graph -- Infer clusters -- Reduce dimensionality to generate embeddings. - You can control which steps to run and their methods (when applicable), via the CLI. The CLI also includes options for computing QC metrics, enforcing matrix sparcity, specifying index names, and plotting output. - -**To see a full list of available arguments and options, run `cellxgene prepare --help`.** - -#### How do I use `cellxgene prepare`? - -As a quick example, let's construct a command to use `prepare` to take a raw expression matrix and generate a processed `h5ad` ready to visualize with cellxgene. - -We'll start off using the raw data from the pbmc3k dataset. This dataset is described [here](https://icb-scanpy.readthedocs-hosted.com/en/stable/api/scanpy.datasets.pbmc3k.html), and is available as part of the scanpy API. For this example, we'll assume this raw data is stored in a file called `pbmc3k-raw.h5ad`. - -Our `prepare` compose our command looks like this: - - -Let's look at what `prepare` is doing to our data, and how each step relates to the command above. You can see a walkthrough of what's going on under the hood for this example in [this notebook](https://github.com/chanzuckerberg/cellxgene-vignettes/blob/master/dataset-processing/pbmc3k-prepare-example.ipynb). - -**1 - Compute quality control metrics and store this in our `AnnData` object for later inspection (A)** -**2 - Normalize the expression matrix using a basic preprocessing recipe (B)** -**3 - Do some preprocessing to run PCA and compute the neighbor graph (auto)** -**4 - Infer clusters with the Louvain algorithm and store these labels to visualize later (auto)** -**5 - Compute and store umap and tsne embeddings (C)** -**6 - Write results to file (D)** - -# Example datasets to use with cellxgene - - -**To download and use these datasets, run:** -`curl -O [URL]` -`unzip [filename.zip]` -`cellxgene launch [filename.h5ad] --open` - -### Peripheral blood mononuclear cells - -Healthy human PBMCs (10X). - -- Source: [10X genomics](https://support.10xgenomics.com/single-cell-gene-expression/datasets/1.1.0/pbmc3k) -- Cells: 2,638 -- File size: 19MB -- [Raw data](http://cf.10xgenomics.com/samples/cell-exp/1.1.0/pbmc3k/pbmc3k_filtered_gene_bc_matrices.tar.gz) -- [Processing](https://github.com/chanzuckerberg/cellxgene-vignettes/blob/master/dataset-processing/pbmc3k-processing.ipynb) -- Download: `curl -O https://cellxgene-example-data.czi.technology/pbmc3k.h5ad.zip` - -### Tabula muris - -20 organs and tissues from healthy mice (Smart-Seq2). -Rich metadata and annotations. - -- Source: [bioRxiv, CZBiohub](https://www.biorxiv.org/content/10.1101/237446v2) -- Cells: 45,423 -- File size: 174MB -- [Raw data](https://figshare.com/projects/Tabula_Muris_Transcriptomic_characterization_of_20_organs_and_tissues_from_Mus_musculus_at_single_cell_resolution/27733) -- [Processing](https://github.com/chanzuckerberg/cellxgene-vignettes/blob/master/dataset-processing/tabula-muris-processing.ipynb) -- Download: `curl -O https://cellxgene-example-data.czi.technology/tabula-muris.h5ad.zip` - -### Tabula muris senis - -22 organs and tissues from healthy mice at ages 3mo, 18mo, 21mo, and 24mo (Smart-Seq2). -Rich metadata and annotations. - -- Source: [bioRxiv, CZBiohub](https://www.biorxiv.org/content/10.1101/661728v1) -- Cells: 81,478 -- File size: 3.9GB -- Raw data [geo link coming soon!] -- [Processing](https://www.biorxiv.org/content/10.1101/661728v1) -- Download: `curl -O https://cellxgene-example-data.czi.technology/tabula-muris-senis.h5ad.zip` diff --git a/docs/diffexp.gif b/docs/diffexp.gif deleted file mode 100644 index d5cf54a6..00000000 Binary files a/docs/diffexp.gif and /dev/null differ diff --git a/docs/faq.md b/docs/faq.md deleted file mode 100644 index 9a062690..00000000 --- a/docs/faq.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -layout: default -title: FAQ -description: Frequently Asked Questions ---- - -# Data formatting - -#### What file formats can I use with _cellxgene_? - -Currently, you can go straight into `cellxgene launch` with your own analyzed data in h5ad format, after you have performed dimenstionality reduction (tsne, umap) and clustering (louvain). - -If your data is in a different format, and/or you still need to perform dimensionality reduction and clustering, `cellxgene` can do that for you with the `prepare` command. `cellxgene prepare` runs `scanpy` under the hood and can read in any format that is currently supported by `scanpy` (including mtx, loom, and more listed [here](https://scanpy.readthedocs.io/en/latest/api/index.html#reading)). - -The output of `cellxgene prepare` is a h5ad file with your computed clusters and tsne/umap projections that can be used in `cellxgene launch`. - -#### I have a directory of 10X-Genomics data with _mtx_ files and I've never used _scanpy_, can I use _cellxgene_? - -Yep! This should only take a couple steps. We'll assume your data is in a folder called `data/` and you've successfully installed `cellxgene` with the `louvain` packages as described above. Just run - -``` -cellxgene prepare data/ --output=data-processed.h5ad --layout=umap -``` - -Depending on the size of the dataset, this may take some time. Once it's done, call - -``` -cellxgene launch data-processed.h5ad --layout=umap --open -``` - -And your web browser should open with an interactive view of your data. - -#### I have extra metadata that I want to add to my dataset - -Currently this is not supported directly, but you should be able to do this yourself using `scanpy`. For example, this [notebook](https://github.com/falexwolf/fun-analyses/blob/master/tabula_muris/tabula_muris.ipynb) shows adding the contents of a `csv` file with metadata to an `anndata` object. For now, you could do this manually on your data in the same way and then save out the result before loading into `cellxgene`. - -#### What part of the _anndata_ objects does cellxgene pull in for visualization? - -- `.obs` and `.var` annotations are use to extract metadata for filtering -- `.X` is used to display expression (histograms, scatterplot & colorscale) and to compute differential expression -- `.obsm` is used for layout. If an embedding has more than two components, the first two will be used for visualization. - -#### I have a BIG dataset - how can I make cellxgene run as fast as possible? - -If your dataset requires gigabytes of disk space, you may need to select an appropriate storage format in order to effectively utilize `cellxgene`. Tips and tricks: - -- `cellxgene` is optimized for columnar data access. For large datasets, format the expression matrix (`.X`) as either a [SciPy CSC sparse matrix](https://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.csc_matrix.html) or a dense Numpy array (whichever creates a smaller `h5ad` file). If you are using `cellxgene prepare`, include the `--sparse` flag to ensure `.X` is formatted as a CSC sparse matrix (by default, `.X` will be a dense matrix). -- `cellxgene` start time is directly proportional to `h5ad` file size and the speed of your file system. Expect that large (eg, million cell) datasets will take minutes to load, even on relatively fast computers with a high performance local hard drive. Once loaded, exploring metadata should still be quick. -- If your dataset size exceeds the size of memory (RAM) on the host computer, differential expression calculations will be extremely slow (or fail, if you run out of virtual memory). - -# Algorithms - -#### How are you computing and sorting differential expression results? - -We use a [Welch's _t_-test](https://en.wikipedia.org/wiki/Welch%27s_t-test) implementation including the same variance overestimation correction as used in `scanpy`. We sort the `tscore` to identify the top N genes, and then filter to remove any that fall below a cutoff log fold change value, which can help remove spurious test results. The default threshold is `0.01` and can be changed using the option `--diffexp-lfc-cutoff`. - -# Problems, errors, & bugs - -#### How do I create a Python environment for _cellxgene_? - -If you use conda and want to create a [conda environment](https://conda.io/docs/user-guide/tasks/manage-environments.html) for _cellxgene_ you can use the following commands - -``` -conda create --yes -n cellxgene python=3.7 -conda activate cellxgene -pip install cellxgene -``` - -Or you can create a virtual environment by using - -``` -ENV_NAME=cellxgene -python3.7 -m venv ${ENV_NAME} -source ${ENV_NAME}/bin/activate -pip install cellxgene -``` - -#### In my _prepare_ command I received the following error `Warning: louvain module is not installed, no clusters will be calculated. To fix this please install cellxgene with the optional feature louvain enabled` - -Louvain clustering requires additional dependencies, so we don't include them by default. For now, you need to specify that you want these packages by using - -``` -pip install cellxgene[louvain] -``` - -#### I ran _prepare_ and I'm getting results that look unexpected - -You might want to try running one of the preprocessing recipes included with `scanpy` (read more about them [here](https://scanpy.readthedocs.io/en/latest/api/index.html#recipes)). You can specify this with the `--recipe` option, such as - -``` -cellxgene prepare data/ --output=data-processed.h5ad --recipe=zheng17 -``` - -It should be easy to run `prepare` then call `cellxgene launch` a few times with different settings to explore different behaviors. We may explore adding other preprocessing options in the future. - -#### I tried to `pip install cellxgene` and got a weird error I don't understand - -This may happen, especially as we work out bugs in our installation process! Please create a new [Github issue](https://github.com/chanzuckerberg/cellxgene/issues), explain what you did, and include all the error messages you saw. It'd also be super helpful if you call `pip freeze` and include the full output alongside your issue. - -#### I'm following the developer instructions and get an error about "missing files and directories” when trying to build the client - -This is likely because you do not have node and npm installed, we recommend using [nvm](https://github.com/creationix/nvm) if you're new to using these tools. diff --git a/docs/getting-started.md b/docs/getting-started.md deleted file mode 100644 index 5ee21ecb..00000000 --- a/docs/getting-started.md +++ /dev/null @@ -1,116 +0,0 @@ -## getting started - -You'll need **python 3.6+** and **Google Chrome**. -The web UI is tested on OSX and Windows using Chrome, and the python CLI is tested on OSX and Ubuntu (via WSL/Windows). It should work on other platforms, but if you run into trouble let us know. - -To install run - -``` -pip install cellxgene -``` - -To start exploring a dataset call - -``` -cellxgene launch dataset.h5ad --open -``` - -If you want an example dataset download [this file](https://github.com/chanzuckerberg/cellxgene/raw/master/example-dataset/pbmc3k.h5ad) and then call - -``` -cellxgene launch pbmc3k.h5ad --open -``` - -On Mac OS and Ubuntu, you should see your web browser open with the following - - - -**Note**: automatic opening of the browser with the `--open` flag only works on OS X, on other platforms you'll need to directly point to the provided link in your browser. - -There are several options available, such as: - -- `--layout` to specify the layout as `tsne`, `umap`, `diffmap`, `phate`, `draw_graph_fa`, or `draw_graph_fr` -- `--title` to show a title on the explorer -- `--open` to automatically open the web browser after launching (OS X only) - -To see all options call - -``` -cellxgene launch --help -``` - -There is an additional subcommand called `cellxgene prepare` that takes an existing dataset in one of several formats and applies minimal preprocessing and reformatting so that `launch` can use it (see [the next section](##data-formatting) for more info on `prepare`). - -## data formatting - -### requirements - -The `launch` command assumes that the data is stored in the `.h5ad` format from the [`anndata`](https://anndata.readthedocs.io/en/latest/index.html) library. It also assumes that certain computations have already been performed. Briefly, the `.h5ad` format wraps a two-dimensional `ndarray` and stores additional metadata as "annotations" for either observations (referred to as `obs` and `obsm`) or variables (`var` and `varm`). `cellxgene launch` makes the following assumptions about your data (we recommend loading and inspecting your data using `scanpy` to validate these assumptions) - -- an `obs` field has a unique identifier for every cell (you can specify which field to use with the `--obs-names` option, by default it will use the value of `data.obs_names`) -- a `var` field has a unique identifier for every gene (you can specify which field to use with the `--var-names` option, by default it will use the value of `data.var_names`) -- an `obsm` field contains the two-dimensional coordinates for the layout that you want to render (e.g. `X_umap` for the `umap` layout) -- any additional `obs` fields will be rendered as per-cell continuous or categorical metadata by the app (e.g. `louvain` cluster assignments) - -### prepare - -The `prepare` command is included to help you format your data. It uses `scanpy` under the hood. This is especially useful if you are starting with raw unanalyzed data and are unfamiliar with `scanpy`. - -To prepare from an existing `.h5ad` file use - -``` -cellxgene prepare dataset.h5ad --output=dataset-processed.h5ad -``` - -This will load the input data, perform PCA and nearest neighbor calculations, compute `umap` and `tsne` layouts and `louvain` cluster assignments, and save the results in a new file called `dataset-processed.h5ad` that can be loaded using `cellxgene launch`. Data can be loaded from several formats, including `.h5ad` `.loom` and a `10-Genomics-formatted` `mtx` directory. Several options are available, including running one of the preprocessing `recipes` included with `scanpy`, which include steps like cell filtering and gene selection. To learn more about the `recipes` please see the `scanpy` [documentation](https://scanpy.readthedocs.io/en/latest/api/index.html#recipes). - -Depending on the options chosen, `prepare` can take a long time to run (a few minutes for datasets with 10-100k cells, up to an hour or more for datasets with >100k cells). If you want `prepare` to run faster we recommend using the `sparse` option and only computing the layout for `umap`, using a call like this - -``` -cellxgene prepare dataset.h5ad --output=dataset-processed.h5ad --layout=umap --sparse -``` - -To see all options call - -``` -cellxgene prepare --help -``` - -**Note**: `cellxgene prepare` will only perform `louvain` clustering if you have the `python-igraph` and `louvain` packages installed. To make sure they are installed alongside `cellxgene` use - -``` -pip install cellxgene[louvain] -``` - -If the aforementioned optional package installation fails, you can also install these packages directly: - -``` -pip install python-igraph louvain>=0.6 -``` - -## conda and virtual environments - -If you use conda and want to create a conda environment for `cellxgene` you can use the following commands - -``` -conda create --yes -n cellxgene python=3.7 -conda activate cellxgene -pip install cellxgene -``` - -Or you can create a virtual environment by using - -``` -ENV_NAME=cellxgene -python3.7 -m venv ${ENV_NAME} -source ${ENV_NAME}/bin/activate -pip install cellxgene -``` - -## docker - -We have included a dockerfile to conveniently run cellxgene from docker. - -1. Build the image `docker build . -t cellxgene` -2. Run the container and mount data `docker run -v "$PWD/example-dataset/:/data/" -p 5005:5005 cellxgene launch --host 0.0.0.0 data/pbmc3k.h5ad` - - You will need to use --host 0.0.0.0 to have the container listen to incoming requests from the browser diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index b48451d1..00000000 --- a/docs/index.md +++ /dev/null @@ -1,41 +0,0 @@ -_cellxgene_ is an interactive data explorer for single-cell transcriptomics data. Whether you need to visualize one thousand cells or one million, _cellxgene_ helps you gain insight into your single-cell data. - -## features - -#### flexible selections, coloring, and differential expression of your selected sets of cells - - -#### single-gene analyses (e.g. expression analysis) - - -## quick start - -To install _cellxgene_ you need Python 3.6+. We recommend [installing _cellxgene_ into a conda or virtual environment.](/faq.html#how-do-i-create-a-python-environment-for-cellxgene) - -Install the package. -``` bash -pip install cellxgene -``` - -Download an example [anndata](https://anndata.readthedocs.io/en/latest/) file - -``` bash -curl -o pbmc3k.h5ad https://raw.githubusercontent.com/chanzuckerberg/cellxgene/master/example-dataset/pbmc3k.h5ad -``` - -Launch _cellxgene_ -``` bash -cellxgene launch pbmc3k.h5ad --open -``` - -To explore more datasets already formatted for _cellxgene_, see [Data](data) or -visit [Getting Started](getting-started) to learn more about formatting your own -data for _cellxgene_. - -## getting help - -We'd love to hear from you! - -For questions, suggestions, or accolades, [join the `#cellxgene-users` channel on the CZI Science Slack](https://join-cellxgene-users.herokuapp.com/) and say "hi!". - -For any errors, [report bugs on Github](https://github.com/chanzuckerberg/cellxgene/issues). diff --git a/docs/prepare-cmd-example.jpg b/docs/prepare-cmd-example.jpg deleted file mode 100644 index 57af0f30..00000000 Binary files a/docs/prepare-cmd-example.jpg and /dev/null differ diff --git a/example-dataset/pbmc3k.h5ad b/example-dataset/pbmc3k.h5ad deleted file mode 100644 index e529a6d8..00000000 Binary files a/example-dataset/pbmc3k.h5ad and /dev/null differ diff --git a/fbs/matrix.fbs b/fbs/matrix.fbs deleted file mode 100644 index cf506f57..00000000 --- a/fbs/matrix.fbs +++ /dev/null @@ -1,73 +0,0 @@ - -/* - - Flatbuffers schema for use in cellxgene wire-format. - - Schema defines a general purpose, polymorphic, 2D matrix. Data is - organized in a columnar layout. Each column is homomorphic, and - several column types are supported: - - IEEE 32 and 64 bit floats - - signed and unsigned 32 bit integers - - JSON/UTF8 encoded array (for other types) - - https://github.com/google/flatbuffers - http://google.github.io/flatbuffers/ - - NOTE: IF YOU MODIFY THIS FILE, YOU MUST RECOMPILE AND COMMIT - RESULTING FILES TO THE REPO: - * server/app/util/fbs/NetEncoding/* - * client/src/util/stateManager/matrix_generated.js - -*/ - -namespace NetEncoding; - -table Float32Array { - data: [float32]; -} - -table Uint32Array { - data: [uint32]; -} - -table Int32Array { - data: [int32]; -} - -table Float64Array { - data: [float64]; -} - -table JSONEncodedArray { - // contains a UTF-8/JSON encoded array. Used to store other - // types (or polymorphic arrays) - data: [uint8]; -} - -union TypedArray { - Float32Array, - Int32Array, - Uint32Array, - Float64Array, - JSONEncodedArray -} - -// Extra level of indirection required because vector of union not yet supported -table Column { - u: TypedArray; -} - -// 2D matrix stored in columnar layout -// -table Matrix { - n_rows: uint32; // all columns have this length - n_cols: uint32; // same as columns.length - columns: [Column]; // length n_cols - - // optional row and column index, with same length as corresponding dimension. - // If null, defaults to numeric index, ie, [0, n_rows) or [0, n_cols) - col_index: TypedArray; - row_index: TypedArray; -} - -root_type Matrix; diff --git a/makefile b/makefile deleted file mode 100644 index 1e0b1514..00000000 --- a/makefile +++ /dev/null @@ -1,132 +0,0 @@ -BUILDDIR := build -CLIENTBUILD := $(BUILDDIR)/client -SERVERBUILD := $(BUILDDIR)/server -CLEANFILES := $(BUILDDIR)/ client/build dist cellxgene.egg-info - -PART ?= patch - -# BUILDING PACKAGE - -build : clean build-server - @echo "done" - -build-server : build-client - mkdir -p $(SERVERBUILD) - cp -r server/* $(SERVERBUILD) - cp -r client/build/ $(CLIENTBUILD) - mkdir -p $(SERVERBUILD)/app/web/static/img - mkdir -p $(SERVERBUILD)/app/web/templates/ - cp $(CLIENTBUILD)/index.html $(SERVERBUILD)/app/web/templates/ - cp -r $(CLIENTBUILD)/static $(SERVERBUILD)/app/web/ - cp $(CLIENTBUILD)/favicon.png $(SERVERBUILD)/app/web/static/img - cp $(CLIENTBUILD)/service-worker.js $(SERVERBUILD)/app/web/static/js/ - cp MANIFEST.in README.md setup.cfg setup.py $(BUILDDIR) - -build-client : - npm install --prefix client/ client - npm run --prefix client build - -# If you are actively developing in the server folder use this, dirties the source tree -build-for-server-dev : clean-server build-client - mkdir -p server/app/web/static/img - mkdir -p server/app/web/static/js - mkdir -p server/app/web/templates/ - cp client/build/index.html server/app/web/templates/ - cp -r client/build/static server/app/web/ - cp client/build/favicon.png server/app/web/static/img - cp client/build/service-worker.js server/app/web/static/js/ - -clean : clean-lite clean-server - rm -rf client/node_modules - -# cleaning node_modules is the longest one, so we avoid that if possible -clean-lite : - rm -rf $(CLEANFILES) - -clean-server : - rm -f server/app/web/templates/index.html - rm -rf server/app/web/static - -.PHONY : build build-server build-client build-for-server-dev clean clean-lite clean-server - -# CREATING DISTRIBUTION RELEASE - -pydist : build - cd $(BUILDDIR); python setup.py sdist -d ../dist - @echo "done" - -.PHONY : pydist - -# RELEASE HELPERS - -# create new version to commit to master -release-stage-1 : dev-env bump clean-lite gen-package-lock - @echo "Version bumped part:$(PART) and client built. Ready to commit and push" - -# build dist and release to dev pypi -release-stage-2 : dev-env pydist twine - @echo "Dist built and uploaded to test.pypi.org" - @echo "Test the install:" - @echo " make install-release-test" - @echo "Then upload to Pypi prod:" - @echo " make twine-prod" - -release-stage-final: twine-prod - @echo "Release uploaded to pypi.org" - -# DANGER: releases directly to prod -# use this if you accidently burned a test release version number, -release-directly-to-prod : dev-env pydist twine-prod - @echo "Dist built and uploaded to pypi.org" - @echo "Test the install:" - @echo " make install-release" - -dev-env : - pip install -r server/requirements-dev.txt - -# give PART=[major, minor, part] as param to make bump -bump : - bumpversion --config-file .bumpversion.cfg $(PART) - -twine : - twine upload --repository-url https://test.pypi.org/legacy/ dist/* - -twine-prod : - twine upload dist/* - -# quicker than re-building client -gen-package-lock : - npm install --prefix client/ client - -.PHONY : release-stage-1 release-stage-2 release-stage-final release-burned dev-env bump twine twine-prod gen-package-lock - -# INSTALL - -# setup.py sucks when you have your library in a separate folder, adding these in to help setup envs - -# install from build directory -install : uninstall - cd $(BUILDDIR); pip install -e . - -# install from source tree for development -install-dev : uninstall - pip install -e . - -# install from test.pypi to test your release -install-release-test : uninstall - pip install --no-cache-dir --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple cellxgene - @echo "Installed cellxgene from test.pypi.org, now run and smoke test" - -# install from pypi to test your release -install-release : uninstall - pip install --no-cache-dir cellxgene - @echo "Installed cellxgene from pypi.org" - -# install from dist -install-dist : uninstall - pip install dist/cellxgene*.tar.gz - -uninstall : - pip uninstall -y cellxgene || : - -.PHONY : install install-dev install-release-test install-release uninstall diff --git a/server/__init__.py b/server/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/server/__main__.py b/server/__main__.py deleted file mode 100644 index 1f0b8730..00000000 --- a/server/__main__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Work around bug https://github.com/pallets/werkzeug/issues/461 -if __package__ is None: - import sys - from pathlib import Path - - PKG_PATH = Path(__file__).parent - sys.path.insert(0, str(PKG_PATH.parent)) - import server # noqa F401 - - __package__ = PKG_PATH.name - -# Main thing -from .cli.cli import cli # noqa F402 - -cli() diff --git a/server/app/__init__.py b/server/app/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/server/app/app.py b/server/app/app.py deleted file mode 100644 index beb56f12..00000000 --- a/server/app/app.py +++ /dev/null @@ -1,38 +0,0 @@ -import os - -from flask import Flask -from flask_caching import Cache -from flask_compress import Compress -from flask_cors import CORS - -from server.app.rest_api.rest import get_api_resources -from server.app.util.utils import Float32JSONEncoder -from server.app.web import webapp - - -class Server: - def __init__(self): - self.data = None - self.cache = Cache(config={"CACHE_TYPE": "simple", "CACHE_DEFAULT_TIMEOUT": 860_000}) - self.app = None - - def create_app(self): - self.app = Flask(__name__, static_folder="web/static") - self.app.json_encoder = Float32JSONEncoder - self.cache.init_app(self.app) - Compress(self.app) - CORS(self.app) - - # Config - SECRET_KEY = os.environ.get("CXG_SECRET_KEY", default="SparkleAndShine") - self.app.config.update(SECRET_KEY=SECRET_KEY) - self.app.config.update(SCRIPTS=[]) - - resources = get_api_resources() - self.app.register_blueprint(webapp.bp) - self.app.register_blueprint(resources.blueprint) - self.app.add_url_rule("/", endpoint="index") - - def attach_data(self, data, title="Demo"): - self.app.config.update(DATASET_TITLE=title) - self.app.data = data diff --git a/server/app/driver/__init__.py b/server/app/driver/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/server/app/driver/driver.py b/server/app/driver/driver.py deleted file mode 100644 index 8a17b67f..00000000 --- a/server/app/driver/driver.py +++ /dev/null @@ -1,88 +0,0 @@ -from abc import ABCMeta, abstractmethod - -""" -Sort order for methods -1. Initialize -2. Helper -3. Filter -4. Data & Metadata -5. Computation -""" - - -class CXGDriver(metaclass=ABCMeta): - def __init__(self, data=None, args={}): - self.config = self._get_default_config() - self.config.update(args) - if data: - self._load_data(data) - else: - self.data = None - - def update(self, data=None, args={}): - self.config.update(args) - if data: - self._load_data(data) - - @staticmethod - def _get_default_config(): - return { - "layout": None, - "diffexp": None, - "max_category_items": None, - "diffexp_lfc_cutoff": None - } - - @property - def features(self): - features = { - "cluster": {"available": False}, - "layout": {"obs": {"available": False}, "var": {"available": False}}, - "diffexp": {"available": False}, - } - # TODO - Interactive limit should be generated from the actual available methods see GH issue #94 - if self.config["layout"]: - # TODO handle "var" when gene layout becomes available - features["layout"]["obs"] = {"available": True, "interactiveLimit": 50000} - if self.config["diffexp"]: - features["diffexp"] = {"available": True, "interactiveLimit": 50000} - return features - - @abstractmethod - def _load_data(self, data): - pass - - @abstractmethod - def annotation_to_fbs_matrix(self, axis, field=None): - """ - Gets annotation value for each observation - :param axis: string obs or var - :param fields: list of keys for annotation to return, returns all annotation values if not set. - :return: flatbuffer: in fbs/matrix.fbs encoding - """ - pass - - @abstractmethod - def data_frame_to_fbs_matrix(self, filter, axis): - pass - - @abstractmethod - def diffexp_topN(self, obsFilter1, obsFilter2, top_n=None, interactive_limit=None): - """ - Computes the top N differentially expressed variables between two observation sets. If mode - is "TOP_N", then stats for the top N - dataframes - contain a subset of variables, then statistics for all variables will be returned, otherwise - only the top N vars will be returned. - :param obsFilter1: filter: dictionary with filter params for first set of observations - :param obsFilter2: filter: dictionary with filter params for second set of observations - :param top_n: Limit results to top N (Top var mode only) - :param interactive_limit: -- don't compute if total # genes in dataframes are larger than this - :return: top N genes and corresponding stats - """ - pass - - @abstractmethod - def layout_to_fbs_matrix(self, filter): - """ same as layout, except returns a flatbuffer """ - pass diff --git a/server/app/rest_api/__init__.py b/server/app/rest_api/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/server/app/rest_api/rest.py b/server/app/rest_api/rest.py deleted file mode 100644 index aafe175a..00000000 --- a/server/app/rest_api/rest.py +++ /dev/null @@ -1,238 +0,0 @@ -from http import HTTPStatus -import pkg_resources -import warnings - -from flask import Blueprint, current_app, jsonify, make_response, request -from flask_restful import Api, Resource - -from server.app.util.constants import ( - Axis, - DiffExpMode, - JSON_NaN_to_num_warning_msg, -) -from server.app.util.errors import ( - FilterError, - InteractiveError, - JSONEncodingValueError, - PrepareError, -) - -""" -Sort order for routes -1. Initialize -2. Data & Metadata -3. Computation -""" - - -class SchemaAPI(Resource): - def get(self): - return make_response( - jsonify({"schema": current_app.data.schema}), HTTPStatus.OK - ) - - -class ConfigAPI(Resource): - def get(self): - config = { - "config": { - "features": [ - { - "method": "POST", - "path": "/cluster/", - **current_app.data.features["cluster"], - }, - { - "method": "POST", - "path": "/layout/obs", - **current_app.data.features["layout"]["obs"], - }, - { - "method": "POST", - "path": "/layout/var", - **current_app.data.features["layout"]["var"], - }, - { - "method": "POST", - "path": "/diffexp/", - **current_app.data.features["diffexp"], - }, - ], - "displayNames": { - "engine": f"cellxgene Scanpy engine version {pkg_resources.get_distribution('cellxgene').version}", - "dataset": current_app.config["DATASET_TITLE"], - }, - "parameters": { - "max-category-items": current_app.data.config["max_category_items"] - }, - "library_versions": { - "scanpy": pkg_resources.get_distribution("scanpy").version, - "cellxgene": pkg_resources.get_distribution("cellxgene").version, - "anndata": pkg_resources.get_distribution("cellxgene").version - } - } - } - return make_response(jsonify(config), HTTPStatus.OK) - - -class AnnotationsObsAPI(Resource): - def get(self): - fields = request.args.getlist("annotation-name", None) - preferred_mimetype = request.accept_mimetypes.best_match( - ["application/octet-stream"] - ) - try: - if preferred_mimetype == "application/octet-stream": - return make_response(current_app.data.annotation_to_fbs_matrix("obs", fields), - HTTPStatus.OK, - {"Content-Type": "application/octet-stream"}) - else: - return make_response(f"Unsupported MIME type '{request.accept_mimetypes}'", HTTPStatus.NOT_ACCEPTABLE) - except KeyError: - return make_response(f"Error bad key in {fields}", HTTPStatus.BAD_REQUEST) - except ValueError as e: - return make_response(str(e), HTTPStatus.INTERNAL_SERVER_ERROR) - - -class AnnotationsVarAPI(Resource): - def get(self): - fields = request.args.getlist("annotation-name", None) - preferred_mimetype = request.accept_mimetypes.best_match( - ["application/octet-stream"] - ) - try: - if preferred_mimetype == "application/octet-stream": - return make_response(current_app.data.annotation_to_fbs_matrix("var", fields), - HTTPStatus.OK, - {"Content-Type": "application/octet-stream"}) - else: - return make_response(f"Unsupported MIME type '{request.accept_mimetypes}'", HTTPStatus.NOT_ACCEPTABLE) - except KeyError: - return make_response(f"Error bad key in {fields}", HTTPStatus.BAD_REQUEST) - except ValueError as e: - return make_response(str(e), HTTPStatus.INTERNAL_SERVER_ERROR) - - -class DataVarAPI(Resource): - def put(self): - preferred_mimetype = request.accept_mimetypes.best_match( - ["application/octet-stream"] - ) - try: - if preferred_mimetype == "application/octet-stream": - filter_json = request.get_json() - filter = filter_json["filter"] if filter_json else None - return make_response( - current_app.data.data_frame_to_fbs_matrix( - filter, axis=Axis.VAR - ), - HTTPStatus.OK, - {"Content-Type": "application/octet-stream"}) - else: - return make_response(f"Unsupported MIME type '{request.accept_mimetypes}'", HTTPStatus.NOT_ACCEPTABLE) - except FilterError as e: - return make_response(e.message, HTTPStatus.BAD_REQUEST) - except ValueError as e: - return make_response(str(e), HTTPStatus.INTERNAL_SERVER_ERROR) - - -class DiffExpObsAPI(Resource): - def post(self): - args = request.get_json() - # confirm mode is present and legal - try: - mode = DiffExpMode(args["mode"]) - except KeyError: - return make_response("Error: mode is required", HTTPStatus.BAD_REQUEST) - except ValueError: - return make_response( - f"Error: invalid mode option {args['mode']}", HTTPStatus.BAD_REQUEST - ) - # Validate filters - if mode == DiffExpMode.VAR_FILTER or "varFilter" in args: - # not NOT_IMPLEMENTED - return make_response( - "mode=varfilter not implemented", HTTPStatus.NOT_IMPLEMENTED - ) - if mode == DiffExpMode.TOP_N and "count" not in args: - return make_response( - "mode=topN requires a count parameter", HTTPStatus.BAD_REQUEST - ) - - if "set1" not in args: - return make_response("set1 is required.", HTTPStatus.BAD_REQUEST) - if Axis.VAR in args["set1"]["filter"]: - return make_response( - "Var filter not allowed for set1", HTTPStatus.BAD_REQUEST - ) - # set2 - if "set2" not in args: - return make_response( - "Set2 as inverse of set1 is not implemented", HTTPStatus.NOT_IMPLEMENTED - ) - if Axis.VAR in args["set2"]["filter"]: - return make_response( - "Var filter not allowed for set2", HTTPStatus.BAD_REQUEST - ) - - set1_filter = args["set1"]["filter"] - set2_filter = args.get("set2", {"filter": {}})["filter"] - - # TODO: implement varfilter mode - - # mode=topN - count = args.get("count", None) - try: - diffexp = current_app.data.diffexp_topN( - set1_filter, - set2_filter, - count, - current_app.data.features["diffexp"]["interactiveLimit"], - ) - return make_response( - diffexp, HTTPStatus.OK, {"Content-Type": "application/json"} - ) - except (ValueError, FilterError) as e: - return make_response(e.message, HTTPStatus.BAD_REQUEST) - except InteractiveError: - return make_response("Non-interactive request", HTTPStatus.FORBIDDEN) - except JSONEncodingValueError as e: - # JSON encoding failure, usually due to bad data - warnings.warn(JSON_NaN_to_num_warning_msg) - return make_response(str(e), HTTPStatus.INTERNAL_SERVER_ERROR) - except ValueError as e: - return make_response(str(e), HTTPStatus.INTERNAL_SERVER_ERROR) - - -class LayoutObsAPI(Resource): - def get(self): - preferred_mimetype = request.accept_mimetypes.best_match( - ["application/octet-stream"] - ) - try: - if preferred_mimetype == "application/octet-stream": - return make_response(current_app.data.layout_to_fbs_matrix(), - HTTPStatus.OK, - {"Content-Type": "application/octet-stream"}) - else: - return make_response(f"Unsupported MIME type '{request.accept_mimetypes}'", HTTPStatus.NOT_ACCEPTABLE) - except PrepareError as e: - return make_response(e.message, HTTPStatus.INTERNAL_SERVER_ERROR) - except ValueError as e: - return make_response(str(e), HTTPStatus.INTERNAL_SERVER_ERROR) - - -def get_api_resources(): - bp = Blueprint("api", __name__, url_prefix="/api/v0.2") - api = Api(bp) - # Initialization routes - api.add_resource(SchemaAPI, "/schema") - api.add_resource(ConfigAPI, "/config") - # Data routes - api.add_resource(AnnotationsObsAPI, "/annotations/obs") - api.add_resource(AnnotationsVarAPI, "/annotations/var") - api.add_resource(DataVarAPI, "/data/var") - # Computation routes - api.add_resource(DiffExpObsAPI, "/diffexp/obs") - api.add_resource(LayoutObsAPI, "/layout/obs") - return api diff --git a/server/app/scanpy_engine/__init__.py b/server/app/scanpy_engine/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/server/app/scanpy_engine/diffexp.py b/server/app/scanpy_engine/diffexp.py deleted file mode 100644 index 3e7bc6d0..00000000 --- a/server/app/scanpy_engine/diffexp.py +++ /dev/null @@ -1,122 +0,0 @@ -import numpy as np -from scipy import sparse, stats - - -# Convenience function which handles sparse data -def _mean_var_n(X): - """ - Two-pass variance calculation. Numerically (more) stable - than naive methods (and same method used by numpy.var()) - https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Two-pass - """ - # fp_err_occurred is a flag indicating that a floating point error - # occured somewhere in our compute. Used to trigger non-finite - # number handling. - fp_err_occurred = False - - def fp_err_set(err, flag): - nonlocal fp_err_occurred - fp_err_occurred = True - - with np.errstate(divide="call", invalid="call", call=fp_err_set): - n = X.shape[0] - if sparse.issparse(X): - mean = X.mean(axis=0).A1 - dfm = X - mean - sumsq = np.sum(np.multiply(dfm, dfm), axis=0).A1 - v = sumsq / (n - 1) - else: - mean = X.mean(axis=0) - dfm = X - mean - sumsq = np.sum(np.multiply(dfm, dfm), axis=0) - v = sumsq / (n - 1) - - if fp_err_occurred: - mean[np.isfinite(mean) == False] = 0 # noqa: E712 - v[np.isfinite(v) == False] = 0 # noqa: E712 - return mean, v, n - - -def diffexp_ttest(adata, maskA, maskB, top_n=8, diffexp_lfc_cutoff=0.01): - """ - Return differential expression statistics for top N variables. - - Algorithm: - - compute log fold change (log2(meanA/meanB)) - - compute Welch's t-test statistic and pvalue (w/ Bonferroni correction) - - return top N abs(logfoldchange) where lfc > diffexp_lfc_cutoff - - If there are not N which meet criteria, augment by removing the logfoldchange - threshold requirement. - - Notes on alogrithm: - - Welch's ttest provides basic statistics test. - https://en.wikipedia.org/wiki/Welch%27s_t-test - - p-values adjusted with Bonferroni correction. - https://en.wikipedia.org/wiki/Bonferroni_correction - - :param adata: anndata dataframe - :param maskA: observation selection mask for set 1 - :param maskB: observation selection mask for set 2 - :param top_n: number of variables to return stats for - :param diffexp_lfc_cutoff: minimum - :return: for top N genes, [ varindex, logfoldchange, pval, pval_adj ] - """ - - if top_n > adata.n_obs: - top_n = adata.n_obs - - # mean, variance, N - calculate for both selections - meanA, vA, nA = _mean_var_n(adata._X[maskA]) - meanB, vB, nB = _mean_var_n(adata._X[maskB]) - - # variance / N - vnA = vA / min(nA, nB) # overestimate variance, would normally be nA - vnB = vB / min(nA, nB) # overestimate variance, would normally be nB - sum_vn = vnA + vnB - - # degrees of freedom for Welch's t-test - with np.errstate(divide="ignore", invalid="ignore"): - dof = sum_vn ** 2 / (vnA ** 2 / (nA - 1) + vnB ** 2 / (nB - 1)) - dof[np.isnan(dof)] = 1 - - # Welch's t-test score calculation - with np.errstate(divide="ignore", invalid="ignore"): - tscores = (meanA - meanB) / np.sqrt(sum_vn) - tscores[np.isnan(tscores)] = 0 - - # p-value - pvals = stats.t.sf(np.abs(tscores), dof) * 2 - pvals_adj = pvals * adata._X.shape[1] - pvals_adj[pvals_adj > 1] = 1 # cap adjusted p-value at 1 - - # logfoldchanges: log2(meanA / meanB) - logfoldchanges = np.log2(np.abs((meanA + 1e-9) / (meanB + 1e-9))) - - # find all with lfc > cutoff - lfc_above_cutoff_idx = np.nonzero(np.abs(logfoldchanges) > diffexp_lfc_cutoff)[0] - stats_to_sort = np.abs(tscores) - - # derive sort order - if lfc_above_cutoff_idx.shape[0] > top_n: - # partition top N - rel_t_partition = np.argpartition(stats_to_sort[lfc_above_cutoff_idx], -top_n)[-top_n:] - t_partition = lfc_above_cutoff_idx[rel_t_partition] - # sort the top N partition - rel_sort_order = np.argsort(stats_to_sort[t_partition])[::-1] - sort_order = t_partition[rel_sort_order] - else: - # partition and sort top N, ignoring lfc cutoff - partition = np.argpartition(stats_to_sort, -top_n)[-top_n:] - rel_sort_order = np.argsort(stats_to_sort[partition])[::-1] - indices = np.indices(stats_to_sort.shape)[0] - sort_order = indices[partition][rel_sort_order] - - # top n slice based upon sort order - logfoldchanges_top_n = logfoldchanges[sort_order] - pvals_top_n = pvals[sort_order] - pvals_adj_top_n = pvals_adj[sort_order] - - # varIndex, logfoldchange, pval, pval_adj - result = [[sort_order[i], logfoldchanges_top_n[i], pvals_top_n[i], pvals_adj_top_n[i]] for i in range(top_n)] - return result diff --git a/server/app/scanpy_engine/scanpy_engine.py b/server/app/scanpy_engine/scanpy_engine.py deleted file mode 100644 index 31cb3707..00000000 --- a/server/app/scanpy_engine/scanpy_engine.py +++ /dev/null @@ -1,474 +0,0 @@ -import warnings - -import numpy as np -import pandas -from pandas.core.dtypes.dtypes import CategoricalDtype -import anndata -from scipy import sparse - -from server.app.driver.driver import CXGDriver -from server.app.util.constants import Axis, DEFAULT_TOP_N, MAX_LAYOUTS -from server.app.util.errors import ( - FilterError, - JSONEncodingValueError, - PrepareError, - ScanpyFileError, -) -from server.app.util.utils import jsonify_scanpy, requires_data -from server.app.scanpy_engine.diffexp import diffexp_ttest -from server.app.util.fbs.matrix import encode_matrix_fbs - -""" -Sort order for methods -1. Initialize -2. Helper -3. Filter -4. Data & Metadata -5. Computation -""" - - -class ScanpyEngine(CXGDriver): - def __init__(self, data=None, args={}): - super().__init__(data, args) - if self.data: - self._validate_and_initialize() - - def update(self, data=None, args={}): - super().__init__(data, args) - if self.data: - self._validate_and_initialize() - - @staticmethod - def _get_default_config(): - return { - "layout": [], - "diffexp": "ttest", - "max_category_items": 100, - "obs_names": None, - "var_names": None, - "diffexp_lfc_cutoff": 0.01, - } - - @staticmethod - def _create_unique_column_name(df, col_name_prefix): - """ given the columns of a dataframe, and a name prefix, return a column name which - does not exist in the dataframe, AND which is prefixed by `prefix` - - The approach is to append a numeric suffix, starting at zero and increasing by - one, until an unused name is found (eg, prefix_0, prefix_1, ...). - """ - suffix = 0 - while f"{col_name_prefix}{suffix}" in df: - suffix += 1 - return f"{col_name_prefix}{suffix}" - - def _alias_annotation_names(self): - """ - The front-end relies on the existance of a unique, human-readable - index for obs & var (eg, var is typically gene name, obs the cell name). - The user can specify these via the --obs-names and --var-names config. - If they are not specified, use the existing index to create them, giving - the resulting column a unique name (eg, "name"). - - In both cases, enforce that the result is unique, and communicate the - index column name to the front-end via the obs_names and var_names config - (which is incorporated into the schema). - """ - for (ax_name, config_name) in ((Axis.OBS, "obs_names"), (Axis.VAR, "var_names")): - name = self.config[config_name] - df_axis = getattr(self.data, str(ax_name)) - if name is None: - # Default: create unique names from index - if not df_axis.index.is_unique: - raise KeyError( - f"Values in {ax_name}.index must be unique. " - "Please prepare data to contain unique index values, or specify an " - "alternative with --{ax_name}-name." - ) - name = self._create_unique_column_name(df_axis.columns, "name_") - self.config[config_name] = name - # reset index to simple range; alias name to point at the - # previously specified index. - df_axis.rename_axis(name, inplace=True) - df_axis.reset_index(inplace=True) - elif name in df_axis.columns: - # User has specified alternative column for unique names, and it exists - if not df_axis[name].is_unique: - raise KeyError( - f"Values in {ax_name}.{name} must be unique. " - "Please prepare data to contain unique values." - ) - df_axis.reset_index(drop=True, inplace=True) - else: - # user specified a non-existent column name - raise KeyError( - f"Annotation name {name}, specified in --{ax_name}-name does not exist." - ) - - @staticmethod - def _can_cast_to_float32(ann): - if ann.dtype.kind == "f": - if not np.can_cast(ann.dtype, np.float32): - warnings.warn( - f"Annotation {ann.name} will be converted to 32 bit float and may lose precision." - ) - return True - return False - - @staticmethod - def _can_cast_to_int32(ann): - if ann.dtype.kind in ["i", "u"]: - if np.can_cast(ann.dtype, np.int32): - return True - ii32 = np.iinfo(np.int32) - if ann.min() >= ii32.min and ann.max() <= ii32.max: - return True - return False - - @requires_data - def _create_schema(self): - self.schema = { - "dataframe": { - "nObs": self.cell_count, - "nVar": self.gene_count, - "type": str(self.data.X.dtype), - }, - "annotations": { - "obs": { - "index": self.config["obs_names"], - "columns": [] - }, - "var": { - "index": self.config["var_names"], - "columns": [] - } - }, - "layout": {"obs": []} - } - for ax in Axis: - curr_axis = getattr(self.data, str(ax)) - for ann in curr_axis: - ann_schema = {"name": ann} - dtype = curr_axis[ann].dtype - data_kind = dtype.kind - - if self._can_cast_to_float32(curr_axis[ann]): - ann_schema["type"] = "float32" - elif self._can_cast_to_int32(curr_axis[ann]): - ann_schema["type"] = "int32" - elif dtype == np.bool_: - ann_schema["type"] = "boolean" - elif data_kind == "O" and dtype == "object": - ann_schema["type"] = "string" - elif data_kind == "O" and dtype == "category": - ann_schema["type"] = "categorical" - ann_schema["categories"] = curr_axis[ann].dtype.categories.tolist() - else: - raise TypeError( - f"Annotations of type {curr_axis[ann].dtype} are unsupported by cellxgene." - ) - self.schema["annotations"][ax]["columns"].append(ann_schema) - - for layout in self.config['layout']: - layout_schema = { - "name": layout, - "type": "float32", - "dims": [f"{layout}_0", f"{layout}_1"] - } - self.schema["layout"]["obs"].append(layout_schema) - - def _load_data(self, data): - # as of AnnData 0.6.19, backed mode performs initial load fast, but at the - # cost of significantly slower access to X data. - try: - self.data = anndata.read_h5ad(data) - except ValueError: - raise ScanpyFileError( - "File must be in the .h5ad format. Please read " - "https://github.com/theislab/scanpy_usage/blob/master/170505_seurat/info_h5ad.md to " - "learn more about this format. You may be able to convert your file into this format " - "using `cellxgene prepare`, please run `cellxgene prepare --help` for more " - "information." - ) - except MemoryError: - raise ScanpyFileError("Error while loading file: out of memory, file is too large" - " for memory available") - except Exception as e: - raise ScanpyFileError( - f"Error while loading file: {e}, File must be in the .h5ad format, please check " - f"that your input and try again." - ) - - @requires_data - def _validate_and_initialize(self): - # var and obs column names must be unique - if not self.data.obs.columns.is_unique or not self.data.var.columns.is_unique: - raise KeyError(f"All annotation column names must be unique.") - - self._alias_annotation_names() - self._validate_data_types() - self.cell_count = self.data.shape[0] - self.gene_count = self.data.shape[1] - self._default_and_validate_layouts() - self._create_schema() - - @requires_data - def _default_and_validate_layouts(self): - """ function: - a) generate list of default layouts, if not already user specified - b) validate layouts are legal. remove/warn on any that are not - c) cap total list of layouts at global const MAX_LAYOUTS - """ - layouts = self.config['layout'] - # handle default - if layouts is None or len(layouts) == 0: - # load default layouts from the data. - layouts = [key[2:] for key in self.data.obsm_keys() if type(key) == str and key.startswith("X_")] - if len(layouts) == 0: - raise PrepareError(f"Unable to find any precomputed layouts within the dataset.") - - # remove invalid layouts - valid_layouts = [] - obsm_keys = self.data.obsm_keys() - for layout in layouts: - layout_name = f"X_{layout}" - if layout_name not in obsm_keys: - warnings.warn(f"Ignoring unknown layout name: {layout}.") - elif not self._is_valid_layout(self.data.obsm[layout_name]): - warnings.warn(f"Ignoring layout due to malformed shape or data type: {layout}") - else: - valid_layouts.append(layout) - - if len(valid_layouts) == 0: - raise PrepareError(f"No valid layout data.") - - # cap layouts to MAX_LAYOUTS - self.config['layout'] = valid_layouts[0:MAX_LAYOUTS] - - @requires_data - def _is_valid_layout(self, arr): - """ return True if this layout data is a valid array for front-end presentation: - * ndarray, with shape (n_obs, >= 2), dtype float/int/uint - * contains only finite values - """ - is_valid = type(arr) == np.ndarray and arr.dtype.kind in "fiu" - is_valid = is_valid and arr.shape[0] == self.data.n_obs and arr.shape[1] >= 2 - is_valid = is_valid and np.all(np.isfinite(arr)) - return is_valid - - @requires_data - def _validate_data_types(self): - if sparse.isspmatrix(self.data.X) and not sparse.isspmatrix_csc(self.data.X): - warnings.warn( - f"Scanpy data matrix is sparse, but not a CSC (columnar) matrix. " - f"Performance may be improved by using CSC." - ) - if self.data.X.dtype != "float32": - warnings.warn( - f"Scanpy data matrix is in {self.data.X.dtype} format not float32. " - f"Precision may be truncated." - ) - for ax in Axis: - curr_axis = getattr(self.data, str(ax)) - for ann in curr_axis: - datatype = curr_axis[ann].dtype - downcast_map = { - "int64": "int32", - "uint32": "int32", - "uint64": "int32", - "float64": "float32", - } - if datatype in downcast_map: - warnings.warn( - f"Scanpy annotation {ax}:{ann} is in unsupported format: {datatype}. " - f"Data will be downcast to {downcast_map[datatype]}." - ) - if isinstance(datatype, CategoricalDtype): - category_num = len(curr_axis[ann].dtype.categories) - if category_num > 500 and category_num > self.config['max_category_items']: - warnings.warn( - f"{str(ax).title()} annotation '{ann}' has {category_num} categories, this may be " - f"cumbersome or slow to display. We recommend setting the " - f"--max-category-items option to 500, this will hide categorical " - f"annotations with more than 500 categories in the UI" - ) - - @staticmethod - def _annotation_filter_to_mask(filter, d_axis, count): - mask = np.ones((count,), dtype=bool) - for v in filter: - if d_axis[v["name"]].dtype.name in ["boolean", "category", "object"]: - key_idx = np.in1d(getattr(d_axis, v["name"]), v["values"]) - mask = np.logical_and(mask, key_idx) - else: - min_ = v.get("min", None) - max_ = v.get("max", None) - if min_ is not None: - key_idx = (getattr(d_axis, v["name"]) >= min_).ravel() - mask = np.logical_and(mask, key_idx) - if max_ is not None: - key_idx = (getattr(d_axis, v["name"]) <= max_).ravel() - mask = np.logical_and(mask, key_idx) - return mask - - @staticmethod - def _index_filter_to_mask(filter, count): - mask = np.zeros((count,), dtype=bool) - for i in filter: - if type(i) == list: - mask[i[0]: i[1]] = True - else: - mask[i] = True - return mask - - @staticmethod - def _axis_filter_to_mask(filter, d_axis, count): - mask = np.ones((count,), dtype=bool) - if "index" in filter: - mask = np.logical_and( - mask, ScanpyEngine._index_filter_to_mask(filter["index"], count) - ) - if "annotation_value" in filter: - mask = np.logical_and( - mask, - ScanpyEngine._annotation_filter_to_mask( - filter["annotation_value"], d_axis, count - ), - ) - return mask - - @requires_data - def _filter_to_mask(self, filter, use_slices=True): - if use_slices: - obs_selector = slice(0, self.data.n_obs) - var_selector = slice(0, self.data.n_vars) - else: - obs_selector = None - var_selector = None - - if filter is not None: - if Axis.OBS in filter: - obs_selector = self._axis_filter_to_mask( - filter["obs"], self.data.obs, self.data.n_obs - ) - if Axis.VAR in filter: - var_selector = self._axis_filter_to_mask( - filter["var"], self.data.var, self.data.n_vars - ) - return obs_selector, var_selector - - @requires_data - def annotation_to_fbs_matrix(self, axis, fields=None): - if axis == Axis.OBS: - df = self.data.obs - else: - df = self.data.var - if fields is not None and len(fields) > 0: - df = df[fields] - return encode_matrix_fbs(df, col_idx=df.columns) - - @staticmethod - def slice_columns(X, var_mask): - """ - Slice columns from the matrix X, as specified by the mask - Semantically equivalent to X[:, var_mask], but handles sparse - matrices in a more performant manner. - """ - if var_mask is None: # noop - return X - if sparse.issparse(X): # use tuned getcol/hstack for performance - indices = np.nonzero(var_mask)[0] - cols = [X.getcol(i) for i in indices] - return sparse.hstack(cols, format="csc") - else: # else, just use standard slicing, which is fine for dense arrays - return X[:, var_mask] - - @requires_data - def data_frame_to_fbs_matrix(self, filter, axis): - """ - Retrieves data 'X' and returns in a flatbuffer Matrix. - :param filter: filter: dictionary with filter params - :param axis: string obs or var - :return: flatbuffer Matrix - - Caveats: - * currently only supports access on VAR axis - * currently only supports filtering on VAR axis - """ - if axis != Axis.VAR: - raise ValueError("Only VAR dimension access is supported") - try: - obs_selector, var_selector = self._filter_to_mask(filter, use_slices=False) - except (KeyError, IndexError, TypeError) as e: - raise FilterError(f"Error parsing filter: {e}") from e - if obs_selector is not None: - raise FilterError("filtering on obs unsupported") - - # Currently only handles VAR dimension - X = self.slice_columns(self.data._X, var_selector) - return encode_matrix_fbs(X, col_idx=np.nonzero(var_selector)[0], row_idx=None) - - @requires_data - def diffexp_topN(self, obsFilterA, obsFilterB, top_n=None, interactive_limit=None): - if Axis.VAR in obsFilterA or Axis.VAR in obsFilterB: - raise FilterError("Observation filters may not contain vaiable conditions") - try: - obs_mask_A = self._axis_filter_to_mask( - obsFilterA["obs"], self.data.obs, self.data.n_obs - ) - obs_mask_B = self._axis_filter_to_mask( - obsFilterB["obs"], self.data.obs, self.data.n_obs - ) - except (KeyError, IndexError) as e: - raise FilterError(f"Error parsing filter: {e}") from e - if top_n is None: - top_n = DEFAULT_TOP_N - result = diffexp_ttest( - self.data, obs_mask_A, obs_mask_B, top_n, self.config['diffexp_lfc_cutoff'] - ) - try: - return jsonify_scanpy(result) - except ValueError: - raise JSONEncodingValueError( - "Error encoding differential expression to JSON" - ) - - @requires_data - def layout_to_fbs_matrix(self): - """ - Return the default 2-D layout for cells as a FBS Matrix. - - Caveats: - * does not support filtering - * only returns Matrix in columnar layout - - All embeddings must be individually centered & scaled (isotropically) - to a [0, 1] range. - """ - try: - layout_data = [] - for layout in self.config["layout"]: - full_embedding = self.data.obsm[f"X_{layout}"] - embedding = full_embedding[:, :2] - - # scale isotropically - min = embedding.min(axis=0) - max = embedding.max(axis=0) - scale = np.amax(max - min) - normalized_layout = (embedding - min) / scale - - # translate to center on both axis - translate = 0.5 - ((max - min) / scale / 2) - normalized_layout = normalized_layout + translate - - normalized_layout = normalized_layout.astype(dtype=np.float32) - layout_data.append(pandas.DataFrame(normalized_layout, columns=[f"{layout}_0", f"{layout}_1"])) - - except ValueError as e: - raise PrepareError( - f"Layout has not been calculated using {self.config['layout']}, " - f"please prepare your datafile and relaunch cellxgene") from e - - df = pandas.concat(layout_data, axis=1, copy=False) - return encode_matrix_fbs(df, col_idx=df.columns, row_idx=None) diff --git a/server/app/util/__init__.py b/server/app/util/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/server/app/util/constants.py b/server/app/util/constants.py deleted file mode 100644 index 13c818a6..00000000 --- a/server/app/util/constants.py +++ /dev/null @@ -1,35 +0,0 @@ -from enum import Enum - - -DEFAULT_TOP_N = 10 - - -class AugmentedEnum(Enum): - def __hash__(self): - return self.value.__hash__() - - def __eq__(self, other): - if isinstance(other, type(self)) or isinstance(other, str): - return self.value == other - return False - - def __str__(self) -> str: - return self.value - - -class Axis(AugmentedEnum): - OBS = "obs" - VAR = "var" - - -class DiffExpMode(AugmentedEnum): - TOP_N = "topN" - VAR_FILTER = "varFilter" - - -JSON_NaN_to_num_warning_msg = ( - "JSON encoding failure - please verify all data are finite values (no NaN or Infinities)" -) -REACTIVE_LIMIT = 1_000_000 - -MAX_LAYOUTS = 30 diff --git a/server/app/util/errors.py b/server/app/util/errors.py deleted file mode 100644 index 6ee89a9d..00000000 --- a/server/app/util/errors.py +++ /dev/null @@ -1,61 +0,0 @@ -class FilterError(Exception): - """ - Raised when filter is malformed - """ - - def __init__(self, message): - self.message = message - - -class InteractiveError(Exception): - """ - Raised when computation would exceed interactive time - """ - - def __init__(self, message): - self.message = message - - -class JSONEncodingValueError(Exception): - """ - Raised when file loaded into scanpy is misformatted - """ - - def __init__(self, message): - self.message = message - - -class MimeTypeError(Exception): - """ - Raised when incompatible MIME type selected - """ - - def __init__(self, message): - self.message = message - - -class PrepareError(Exception): - """ - Raised when data is misprepared - """ - - def __init__(self, message): - self.message = message - - -class ScanpyFileError(Exception): - """ - Raised when file loaded into scanpy is misformatted - """ - - def __init__(self, message): - self.message = message - - -class DriverError(Exception): - """ - Raised when file loaded into scanpy is misformatted - """ - - def __init__(self, message): - self.message = message diff --git a/server/app/util/fbs/NetEncoding/Column.py b/server/app/util/fbs/NetEncoding/Column.py deleted file mode 100644 index 89b786d6..00000000 --- a/server/app/util/fbs/NetEncoding/Column.py +++ /dev/null @@ -1,41 +0,0 @@ -# automatically generated by the FlatBuffers compiler, do not modify - -# namespace: NetEncoding - -import flatbuffers - -class Column(object): - __slots__ = ['_tab'] - - @classmethod - def GetRootAsColumn(cls, buf, offset): - n = flatbuffers.encode.Get(flatbuffers.packer.uoffset, buf, offset) - x = Column() - x.Init(buf, n + offset) - return x - - # Column - def Init(self, buf, pos): - self._tab = flatbuffers.table.Table(buf, pos) - - # Column - def UType(self): - o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4)) - if o != 0: - return self._tab.Get(flatbuffers.number_types.Uint8Flags, o + self._tab.Pos) - return 0 - - # Column - def U(self): - o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(6)) - if o != 0: - from flatbuffers.table import Table - obj = Table(bytearray(), 0) - self._tab.Union(obj, o) - return obj - return None - -def ColumnStart(builder): builder.StartObject(2) -def ColumnAddUType(builder, uType): builder.PrependUint8Slot(0, uType, 0) -def ColumnAddU(builder, u): builder.PrependUOffsetTRelativeSlot(1, flatbuffers.number_types.UOffsetTFlags.py_type(u), 0) -def ColumnEnd(builder): return builder.EndObject() diff --git a/server/app/util/fbs/NetEncoding/Float32Array.py b/server/app/util/fbs/NetEncoding/Float32Array.py deleted file mode 100644 index 1acc426c..00000000 --- a/server/app/util/fbs/NetEncoding/Float32Array.py +++ /dev/null @@ -1,46 +0,0 @@ -# automatically generated by the FlatBuffers compiler, do not modify - -# namespace: NetEncoding - -import flatbuffers - -class Float32Array(object): - __slots__ = ['_tab'] - - @classmethod - def GetRootAsFloat32Array(cls, buf, offset): - n = flatbuffers.encode.Get(flatbuffers.packer.uoffset, buf, offset) - x = Float32Array() - x.Init(buf, n + offset) - return x - - # Float32Array - def Init(self, buf, pos): - self._tab = flatbuffers.table.Table(buf, pos) - - # Float32Array - def Data(self, j): - o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4)) - if o != 0: - a = self._tab.Vector(o) - return self._tab.Get(flatbuffers.number_types.Float32Flags, a + flatbuffers.number_types.UOffsetTFlags.py_type(j * 4)) - return 0 - - # Float32Array - def DataAsNumpy(self): - o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4)) - if o != 0: - return self._tab.GetVectorAsNumpy(flatbuffers.number_types.Float32Flags, o) - return 0 - - # Float32Array - def DataLength(self): - o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4)) - if o != 0: - return self._tab.VectorLen(o) - return 0 - -def Float32ArrayStart(builder): builder.StartObject(1) -def Float32ArrayAddData(builder, data): builder.PrependUOffsetTRelativeSlot(0, flatbuffers.number_types.UOffsetTFlags.py_type(data), 0) -def Float32ArrayStartDataVector(builder, numElems): return builder.StartVector(4, numElems, 4) -def Float32ArrayEnd(builder): return builder.EndObject() diff --git a/server/app/util/fbs/NetEncoding/Float64Array.py b/server/app/util/fbs/NetEncoding/Float64Array.py deleted file mode 100644 index 2ec343a2..00000000 --- a/server/app/util/fbs/NetEncoding/Float64Array.py +++ /dev/null @@ -1,46 +0,0 @@ -# automatically generated by the FlatBuffers compiler, do not modify - -# namespace: NetEncoding - -import flatbuffers - -class Float64Array(object): - __slots__ = ['_tab'] - - @classmethod - def GetRootAsFloat64Array(cls, buf, offset): - n = flatbuffers.encode.Get(flatbuffers.packer.uoffset, buf, offset) - x = Float64Array() - x.Init(buf, n + offset) - return x - - # Float64Array - def Init(self, buf, pos): - self._tab = flatbuffers.table.Table(buf, pos) - - # Float64Array - def Data(self, j): - o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4)) - if o != 0: - a = self._tab.Vector(o) - return self._tab.Get(flatbuffers.number_types.Float64Flags, a + flatbuffers.number_types.UOffsetTFlags.py_type(j * 8)) - return 0 - - # Float64Array - def DataAsNumpy(self): - o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4)) - if o != 0: - return self._tab.GetVectorAsNumpy(flatbuffers.number_types.Float64Flags, o) - return 0 - - # Float64Array - def DataLength(self): - o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4)) - if o != 0: - return self._tab.VectorLen(o) - return 0 - -def Float64ArrayStart(builder): builder.StartObject(1) -def Float64ArrayAddData(builder, data): builder.PrependUOffsetTRelativeSlot(0, flatbuffers.number_types.UOffsetTFlags.py_type(data), 0) -def Float64ArrayStartDataVector(builder, numElems): return builder.StartVector(8, numElems, 8) -def Float64ArrayEnd(builder): return builder.EndObject() diff --git a/server/app/util/fbs/NetEncoding/Int32Array.py b/server/app/util/fbs/NetEncoding/Int32Array.py deleted file mode 100644 index f3f8156f..00000000 --- a/server/app/util/fbs/NetEncoding/Int32Array.py +++ /dev/null @@ -1,46 +0,0 @@ -# automatically generated by the FlatBuffers compiler, do not modify - -# namespace: NetEncoding - -import flatbuffers - -class Int32Array(object): - __slots__ = ['_tab'] - - @classmethod - def GetRootAsInt32Array(cls, buf, offset): - n = flatbuffers.encode.Get(flatbuffers.packer.uoffset, buf, offset) - x = Int32Array() - x.Init(buf, n + offset) - return x - - # Int32Array - def Init(self, buf, pos): - self._tab = flatbuffers.table.Table(buf, pos) - - # Int32Array - def Data(self, j): - o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4)) - if o != 0: - a = self._tab.Vector(o) - return self._tab.Get(flatbuffers.number_types.Int32Flags, a + flatbuffers.number_types.UOffsetTFlags.py_type(j * 4)) - return 0 - - # Int32Array - def DataAsNumpy(self): - o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4)) - if o != 0: - return self._tab.GetVectorAsNumpy(flatbuffers.number_types.Int32Flags, o) - return 0 - - # Int32Array - def DataLength(self): - o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4)) - if o != 0: - return self._tab.VectorLen(o) - return 0 - -def Int32ArrayStart(builder): builder.StartObject(1) -def Int32ArrayAddData(builder, data): builder.PrependUOffsetTRelativeSlot(0, flatbuffers.number_types.UOffsetTFlags.py_type(data), 0) -def Int32ArrayStartDataVector(builder, numElems): return builder.StartVector(4, numElems, 4) -def Int32ArrayEnd(builder): return builder.EndObject() diff --git a/server/app/util/fbs/NetEncoding/JSONEncodedArray.py b/server/app/util/fbs/NetEncoding/JSONEncodedArray.py deleted file mode 100644 index 366ebadd..00000000 --- a/server/app/util/fbs/NetEncoding/JSONEncodedArray.py +++ /dev/null @@ -1,46 +0,0 @@ -# automatically generated by the FlatBuffers compiler, do not modify - -# namespace: NetEncoding - -import flatbuffers - -class JSONEncodedArray(object): - __slots__ = ['_tab'] - - @classmethod - def GetRootAsJSONEncodedArray(cls, buf, offset): - n = flatbuffers.encode.Get(flatbuffers.packer.uoffset, buf, offset) - x = JSONEncodedArray() - x.Init(buf, n + offset) - return x - - # JSONEncodedArray - def Init(self, buf, pos): - self._tab = flatbuffers.table.Table(buf, pos) - - # JSONEncodedArray - def Data(self, j): - o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4)) - if o != 0: - a = self._tab.Vector(o) - return self._tab.Get(flatbuffers.number_types.Uint8Flags, a + flatbuffers.number_types.UOffsetTFlags.py_type(j * 1)) - return 0 - - # JSONEncodedArray - def DataAsNumpy(self): - o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4)) - if o != 0: - return self._tab.GetVectorAsNumpy(flatbuffers.number_types.Uint8Flags, o) - return 0 - - # JSONEncodedArray - def DataLength(self): - o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4)) - if o != 0: - return self._tab.VectorLen(o) - return 0 - -def JSONEncodedArrayStart(builder): builder.StartObject(1) -def JSONEncodedArrayAddData(builder, data): builder.PrependUOffsetTRelativeSlot(0, flatbuffers.number_types.UOffsetTFlags.py_type(data), 0) -def JSONEncodedArrayStartDataVector(builder, numElems): return builder.StartVector(1, numElems, 1) -def JSONEncodedArrayEnd(builder): return builder.EndObject() diff --git a/server/app/util/fbs/NetEncoding/Matrix.py b/server/app/util/fbs/NetEncoding/Matrix.py deleted file mode 100644 index 8c9d0eb8..00000000 --- a/server/app/util/fbs/NetEncoding/Matrix.py +++ /dev/null @@ -1,98 +0,0 @@ -# automatically generated by the FlatBuffers compiler, do not modify - -# namespace: NetEncoding - -import flatbuffers - -class Matrix(object): - __slots__ = ['_tab'] - - @classmethod - def GetRootAsMatrix(cls, buf, offset): - n = flatbuffers.encode.Get(flatbuffers.packer.uoffset, buf, offset) - x = Matrix() - x.Init(buf, n + offset) - return x - - # Matrix - def Init(self, buf, pos): - self._tab = flatbuffers.table.Table(buf, pos) - - # Matrix - def NRows(self): - o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4)) - if o != 0: - return self._tab.Get(flatbuffers.number_types.Uint32Flags, o + self._tab.Pos) - return 0 - - # Matrix - def NCols(self): - o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(6)) - if o != 0: - return self._tab.Get(flatbuffers.number_types.Uint32Flags, o + self._tab.Pos) - return 0 - - # Matrix - def Columns(self, j): - o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(8)) - if o != 0: - x = self._tab.Vector(o) - x += flatbuffers.number_types.UOffsetTFlags.py_type(j) * 4 - x = self._tab.Indirect(x) - from .Column import Column - obj = Column() - obj.Init(self._tab.Bytes, x) - return obj - return None - - # Matrix - def ColumnsLength(self): - o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(8)) - if o != 0: - return self._tab.VectorLen(o) - return 0 - - # Matrix - def ColIndexType(self): - o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(10)) - if o != 0: - return self._tab.Get(flatbuffers.number_types.Uint8Flags, o + self._tab.Pos) - return 0 - - # Matrix - def ColIndex(self): - o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(12)) - if o != 0: - from flatbuffers.table import Table - obj = Table(bytearray(), 0) - self._tab.Union(obj, o) - return obj - return None - - # Matrix - def RowIndexType(self): - o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(14)) - if o != 0: - return self._tab.Get(flatbuffers.number_types.Uint8Flags, o + self._tab.Pos) - return 0 - - # Matrix - def RowIndex(self): - o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(16)) - if o != 0: - from flatbuffers.table import Table - obj = Table(bytearray(), 0) - self._tab.Union(obj, o) - return obj - return None - -def MatrixStart(builder): builder.StartObject(7) -def MatrixAddNRows(builder, nRows): builder.PrependUint32Slot(0, nRows, 0) -def MatrixAddNCols(builder, nCols): builder.PrependUint32Slot(1, nCols, 0) -def MatrixAddColumns(builder, columns): builder.PrependUOffsetTRelativeSlot(2, flatbuffers.number_types.UOffsetTFlags.py_type(columns), 0) -def MatrixStartColumnsVector(builder, numElems): return builder.StartVector(4, numElems, 4) -def MatrixAddColIndexType(builder, colIndexType): builder.PrependUint8Slot(3, colIndexType, 0) -def MatrixAddColIndex(builder, colIndex): builder.PrependUOffsetTRelativeSlot(4, flatbuffers.number_types.UOffsetTFlags.py_type(colIndex), 0) -def MatrixAddRowIndexType(builder, rowIndexType): builder.PrependUint8Slot(5, rowIndexType, 0) -def MatrixAddRowIndex(builder, rowIndex): builder.PrependUOffsetTRelativeSlot(6, flatbuffers.number_types.UOffsetTFlags.py_type(rowIndex), 0) -def MatrixEnd(builder): return builder.EndObject() diff --git a/server/app/util/fbs/NetEncoding/TypedArray.py b/server/app/util/fbs/NetEncoding/TypedArray.py deleted file mode 100644 index e36c4f1b..00000000 --- a/server/app/util/fbs/NetEncoding/TypedArray.py +++ /dev/null @@ -1,12 +0,0 @@ -# automatically generated by the FlatBuffers compiler, do not modify - -# namespace: NetEncoding - -class TypedArray(object): - NONE = 0 - Float32Array = 1 - Int32Array = 2 - Uint32Array = 3 - Float64Array = 4 - JSONEncodedArray = 5 - diff --git a/server/app/util/fbs/NetEncoding/Uint32Array.py b/server/app/util/fbs/NetEncoding/Uint32Array.py deleted file mode 100644 index 01c7dfdd..00000000 --- a/server/app/util/fbs/NetEncoding/Uint32Array.py +++ /dev/null @@ -1,46 +0,0 @@ -# automatically generated by the FlatBuffers compiler, do not modify - -# namespace: NetEncoding - -import flatbuffers - -class Uint32Array(object): - __slots__ = ['_tab'] - - @classmethod - def GetRootAsUint32Array(cls, buf, offset): - n = flatbuffers.encode.Get(flatbuffers.packer.uoffset, buf, offset) - x = Uint32Array() - x.Init(buf, n + offset) - return x - - # Uint32Array - def Init(self, buf, pos): - self._tab = flatbuffers.table.Table(buf, pos) - - # Uint32Array - def Data(self, j): - o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4)) - if o != 0: - a = self._tab.Vector(o) - return self._tab.Get(flatbuffers.number_types.Uint32Flags, a + flatbuffers.number_types.UOffsetTFlags.py_type(j * 4)) - return 0 - - # Uint32Array - def DataAsNumpy(self): - o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4)) - if o != 0: - return self._tab.GetVectorAsNumpy(flatbuffers.number_types.Uint32Flags, o) - return 0 - - # Uint32Array - def DataLength(self): - o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4)) - if o != 0: - return self._tab.VectorLen(o) - return 0 - -def Uint32ArrayStart(builder): builder.StartObject(1) -def Uint32ArrayAddData(builder, data): builder.PrependUOffsetTRelativeSlot(0, flatbuffers.number_types.UOffsetTFlags.py_type(data), 0) -def Uint32ArrayStartDataVector(builder, numElems): return builder.StartVector(4, numElems, 4) -def Uint32ArrayEnd(builder): return builder.EndObject() diff --git a/server/app/util/fbs/NetEncoding/__init__.py b/server/app/util/fbs/NetEncoding/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/server/app/util/fbs/__init__.py b/server/app/util/fbs/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/server/app/util/fbs/matrix.py b/server/app/util/fbs/matrix.py deleted file mode 100644 index efa9ecb0..00000000 --- a/server/app/util/fbs/matrix.py +++ /dev/null @@ -1,207 +0,0 @@ -import flatbuffers -import numpy as np -from scipy import sparse -import pandas as pd - -import server.app.util.fbs.NetEncoding.Column as Column -import server.app.util.fbs.NetEncoding.TypedArray as TypedArray -import server.app.util.fbs.NetEncoding.Matrix as Matrix - - -# Placeholder until recent enhancements to flatbuffers Python -# runtime are released, at which point we can use the default -# version. This code is a port of the head. See: -# -# https://github.com/google/flatbuffers/pull/4829 -# -def CreateNumpyVector(builder, x): - """CreateNumpyVector writes a numpy array into the buffer.""" - - if not isinstance(x, np.ndarray): - raise TypeError("non-numpy-ndarray passed to CreateNumpyVector") - - if x.dtype.kind not in ['b', 'i', 'u', 'f']: - raise TypeError("numpy-ndarray holds elements of unsupported datatype") - - if x.ndim > 1: - raise TypeError("multidimensional-ndarray passed to CreateNumpyVector") - - builder.StartVector(x.itemsize, x.size, x.dtype.alignment) - - # Ensure little endian byte ordering - if x.dtype.str[0] == "<": - x_little_endian = x - else: - x_little_endian = x.byteswap(inplace=False) - - # Calculate total length - len = int(x_little_endian.itemsize * x_little_endian.size) - builder.head = int(builder.Head() - len) - - # tobytes ensures c_contiguous ordering - builder.Bytes[builder.Head():builder.Head() + len] = x_little_endian.tobytes(order='C') - - return builder.EndVector(x.size) - - -# Serialization helper -def serialize_column(builder, typed_arr): - """ Serialize NetEncoding.Column """ - (u_type, u_value) = typed_arr - Column.ColumnStart(builder) - Column.ColumnAddUType(builder, u_type) - Column.ColumnAddU(builder, u_value) - return Column.ColumnEnd(builder) - - -# Serialization helper -def serialize_matrix(builder, n_rows, n_cols, columns, col_idx): - """ Serialize NetEncoding.Matrix """ - Matrix.MatrixStart(builder) - Matrix.MatrixAddNRows(builder, n_rows) - Matrix.MatrixAddNCols(builder, n_cols) - Matrix.MatrixAddColumns(builder, columns) - if col_idx is not None: - (u_type, u_val) = col_idx - Matrix.MatrixAddColIndexType(builder, u_type) - Matrix.MatrixAddColIndex(builder, u_val) - return Matrix.MatrixEnd(builder) - - -# Serialization helper -def serialize_typed_array(builder, source_array, encoding_info): - """ - Serialize any of the various typed arrays, eg, Float32Array. Specific - means of serialization and type conversion are provided by type_info. - """ - arr = source_array - (array_type, as_type) = encoding_info(source_array) - - if isinstance(arr, pd.Index): - arr = arr.to_series() - - # convert to a simple ndarray - if as_type == 'json': - as_json = arr.to_json(orient='records') - arr = np.array(bytearray(as_json, 'utf-8')) - else: - if sparse.issparse(arr): - arr = arr.toarray() - elif isinstance(arr, pd.Series): - arr = arr.get_values() - if arr.dtype != as_type: - arr = arr.astype(as_type) - - # serialize the ndarray into a vector - if arr.ndim == 2 and arr.shape[0] == 1: - arr = arr[0] - vec = CreateNumpyVector(builder, arr) - - # serialize the typed array table - builder.StartObject(1) - builder.PrependUOffsetTRelativeSlot(0, vec, 0) - array_value = builder.EndObject() - return (array_type, array_value) - - -def column_encoding(arr): - type_map = { - # dtype: ( array_type, as_type ) - np.float64: (TypedArray.TypedArray.Float32Array, np.float32), - np.float32: (TypedArray.TypedArray.Float32Array, np.float32), - np.float16: (TypedArray.TypedArray.Float32Array, np.float32), - - np.int8: (TypedArray.TypedArray.Int32Array, np.int32), - np.int16: (TypedArray.TypedArray.Int32Array, np.int32), - np.int32: (TypedArray.TypedArray.Int32Array, np.int32), - np.int64: (TypedArray.TypedArray.Int32Array, np.int32), - - np.uint8: (TypedArray.TypedArray.Uint32Array, np.uint32), - np.uint16: (TypedArray.TypedArray.Uint32Array, np.uint32), - np.uint32: (TypedArray.TypedArray.Uint32Array, np.uint32), - np.uint64: (TypedArray.TypedArray.Uint32Array, np.uint32) - } - type_map_default = (TypedArray.TypedArray.JSONEncodedArray, 'json') - return type_map.get(arr.dtype.type, type_map_default) - - -def index_encoding(arr): - type_map = { - # dtype: ( array_type, as_type ) - np.int32: (TypedArray.TypedArray.Int32Array, np.int32), - np.int64: (TypedArray.TypedArray.Int32Array, np.int32), - - np.uint32: (TypedArray.TypedArray.Uint32Array, np.uint32), - np.uint64: (TypedArray.TypedArray.Uint32Array, np.uint32) - } - type_map_default = (TypedArray.TypedArray.JSONEncodedArray, 'json') - return type_map.get(arr.dtype.type, type_map_default) - - -def guess_at_mem_needed(matrix): - (n_rows, n_cols) = matrix.shape - if isinstance(matrix, np.ndarray) or sparse.issparse(matrix): - guess = (n_rows * n_cols * matrix.dtype.itemsize) + 1024 - elif isinstance(matrix, pd.DataFrame): - # XXX TODO - DataFrame type estimate - guess = 1 - else: - guess = 1 - - # round up to nearest 1024 bytes - guess = (guess + 0x400) & (~0x3ff) - return guess - - -def encode_matrix_fbs(matrix, row_idx=None, col_idx=None): - """ - Given a 2D DataFrame, ndarray or sparse equivalent, create and return a - Matrix flatbuffer. - - :param matrix: 2D DataFrame, ndarray or sparse equivalent - :param row_idx: index for row dimension, Index or ndarray - :param col_idx: index for col dimension, Index or ndarray - - NOTE: row indices are (currently) unsupported and must be None - """ - - if row_idx is not None: - raise ValueError("row indexing not supported for FBS Matrix") - if matrix.ndim != 2: - raise ValueError("FBS Matrix must be 2D") - - (n_rows, n_cols) = matrix.shape - - # estimate size needed, so we don't unnecessarily realloc. - builder = flatbuffers.Builder(guess_at_mem_needed(matrix)) - - if isinstance(matrix, pd.DataFrame): - matrix_columns = reversed(tuple(matrix[name] for name in matrix)) - else: - matrix_columns = reversed(tuple(c for c in matrix.T)) - - columns = [] - # for idx in reversed(np.arange(n_cols)): - for c in matrix_columns: - # serialize the typed array - typed_arr = serialize_typed_array(builder, c, column_encoding) - - # serialize the Column union - columns.append(serialize_column(builder, typed_arr)) - - # Serialize Matrix.columns[] - Matrix.MatrixStartColumnsVector(builder, n_cols) - for c in columns: - builder.PrependUOffsetTRelative(c) - matrix_column_vec = builder.EndVector(n_cols) - - # serialize the colIndex if provided - cidx = None - if col_idx is not None: - cidx = serialize_typed_array(builder, col_idx, index_encoding) - - # Serialize Matrix - matrix = serialize_matrix(builder, n_rows, n_cols, matrix_column_vec, cidx) - - builder.Finish(matrix) - return builder.Output() diff --git a/server/app/util/utils.py b/server/app/util/utils.py deleted file mode 100644 index 4bf57612..00000000 --- a/server/app/util/utils.py +++ /dev/null @@ -1,43 +0,0 @@ -from functools import wraps - -from flask import json -from numpy import float32, integer - -from server.app.util.errors import DriverError - - -class Float32JSONEncoder(json.JSONEncoder): - def __init__(self, *args, **kwargs): - """ - NaN/Infinities are illegal in standard JSON. Python extends JSON with - non-standard symbols that most JavaScript JSON parsers do not understand. - The `allow_nan` parameter will force Python simplejson to throw an ValueError - if it runs into non-finite floating point values which are unsupported by - standard JSON. - """ - kwargs["allow_nan"] = False - super().__init__(*args, **kwargs) - - def default(self, obj): - if isinstance(obj, float32): - return float(obj) - elif isinstance(obj, integer): - return int(obj) - return json.JSONEncoder.default(self, obj) - - -def custom_format_warning(msg, *args, **kwargs): - return f"[cellxgene] Warning: {msg} \n" - - -def jsonify_scanpy(data): - return json.dumps(data, cls=Float32JSONEncoder, allow_nan=False) - - -def requires_data(func): - @wraps(func) - def wrapped_function(self, *args, **kwargs): - if self.data is None: - raise DriverError(f"error data must be loaded before you call {func.__name__}") - return func(self, *args, **kwargs) - return wrapped_function diff --git a/server/app/web/__init__.py b/server/app/web/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/server/app/web/webapp.py b/server/app/web/webapp.py deleted file mode 100644 index 8af94640..00000000 --- a/server/app/web/webapp.py +++ /dev/null @@ -1,17 +0,0 @@ -import os -from flask import Blueprint, render_template, send_from_directory, current_app - - -bp = Blueprint("webapp", __name__, template_folder="templates") - - -@bp.route("/") -def index(): - dataset_title = current_app.config["DATASET_TITLE"] - scripts = current_app.config["SCRIPTS"] - return render_template("index.html", datasetTitle=dataset_title, SCRIPTS=scripts) - - -@bp.route("/favicon.png") -def favicon(): - return send_from_directory(os.path.join(bp.root_path, "static/img/"), "favicon.png") diff --git a/server/cli/__init__.py b/server/cli/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/server/cli/cli.py b/server/cli/cli.py deleted file mode 100644 index fbbf06b0..00000000 --- a/server/cli/cli.py +++ /dev/null @@ -1,14 +0,0 @@ -import click - -from .launch import launch -from .prepare import prepare - - -@click.group(name="cellxgene", context_settings=dict(max_content_width=85)) -@click.version_option(version="0.10.1", prog_name="cellxgene", message="[%(prog)s] Version %(version)s") -def cli(): - pass - - -cli.add_command(launch) -cli.add_command(prepare) diff --git a/server/cli/launch.py b/server/cli/launch.py deleted file mode 100644 index cf606364..00000000 --- a/server/cli/launch.py +++ /dev/null @@ -1,213 +0,0 @@ -import errno -import logging -from os import devnull -from os.path import splitext, basename, getsize -import sys -import warnings -import webbrowser - -import click - -from server.app.app import Server -from server.app.util.errors import ScanpyFileError -from server.app.util.utils import custom_format_warning -from server.utils.utils import find_available_port, is_port_available - - -# anything bigger than this will generate a special message -BIG_FILE_SIZE_THRESHOLD = 100 * 2**20 # 100MB - - -@click.command() -@click.argument("data", metavar="", type=click.Path(exists=True, file_okay=True, dir_okay=False)) -@click.option( - "--layout", - "-l", - default=[], - multiple=True, - show_default=True, - help="Layout name, eg, 'umap'." -) -@click.option( - "--diffexp", - "-d", - type=click.Choice(["ttest"]), - default="ttest", - show_default=True, - help="Method for differential expression.", -) -@click.option("--title", "-t", help="Title to display (if omitted will use file name).", metavar="") -@click.option( - "--verbose", - "-v", - is_flag=True, - default=False, - show_default=True, - help="Provide verbose output, including warnings and all server requests.", -) -@click.option("--debug", is_flag=True, default=False, show_default=True, help="Run in debug mode.") -@click.option( - "--open", - "-o", - "open_browser", - is_flag=True, - default=False, - show_default=True, - help="Open the web browser after launch.", -) -@click.option("--port", "-p", help="Port to run server on, if not specified cellxgene will find an available port.", - metavar="", show_default=True) -@click.option("--obs-names", default=None, metavar="", help="Name of annotation field to use for observations.") -@click.option("--var-names", default=None, metavar="", help="Name of annotation to use for variables.") -@click.option("--host", default="127.0.0.1", help="Host IP address") -@click.option( - "--max-category-items", - default=1000, - metavar="", - show_default=True, - help="Categories with more distinct values than this will not be displayed.", -) -@click.option( - "--diffexp-lfc-cutoff", - default=0.01, - show_default=True, - help="Relative expression cutoff used when selecting top N differentially expressed genes", -) -@click.option( - "--scripts", - default=[], - multiple=True, - help="Additional script files to include in html page", - show_default=True, -) -def launch( - data, - layout, - diffexp, - title, - verbose, - debug, - obs_names, - var_names, - open_browser, - port, - host, - max_category_items, - diffexp_lfc_cutoff, - scripts, -): - """Launch the cellxgene data viewer. - This web app lets you explore single-cell expression data. - Data must be in a format that cellxgene expects, read the - "getting started" guide. - - Examples: - - > cellxgene launch example_dataset/pbmc3k.h5ad --title pbmc3k - - > cellxgene launch --title """ - - # Startup message - click.echo("[cellxgene] Starting the CLI...") - - # Argument checking - name, extension = splitext(data) - if extension != ".h5ad": - raise click.FileError(basename(data), hint="file type must be .h5ad") - - if debug: - verbose = True - open_browser = False - else: - warnings.formatwarning = custom_format_warning - - if scripts: - click.echo(r""" -/ / /\ \ \__ _ _ __ _ __ (_)_ __ __ _ -\ \/ \/ / _` | '__| '_ \| | '_ \ / _` | - \ /\ / (_| | | | | | | | | | | (_| | - \/ \/ \__,_|_| |_| |_|_|_| |_|\__, | - |___/ -The --scripts flag is intended for developers to include google analytics etc. You could be opening yourself to a -security risk by including the --scripts flag. Make sure you trust the scripts that you are including. - """) - scripts_pretty = ", ".join(scripts) - click.confirm(f"Are you sure you want to inject these scripts: {scripts_pretty}?", abort=True) - - if not verbose: - sys.tracebacklimit = 0 - - if not title: - file_parts = splitext(basename(data)) - title = file_parts[0] - - if port: - if debug: - raise click.ClickException("--port and --debug may not be used together (try --verbose for error logging).") - if not is_port_available(host, int(port)): - raise click.ClickException( - f"The port selected {port} is in use, please specify an open port using the --port flag." - ) - else: - port = find_available_port(host) - - # Setup app - cellxgene_url = f"http://{host}:{port}" - - # Import Flask app - server = Server() - - server.create_app() - server.app.config.update(SCRIPTS=scripts) - - if not verbose: - log = logging.getLogger("werkzeug") - log.setLevel(logging.ERROR) - - file_size = getsize(data) - - # if a big file, let the user know it may take a while to load. - if file_size > BIG_FILE_SIZE_THRESHOLD: - click.echo(f"[cellxgene] Loading data from {basename(data)}, this may take awhile...") - else: - click.echo(f"[cellxgene] Loading data from {basename(data)}.") - - # Fix for anaconda python. matplotlib typically expects python to be installed as a framework TKAgg is usually - # available and fixes this issue. See https://matplotlib.org/faq/virtualenv_faq.html - import matplotlib as mpl - - mpl.use("TkAgg") - from server.app.scanpy_engine.scanpy_engine import ScanpyEngine - - args = { - "layout": layout, - "diffexp": diffexp, - "max_category_items": max_category_items, - "diffexp_lfc_cutoff": diffexp_lfc_cutoff, - "obs_names": obs_names, - "var_names": var_names, - } - - try: - server.attach_data(ScanpyEngine(data, args), title=title) - except ScanpyFileError as e: - raise click.ClickException(f"{e}") - - if open_browser: - click.echo(f"[cellxgene] Launching! Opening your browser to {cellxgene_url} now.") - webbrowser.open(cellxgene_url) - else: - click.echo(f"[cellxgene] Launching! Please go to {cellxgene_url} in your browser.") - - click.echo("[cellxgene] Type CTRL-C at any time to exit.") - - if not verbose: - f = open(devnull, "w") - sys.stdout = f - - try: - server.app.run(host=host, debug=debug, port=port, threaded=True, use_debugger=False) - except OSError as e: - if e.errno == errno.EADDRINUSE: - raise click.ClickException("Port is in use, please specify an open port using the --port flag.") from e - raise diff --git a/server/cli/prepare.py b/server/cli/prepare.py deleted file mode 100644 index bad6b18c..00000000 --- a/server/cli/prepare.py +++ /dev/null @@ -1,214 +0,0 @@ -from os.path import expanduser, isdir, isfile, sep, splitext - -import click -from numpy import ndarray, unique -from scipy.sparse.csc import csc_matrix - - -@click.command() -@click.argument("data", nargs=1, metavar="", required=True) -@click.option( - "--layout", - "-l", - default=["umap", "tsne"], - multiple=True, - type=click.Choice(["umap", "tsne"]), - help="Layout algorithm", - show_default=True, -) -@click.option( - "--recipe", - "-r", - default="none", - type=click.Choice(["none", "seurat", "zheng17"]), - help="Preprocessing to run.", - show_default=True, -) -@click.option("--output", "-o", default="", help="Save a new file to filename.", metavar="") -@click.option("--plotting", "-p", default=False, is_flag=True, help="Whether to generate plots.", show_default=True) -@click.option("--sparse", default=False, is_flag=True, help="Whether to force sparsity.", show_default=True) -@click.option("--overwrite", default=False, is_flag=True, help="Allow file overwriting.", show_default=True) -@click.option("--set-obs-names", default="", help="Named field to set as index for obs.", metavar="") -@click.option("--set-var-names", default="", help="Named field to set as index for var.", metavar="") -@click.option( - "--run-qc/--skip-qc", default=True, is_flag=True, - help="Whether to calculate QC metrics (saved to adata.obs and adata.var). \ - See scanpy.pp.calculate_qc_metrics for details.", show_default=True) -@click.option( - "--make-obs-names-unique", default=True, is_flag=True, help="Ensure obs index is unique.", show_default=True -) -@click.option( - "--make-var-names-unique", default=True, is_flag=True, help="Ensure var index is unique.", show_default=True -) -def prepare( - data, - layout, - recipe, - output, - plotting, - sparse, - overwrite, - set_obs_names, - set_var_names, - run_qc, - make_obs_names_unique, - make_var_names_unique, -): - """Preprocesses data for use with cellxgene. - - This tool runs a series of scanpy routines for preparing a dataset - for use with cellxgene. It loads data from different formats - (h5ad, loom, or a 10x directory), runs dimensionality reduction, - computes nearest neighbors, computes a layout, performs clustering, - and saves the results. Includes additional options for naming - annotations, ensuring sparsity, and plotting results.""" - - # collect slow imports here to make CLI startup more responsive - click.echo("[cellxgene] Starting CLI...") - import matplotlib - - matplotlib.use("Agg") - import scanpy as sc - - # scanpy settings - sc.settings.verbosity = 0 - sc.settings.autosave = True - - # check args - if sparse and not recipe == "none": - raise click.UsageError("Cannot use a recipe when forcing sparsity") - - output = expanduser(output) - - if not output: - click.echo( - "Warning: No file will be saved, to save the results of cellxgene prepare include " - "--output to save output to a new file" - ) - if isfile(output) and not overwrite: - raise click.UsageError(f"Cannot overwrite existing file {output}, try using the flag --overwrite") - - def load_data(data): - if isfile(data): - name, extension = splitext(data) - if extension == ".h5ad": - adata = sc.read_h5ad(data) - elif extension == ".loom": - adata = sc.read_loom(data) - else: - raise click.FileError(data, hint="does not have a valid extension [.h5ad | .loom]") - elif isdir(data): - if not data.endswith(sep): - data += sep - adata = sc.read_10x_mtx(data) - else: - raise click.FileError(data, hint="not a valid file or path") - - if not set_obs_names == "": - if set_obs_names not in adata.obs_keys(): - raise click.UsageError(f"obs {set_obs_names} not found, options are: {adata.obs_keys()}") - adata.obs_names = adata.obs[set_obs_names] - if not set_var_names == "": - if set_var_names not in adata.var_keys(): - raise click.UsageError(f"var {set_var_names} not found, options are: {adata.var_keys()}") - adata.var_names = adata.var[set_var_names] - if make_obs_names_unique: - adata.obs_names_make_unique() - if make_var_names_unique: - adata.var_names_make_unique() - if not adata._obs.index.is_unique: - click.echo("Warning: obs index is not unique") - if not adata._var.index.is_unique: - click.echo("Warning: var index is not unique") - return adata - - def calculate_qc_metrics(adata): - if run_qc: - sc.pp.calculate_qc_metrics(adata, inplace=True) - return adata - - def make_sparse(adata): - if (type(adata.X) is ndarray) and sparse: - adata.X = csc_matrix(adata.X) - - def run_recipe(adata): - if recipe == "seurat": - sc.pp.recipe_seurat(adata) - elif recipe == "zheng17": - sc.pp.recipe_zheng17(adata) - else: - sc.pp.filter_cells(adata, min_genes=5) - sc.pp.filter_genes(adata, min_cells=25) - if sparse: - sc.pp.scale(adata, zero_center=False) - else: - sc.pp.scale(adata) - - def run_pca(adata): - if sparse: - sc.pp.pca(adata, svd_solver="arpack", zero_center=False) - else: - sc.pp.pca(adata, svd_solver="arpack") - - def run_neighbors(adata): - sc.pp.neighbors(adata) - - def run_louvain(adata): - try: - sc.tl.louvain(adata) - except ModuleNotFoundError: - click.echo( - "\nWarning: louvain module is not installed, no clusters will be calculated. " - "To fix this please install cellxgene with the optional feature louvain enabled: " - "`pip install cellxgene[louvain]`" - ) - - def run_layout(adata): - if len(unique(adata.obs["louvain"].values)) < 10: - palette = "tab10" - else: - palette = "tab20" - - if "umap" in layout: - sc.tl.umap(adata) - if plotting: - sc.pl.umap(adata, color="louvain", palette=palette, save="_louvain") - - if "tsne" in layout: - sc.tl.tsne(adata) - if plotting: - sc.pl.tsne(adata, color="louvain", palette=palette, save="_louvain") - - def show_step(item): - if run_qc: - qc_name = "Calculating QC metrics" - else: - qc_name = "Skipping QC" - names = { - "calculate_qc_metrics": qc_name, - "make_sparse": "Ensuring sparsity", - "run_recipe": f'Running preprocessing recipe "{recipe}"', - "run_pca": "Running PCA", - "run_neighbors": "Calculating neighbors", - "run_louvain": "Calculating clusters", - "run_layout": "Computing layout", - } - if item is not None: - return names[item.__name__] - - steps = [calculate_qc_metrics, make_sparse, run_recipe, run_pca, run_neighbors, run_louvain, run_layout] - - click.echo(f"[cellxgene] Loading data from {data}, please wait...") - adata = load_data(data) - - click.echo("[cellxgene] Beginning preprocessing...") - with click.progressbar(steps, label="[cellxgene] Progress", show_eta=False, item_show_func=show_step) as bar: - for step in bar: - step(adata) - - # saving - if not output == "": - click.echo(f"[cellxgene] Saving results to {output}...") - adata.write(output) - - click.echo("[cellxgene] Success!") diff --git a/server/gui/__init__.py b/server/gui/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/server/gui/browser.py b/server/gui/browser.py deleted file mode 100644 index d09bc621..00000000 --- a/server/gui/browser.py +++ /dev/null @@ -1,93 +0,0 @@ -# flake8: noqa F403, F405 -from cefpython3 import cefpython as cef -from PySide2.QtCore import * -from PySide2.QtGui import * -from PySide2.QtWidgets import * - -from server.gui.utils import WINDOWS, LINUX - -WindowUtils = cef.WindowUtils() - -# OS differences -# noinspection PyUnresolvedReferences -CefWidgetParent = QWidget - -class CefWidget(CefWidgetParent): - def __init__(self, parent=None): - super(CefWidget, self).__init__(parent) - self.parent = parent - self.browser = None - # TODO test without this on linux - self.hidden_window = None # Required for PyQt5 on Linux - self.show() - - def focusInEvent(self, event): - # This event seems to never get called on Linux, as CEF is - # stealing all focus due to Issue #284. - if self.browser: - if WINDOWS: - WindowUtils.OnSetFocus(self.getHandle(), 0, 0, 0) - self.browser.SetFocus(True) - - def focusOutEvent(self, event): - # This event seems to never get called on Linux, as CEF is - # stealing all focus due to Issue #284. - if self.browser: - self.browser.SetFocus(False) - - def embedBrowser(self): - if LINUX: - self.hidden_window = QWindow() - window_info = cef.WindowInfo() - rect = [0, 0, self.width(), self.height()] - window_info.SetAsChild(self.getHandle(), rect) - # TODO better splash - self.browser = cef.CreateBrowserSync(window_info) - - def getHandle(self): - if self.hidden_window: - # PyQt5 on Linux - return int(self.hidden_window.winId()) - else: - return int(self.winId()) - - def moveEvent(self, _): - self.x = 0 - self.y = 0 - if self.browser: - if WINDOWS: - WindowUtils.OnSize(self.getHandle(), 0, 0, 0) - elif LINUX: - self.browser.SetBounds(self.x, self.y, - self.width(), self.height()) - self.browser.NotifyMoveOrResizeStarted() - - def resizeEvent(self, event): - size = event.size() - if self.browser: - if WINDOWS: - WindowUtils.OnSize(self.getHandle(), 0, 0, 0) - elif LINUX: - self.browser.SetBounds(self.x, self.y, - size.width(), size.height()) - self.browser.NotifyMoveOrResizeStarted() - - -class CefApplication(QApplication): - def __init__(self, args): - super(CefApplication, self).__init__(args) - if not cef.GetAppSetting("external_message_pump"): - self.timer = self.createTimer() - - def createTimer(self): - timer = QTimer() - timer.timeout.connect(self.onTimer) - timer.start(10) - return timer - - def onTimer(self): - cef.MessageLoopWork() - - def stopTimer(self): - # Stop the timer after Qt's message loop has ended - self.timer.stop() diff --git a/server/gui/main.py b/server/gui/main.py deleted file mode 100644 index e1258d70..00000000 --- a/server/gui/main.py +++ /dev/null @@ -1,279 +0,0 @@ -# flake8: noqa F403, F405 -from functools import partialmethod -from multiprocessing import Pipe, Process -from os import environ -from os.path import splitext, basename, dirname, join -import sys -import threading - -from cefpython3 import cefpython as cef -import PySide2 -from PySide2.QtCore import * -from PySide2.QtWidgets import * - -from server.gui.browser import CefWidget, CefApplication -from server.gui.workers import Worker, SiteReadyWorker -from server.gui.utils import WINDOWS, LINUX, MAC, FileLoadSignals, Emitter, WorkerSignals -from server.utils.constants import MODES -from server.utils.utils import find_available_port - -if WINDOWS or LINUX: - dirname = dirname(PySide2.__file__) - plugin_path = join(dirname, 'plugins', 'platforms') - environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = plugin_path - -# Configuration -# TODO remember this or calculate it? -WIDTH = 1024 -HEIGHT = 768 -GUI_PORT = find_available_port("localhost") -BROWSER_INDEX = 0 -LOAD_INDEX = 1 - -class MainWindow(QMainWindow): - def __init__(self): - super(MainWindow, self).__init__(None) - self.cef_widget = None - self.data_widget = None - self.parent_conn, self.child_conn = None, None - self.load_emitter = None - self.emitter_thread = None - self.worker = None - self.url = f"http://localhost:{GUI_PORT}/" - self.setWindowTitle("cellxgene") - - # Strong focus - accepts focus by tab & click - self.setFocusPolicy(Qt.StrongFocus) - self.setupLayout() - self.setupMenu() - - def showBrowser(self): - self.stacked_layout.setCurrentIndex(BROWSER_INDEX) - def restartOnError(self): - self.window().shutdownServer() - # close emitter on error/finished - self.parent_conn, self.child_conn = Pipe() - self.load_emitter = Emitter(self.parent_conn, WorkerSignals) - self.emitter_thread = threading.Thread(target=self.load_emitter.run, daemon=True) - self.emitter_thread.start() - # send to load with error message? - - def setupLayout(self): - self.resize(WIDTH, HEIGHT) - self.cef_widget = CefWidget(self) - self.cef_widget.setSizePolicy(QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)) - self.data_widget = LoadWidget(self) - self.stacked_layout = QStackedLayout() - self.stacked_layout.addWidget(self.cef_widget) - self.stacked_layout.addWidget(self.data_widget) - main_layout = QVBoxLayout() - main_layout.setContentsMargins(0, 0, 0, 0) - main_layout.setSpacing(0) - main_layout.addLayout(self.stacked_layout) - frame = QFrame() - frame.setLayout(main_layout) - self.setCentralWidget(frame) - - if WINDOWS: - # On Windows with PyQt5 main window must be shown first - # before CEF browser is embedded, otherwise window is - # not resized and application hangs during resize. - self.show() - - # Browser can be embedded only after layout was set up - self.cef_widget.embedBrowser() - - if LINUX: - # On Linux with PyQt5 the QX11EmbedContainer widget is - # no longer available. An equivalent in Qt5 is to create - # a hidden window, embed CEF browser in it and then - # create a container for that hidden window and replace - # cef widget in the layout with the container. - self.container = QWidget.createWindowContainer( - self.cef_widget.hidden_window, parent=self) - self.stacked_layout.replaceWidget(self.cef_widget, self.container) - self.stacked_layout.setCurrentIndex(LOAD_INDEX) - - def setupServer(self): - self.shutdownServer() - # close emitter on error/finished - self.parent_conn, self.child_conn = Pipe() - self.load_emitter = Emitter(self.parent_conn, WorkerSignals) - self.emitter_thread = threading.Thread(target=self.load_emitter.run, daemon=True) - self.emitter_thread.start() - # send to load with error message? - - def shutdownServer(self): - if self.worker: - self.worker.terminate() - if self.parent_conn: - self.parent_conn.close() - - def setupMenu(self): - # TODO add communication to subprocess on reload - main_menu = self.menuBar() - file_menu = main_menu.addMenu('File') - load_action = QAction("Load file...", self) - load_action.setStatusTip("Load file") - load_action.setShortcut("Ctrl+O") - load_action.triggered.connect(self.showLoad) - file_menu.addAction(load_action) - - def showLoad(self): - self.stacked_layout.setCurrentIndex(LOAD_INDEX) - - def closeEvent(self, event): - # Close browser (force=True) and free CEF reference - if self.cef_widget.browser: - self.cef_widget.browser.CloseBrowser(True) - self.clearBrowserReferences() - - def clearBrowserReferences(self): - # Clear browser references that you keep anywhere in your - # code. All references must be cleared for CEF to shutdown cleanly. - self.cef_widget.browser = None - - -class LoadWidget(QFrame): - def __init__(self, parent): - super(LoadWidget, self).__init__(parent=parent) - # Init layout - self.MAX_CONTENT_WIDTH = 500 - load_ui_layout = QVBoxLayout() - h_margin = (WIDTH - self.MAX_CONTENT_WIDTH) // 2 - if h_margin < 10: - h_margin = 10 - load_ui_layout.setContentsMargins(h_margin, 20, h_margin, 20) - logo_layout = QHBoxLayout() - logo_layout.setContentsMargins(0, 0, 0, 20) - - load_layout = QGridLayout() - load_layout.setContentsMargins(0, 0, 0, 0) - load_layout.setSpacing(0) - message_layout = QHBoxLayout() - message_layout.setContentsMargins(0, 0, 0, 0) - - self.serverError = False - self.title = "" - self.label = QLabel("cellxgene") - logo_layout.addWidget(self.label) - - # UI section - # TODO add load spinner - # TODO add cancel button to send back to browser (if available) - self.embedding_label = QLabel("embedding: ") - load_layout.addWidget(self.embedding_label, 0, 0) - self.file_label = QLabel("file: ") - load_layout.addWidget(self.file_label, 0, 1) - self.embeddings = QComboBox(self) - self.embeddings.currentIndexChanged.connect(self.updateEmbedding) - self.embeddings.addItems(MODES) - self.embedding_selection = [MODES[0]] - load_layout.addWidget(self.embeddings, 1, 0) - - self.load = QPushButton("Open...") - self.load.clicked.connect(self.onLoad) - load_layout.addWidget(self.load, 1, 1) - - # Error section - self.error_label = QLabel("") - self.error_label.setWordWrap(True) - self.error_label.setFixedWidth(self.MAX_CONTENT_WIDTH) - message_layout.addWidget(self.error_label, alignment=Qt.AlignTop) - - # Layout - for l in [logo_layout, load_layout, message_layout ]: - load_ui_layout.addLayout(l) - - load_ui_layout.setStretch(2, 10) - self.setLayout(load_ui_layout) - - self.signals = FileLoadSignals() - self.signals.selectedFile.connect(self.createScanpyEngine) - - def updateEmbedding(self, idx): - self.embedding_selection = [MODES[idx]] - - def createScanpyEngine(self, file_name): - self.window().setupServer() - worker = Worker(self.window().parent_conn, self.window().child_conn, file_name, self.title, host="127.0.0.1", port=GUI_PORT, - layout=self.embedding_selection) - self.window().load_emitter.signals.ready.connect(self.onDataReady) - self.window().load_emitter.signals.engine_error.connect(self.onServerError) - self.window().load_emitter.signals.server_error.connect(self.onServerError) - # Error is generic error from emitter - self.window().load_emitter.signals.error.connect(self.onServerError) - self.window().worker = Process(target=worker.run, daemon=True) - self.window().worker.start() - self.window().child_conn.close() - - def onLoad(self): - options = QFileDialog.Options() - # options |= QFileDialog.DontUseNativeDialog - file_name, _ = QFileDialog.getOpenFileName(self, - "Open H5AD File", "", "H5AD Files (*.h5ad)", options=options) - self.title = splitext(basename(file_name))[0] - if file_name: - self.signals.selectedFile.emit(file_name) - # Reset error on reload - self.serverError = False - - def onDataReady(self): - self.site_ready_worker = SiteReadyWorker(self.window().url) - self.site_ready_worker.signals.ready.connect(self.onServerReady) - self.site_ready_worker.signals.error.connect(self.onServerError) - - srw_thread = threading.Thread(target=self.site_ready_worker.run, daemon=True) - srw_thread.start() - - def onServerReady(self): - if not self.serverError: - self.window().cef_widget.browser.Navigate(self.window().url) - self.window().showBrowser() - - def onError(self, err, server_error=False): - # Restart worker - if server_error: - self.serverError = True - # Report error and switch to load screen - self.window().shutdownServer() - self.window().stacked_layout.setCurrentIndex(LOAD_INDEX) - self.error_label.setText(f"Error: {err}") - self.error_label.resize(self.MAX_CONTENT_WIDTH, self.error_label.height()) - - onServerError = partialmethod(onError, server_error=True) - - -def main(): - # This generates an error.log file on error - sys.excepthook = cef.ExceptHook # To shutdown all CEF processes on error - settings = {} - # Instead of timer loop - if MAC: - settings["external_message_pump"] = True - - # Create and launch cef browser and qt window - cef.Initialize(settings) - app = CefApplication(sys.argv) - main_window = MainWindow() - main_window.show() - main_window.activateWindow() - main_window.raise_() - try: - app.exec_() - except Exception as e: - raise - finally: - # Clean up on close - if not cef.GetAppSetting("external_message_pump"): - app.stopTimer() - - main_window.shutdownServer() - del main_window # Just to be safe, similarly to "del app" - del app # Must destroy app object before calling Shutdown - cef.Shutdown() - sys.exit(0) - - -if __name__ == '__main__': - main() diff --git a/server/gui/utils.py b/server/gui/utils.py deleted file mode 100644 index 3a76d572..00000000 --- a/server/gui/utils.py +++ /dev/null @@ -1,74 +0,0 @@ -import errno -import platform - -from PySide2.QtCore import QObject, Signal - -# Detect OS -WINDOWS = (platform.system() == "Windows") -LINUX = (platform.system() == "Linux") -MAC = (platform.system() == "Darwin") - - -class WorkerSignals(QObject): - """ - Defines the signals available from a running worker thread. - Supported signals are: - finished - ready - error - `str` error message - result - `object` data returned from processing, anything - """ - finished = Signal() - engine_error = Signal(str) - server_error = Signal(str) - error = Signal(str) - result = Signal(object) - ready = Signal() - - -class SiteReadySignals(QObject): - """ - Defines the signals available from a running worker thread. - Supported signals are: - timeout - ready - error - `str` error message - """ - ready = Signal() - timeout = Signal() - error = Signal(str) - - -class FileLoadSignals(QObject): - selectedFile = Signal(str) - - -class Emitter: - def __init__(self, transport, signals): - self.transport = transport - self.signals = signals() - - def _emit(self, signature, args=None): - if args is None: - getattr(self.signals, signature).emit() - else: - getattr(self.signals, signature).emit(args) - - def run(self): - while True: - try: - signature = self.transport.recv() - except EOFError: - # Server done - break - except OSError as e: - if e.errno == errno.EBADF: - break - else: - self.signals.error.emit(str(e)) - break - except Exception as e: - self.signals.error.emit(str(e)) - break - else: - self._emit(*signature) diff --git a/server/gui/workers.py b/server/gui/workers.py deleted file mode 100644 index 157b72c0..00000000 --- a/server/gui/workers.py +++ /dev/null @@ -1,89 +0,0 @@ -from multiprocessing import Process -import time - -import requests - -from server.gui.utils import SiteReadySignals - - -class EmittingProcess(Process): - def __init__(self, parent_conn, child_conn, *arg, **kwargs): - super(EmittingProcess, self).__init__() - self.parent_conn = parent_conn - self.child_conn = child_conn - - def run(self): - self.parent_conn.close() - - def emit(self, signal_name, *args): - message = (signal_name, *args) - self.child_conn.send(message) - - -class Worker(EmittingProcess): - def __init__(self, parent_conn, child_conn, data_file, title, host, port, layout=["umap"], *args, **kwargs): - super(Worker, self).__init__(parent_conn, child_conn) - self.data_file = data_file - self.layout = layout - self.title = title - self.host = host - self.port = port - - def run(self): - super(Worker, self).run() - if not self.data_file: - self.emit("finished") - return - from server.app.app import Server - from server.app.scanpy_engine.scanpy_engine import ScanpyEngine - # create server - try: - server = Server() - server.create_app() - except Exception as e: - self.emit("server_error", str(e)) - self.emit("finished") - return - # load data - try: - args = { - "layout": self.layout, - "max_category_items": 100, - "diffexp_lfc_cutoff": 0.01, - "obs_names": None, - "var_names": None, - } - data = ScanpyEngine(self.data_file, args) - server.attach_data(data, self.title) - self.emit("ready") - except Exception as e: - self.emit("engine_error", str(e)) - self.emit("finished") - return - # launch server - try: - server.app.run(host=self.host, debug=False, port=self.port, threaded=True) - except Exception as e: - self.emit("server_error", str(e)) - finally: - self.emit("finished") - - -class SiteReadyWorker: - def __init__(self, location): - super(SiteReadyWorker, self).__init__() - self.signals = SiteReadySignals() - self.location = location - - def run(self): - session = requests.Session() - for i in range(90): - try: - session.head(self.location) - self.signals.ready.emit() - break - except requests.exceptions.ConnectionError: - time.sleep(1) - except Exception as e: - self.signals.error.emit(str(e)) - self.signals.timeout.emit() diff --git a/server/requirements-dev.txt b/server/requirements-dev.txt deleted file mode 100644 index 95c0b2b1..00000000 --- a/server/requirements-dev.txt +++ /dev/null @@ -1,6 +0,0 @@ -black -bumpversion>=0.5 -pytest>=3.6.3 -requests>=2.18.4 -twine>=1.12.1 --r requirements.txt diff --git a/server/requirements.txt b/server/requirements.txt deleted file mode 100644 index 18ff0f31..00000000 --- a/server/requirements.txt +++ /dev/null @@ -1,15 +0,0 @@ -anndata>=0.6.20 -click>=6.7 -Flask>=1.0.2 -Flask-Caching>=1.4.0 -Flask-Compress>=1.4.0 -Flask-Cors>=3.0.6 -Flask-RESTful>=0.3.6 -flatbuffers>=1.10.0 -matplotlib>=2.2 -numpy>=1.15.2 -pandas>=0.23.1 -scanpy>=1.3.7 -scipy>=1.1.0 -scikit-learn>=0.19.1,!=0.20.0 -tables==3.5.1 diff --git a/server/test/decode_fbs.py b/server/test/decode_fbs.py deleted file mode 100644 index a9b847ad..00000000 --- a/server/test/decode_fbs.py +++ /dev/null @@ -1,69 +0,0 @@ - -""" -Code to decode, for testing purposes, the flatbuffer encoded blobs. -This code will need to be updated if fbs/matrix.fbs changes. - -For more information, see fbs/matrix.fbs and server/app/util/fbs/ -""" -import json - -import server.app.util.fbs.NetEncoding.TypedArray as TypedArray -import server.app.util.fbs.NetEncoding.Matrix as Matrix -import server.app.util.fbs.NetEncoding.Int32Array as Int32Array -import server.app.util.fbs.NetEncoding.Uint32Array as Uint32Array -import server.app.util.fbs.NetEncoding.Float32Array as Float32Array -import server.app.util.fbs.NetEncoding.Float64Array as Float64Array -import server.app.util.fbs.NetEncoding.JSONEncodedArray as JSONEncodedArray - - -def decode_typed_array(tarr): - type_map = { - TypedArray.TypedArray.Uint32Array: Uint32Array.Uint32Array, - TypedArray.TypedArray.Int32Array: Int32Array.Int32Array, - TypedArray.TypedArray.Float32Array: Float32Array.Float32Array, - TypedArray.TypedArray.Float64Array: Float64Array.Float64Array, - TypedArray.TypedArray.JSONEncodedArray: JSONEncodedArray.JSONEncodedArray - } - (u_type, u) = tarr - if u_type == TypedArray.TypedArray.NONE: - return None - - TarType = type_map.get(u_type, None) - assert(TarType is not None) - - arr = TarType() - arr.Init(u.Bytes, u.Pos) - narr = arr.DataAsNumpy() - if u_type == TypedArray.TypedArray.JSONEncodedArray: - narr = json.loads(narr.tostring().decode('utf-8')) - return narr - - -def decode_matrix_FBS(buf): - """ - Given a FBS Matrix, return an decoded Python dict containing - same info in native format. - - NOTE / TODO: row_idx not currently implemented - """ - df = Matrix.Matrix.GetRootAsMatrix(buf, 0) - n_rows = df.NRows() - n_cols = df.NCols() - - columns_length = df.ColumnsLength() - - decoded_columns = [] - for col_idx in range(0, columns_length): - col = df.Columns(col_idx) - tarr = (col.UType(), col.U()) - decoded_columns.append(decode_typed_array(tarr)) - - cidx = decode_typed_array((df.ColIndexType(), df.ColIndex())) - - return { - "n_rows": n_rows, - "n_cols": n_cols, - "columns": decoded_columns, - "col_idx": cidx, - "row_idx": None - } diff --git a/server/test/schema.json b/server/test/schema.json deleted file mode 100644 index 828920e3..00000000 --- a/server/test/schema.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "dataframe": { - "nObs": 2638, - "nVar": 1838, - "type": "float32" - }, - "annotations": { - "obs": { - "index": "name_0", - "columns": [ - { - "name": "name_0", - "type": "string" - }, - { - "name": "n_genes", - "type": "int32" - }, - { - "name": "percent_mito", - "type": "float32" - }, - { - "name": "n_counts", - "type": "float32" - }, - { - "name": "louvain", - "type": "categorical", - "categories": [ - "CD4 T cells", - "CD14+ Monocytes", - "B cells", - "CD8 T cells", - "NK cells", - "FCGR3A+ Monocytes", - "Dendritic cells", - "Megakaryocytes" - ] - } - ] - }, - "var": { - "index": "name_0", - "columns": [ - { - "name": "name_0", - "type": "string" - }, - { - "name": "n_cells", - "type": "int32" - } - ] - } - }, - "layout": { - "obs": [ - { - "name": "umap", - "type": "float32", - "dims": ["umap_0", "umap_1"] - } - ] - } -} diff --git a/server/test/test_api.py b/server/test/test_api.py deleted file mode 100644 index 7f396431..00000000 --- a/server/test/test_api.py +++ /dev/null @@ -1,268 +0,0 @@ -from http import HTTPStatus -from subprocess import Popen -import unittest -import time - -import requests - -import decode_fbs - -LOCAL_URL = "http://127.0.0.1:5005/" -VERSION = "v0.2" -URL_BASE = f"{LOCAL_URL}api/{VERSION}/" - -BAD_FILTER = {"filter": {"obs": {"annotation_value": [{"name": "xyz"}]}}} - - -class EndPoints(unittest.TestCase): - """Test Case for endpoints""" - - @classmethod - def setUpClass(cls): - cls.ps = Popen(["cellxgene", "launch", "example-dataset/pbmc3k.h5ad", "--verbose", "--port", "5005"]) - session = requests.Session() - for i in range(90): - try: - result = session.get(f"{URL_BASE}schema") - cls.schema = result.json() - except requests.exceptions.ConnectionError: - time.sleep(1) - - @classmethod - def tearDownClass(cls): - try: - cls.ps.terminate() - except ProcessLookupError: - pass - - def setUp(self): - self.session = requests.Session() - - def test_initialize(self): - endpoint = "schema" - url = f"{URL_BASE}{endpoint}" - result = self.session.get(url) - self.assertEqual(result.status_code, HTTPStatus.OK) - self.assertEqual(result.headers["Content-Type"], "application/json") - result_data = result.json() - self.assertEqual(result_data["schema"]["dataframe"]["nObs"], 2638) - self.assertEqual(len(result_data["schema"]["annotations"]["obs"]), 2) - self.assertEqual(len(result_data["schema"]["annotations"]["obs"]["columns"]), 5) - - def test_config(self): - endpoint = "config" - url = f"{URL_BASE}{endpoint}" - result = self.session.get(url) - self.assertEqual(result.status_code, HTTPStatus.OK) - self.assertEqual(result.headers["Content-Type"], "application/json") - result_data = result.json() - self.assertIn("library_versions", result_data["config"]) - self.assertEqual(result_data["config"]["displayNames"]["dataset"], "pbmc3k") - self.assertEqual(len(result_data["config"]["features"]), 4) - - def test_get_layout_fbs(self): - endpoint = "layout/obs" - url = f"{URL_BASE}{endpoint}" - header = {"Accept": "application/octet-stream"} - result = self.session.get(url, headers=header) - self.assertEqual(result.status_code, HTTPStatus.OK) - self.assertEqual(result.headers["Content-Type"], "application/octet-stream") - df = decode_fbs.decode_matrix_FBS(result.content) - self.assertEqual(df['n_rows'], 2638) - self.assertEqual(df['n_cols'], 8) - self.assertIsNotNone(df['columns']) - self.assertListEqual(df['col_idx'], [ - 'pca_0', 'pca_1', 'tsne_0', 'tsne_1', 'umap_0', 'umap_1', 'draw_graph_fr_0', 'draw_graph_fr_1' - ]) - self.assertIsNone(df['row_idx']) - self.assertEqual(len(df['columns']), df['n_cols']) - - def test_bad_filter(self): - endpoint = "data/var" - url = f"{URL_BASE}{endpoint}" - result = self.session.put(url, json=BAD_FILTER) - self.assertEqual(result.status_code, HTTPStatus.BAD_REQUEST) - - def test_get_annotations_obs_fbs(self): - endpoint = "annotations/obs" - url = f"{URL_BASE}{endpoint}" - header = {"Accept": "application/octet-stream"} - result = self.session.get(url, headers=header) - self.assertEqual(result.status_code, HTTPStatus.OK) - self.assertEqual(result.headers["Content-Type"], "application/octet-stream") - df = decode_fbs.decode_matrix_FBS(result.content) - self.assertEqual(df['n_rows'], 2638) - self.assertEqual(df['n_cols'], 5) - self.assertIsNotNone(df['columns']) - self.assertIsNotNone(df['col_idx']) - self.assertIsNone(df['row_idx']) - self.assertEqual(len(df['columns']), df['n_cols']) - obs_index_col_name = self.schema["schema"]["annotations"]["obs"]["index"] - self.assertListEqual(df['col_idx'], [obs_index_col_name, 'n_genes', 'percent_mito', 'n_counts', 'louvain']) - - def test_get_annotations_obs_keys_fbs(self): - endpoint = "annotations/obs" - query = "annotation-name=n_genes&annotation-name=percent_mito" - url = f"{URL_BASE}{endpoint}?{query}" - header = {"Accept": "application/octet-stream"} - result = self.session.get(url, headers=header) - self.assertEqual(result.status_code, HTTPStatus.OK) - self.assertEqual(result.headers["Content-Type"], "application/octet-stream") - df = decode_fbs.decode_matrix_FBS(result.content) - self.assertEqual(df['n_rows'], 2638) - self.assertEqual(df['n_cols'], 2) - self.assertIsNotNone(df['columns']) - self.assertIsNotNone(df['col_idx']) - self.assertIsNone(df['row_idx']) - self.assertEqual(len(df['columns']), df['n_cols']) - self.assertListEqual(df['col_idx'], ['n_genes', 'percent_mito']) - - def test_get_annotations_obs_error(self): - endpoint = "annotations/obs" - query = "annotation-name=notakey" - url = f"{URL_BASE}{endpoint}?{query}" - result = self.session.get(url) - self.assertEqual(result.status_code, HTTPStatus.BAD_REQUEST) - - def test_diff_exp(self): - endpoint = "diffexp/obs" - url = f"{URL_BASE}{endpoint}" - params = { - "mode": "topN", - "set1": {"filter": {"obs": {"annotation_value": [{"name": "louvain", "values": ["NK cells"]}]}}}, - "set2": {"filter": {"obs": {"annotation_value": [{"name": "louvain", "values": ["CD8 T cells"]}]}}}, - "count": 7, - } - result = self.session.post(url, json=params) - self.assertEqual(result.status_code, HTTPStatus.OK) - self.assertEqual(result.headers["Content-Type"], "application/json") - result_data = result.json() - self.assertEqual(len(result_data), 7) - - def test_diff_exp_indices(self): - endpoint = "diffexp/obs" - url = f"{URL_BASE}{endpoint}" - params = { - "mode": "topN", - "count": 10, - "set1": {"filter": {"obs": {"index": [[0, 500]]}}}, - "set2": {"filter": {"obs": {"index": [[500, 1000]]}}}, - } - result = self.session.post(url, json=params) - self.assertEqual(result.status_code, HTTPStatus.OK) - self.assertEqual(result.headers["Content-Type"], "application/json") - result_data = result.json() - self.assertEqual(len(result_data), 10) - - def test_get_annotations_var_fbs(self): - endpoint = "annotations/var" - url = f"{URL_BASE}{endpoint}" - header = {"Accept": "application/octet-stream"} - result = self.session.get(url, headers=header) - self.assertEqual(result.status_code, HTTPStatus.OK) - self.assertEqual(result.headers["Content-Type"], "application/octet-stream") - df = decode_fbs.decode_matrix_FBS(result.content) - self.assertEqual(df['n_rows'], 1838) - self.assertEqual(df['n_cols'], 2) - self.assertIsNotNone(df['columns']) - self.assertIsNotNone(df['col_idx']) - self.assertIsNone(df['row_idx']) - self.assertEqual(len(df['columns']), df['n_cols']) - var_index_col_name = self.schema["schema"]["annotations"]["var"]["index"] - self.assertListEqual(df['col_idx'], [var_index_col_name, 'n_cells']) - - def test_get_annotations_var_keys_fbs(self): - endpoint = "annotations/var" - query = "annotation-name=n_cells" - url = f"{URL_BASE}{endpoint}?{query}" - header = {"Accept": "application/octet-stream"} - result = self.session.get(url, headers=header) - self.assertEqual(result.status_code, HTTPStatus.OK) - self.assertEqual(result.headers["Content-Type"], "application/octet-stream") - df = decode_fbs.decode_matrix_FBS(result.content) - self.assertEqual(df['n_rows'], 1838) - self.assertEqual(df['n_cols'], 1) - self.assertIsNotNone(df['columns']) - self.assertIsNotNone(df['col_idx']) - self.assertIsNone(df['row_idx']) - self.assertEqual(len(df['columns']), df['n_cols']) - self.assertListEqual(df['col_idx'], ['n_cells']) - - def test_get_annotations_var_error(self): - endpoint = "annotations/var" - query = "annotation-name=notakey" - url = f"{URL_BASE}{endpoint}?{query}" - result = self.session.get(url) - self.assertEqual(result.status_code, HTTPStatus.BAD_REQUEST) - - def test_data_mimetype_error(self): - endpoint = f"data/var" - header = {"Accept": "xxx"} - url = f"{URL_BASE}{endpoint}" - result = self.session.put(url, headers=header) - self.assertEqual(result.status_code, HTTPStatus.NOT_ACCEPTABLE) - - def test_fbs_default(self): - endpoint = f"data/var" - url = f"{URL_BASE}{endpoint}" - result = self.session.put(url) - self.assertEqual(result.status_code, HTTPStatus.OK) - self.assertEqual(result.headers["Content-Type"], "application/octet-stream") - - def test_data_put_fbs(self): - endpoint = f"data/var" - url = f"{URL_BASE}{endpoint}" - header = {"Accept": "application/octet-stream"} - result = self.session.put(url, headers=header) - self.assertEqual(result.status_code, HTTPStatus.OK) - self.assertEqual(result.headers["Content-Type"], "application/octet-stream") - df = decode_fbs.decode_matrix_FBS(result.content) - self.assertEqual(df['n_rows'], 2638) - self.assertEqual(df['n_cols'], 1838) - self.assertIsNotNone(df['columns']) - self.assertListEqual(df['col_idx'].tolist(), []) - self.assertIsNone(df['row_idx']) - self.assertEqual(len(df['columns']), df['n_cols']) - - def test_data_put_filter_fbs(self): - endpoint = f"data/var" - url = f"{URL_BASE}{endpoint}" - header = {"Accept": "application/octet-stream"} - filter = { - "filter": { - "var": { - "index": [0, 1, 4] - } - } - } - result = self.session.put(url, headers=header, json=filter) - self.assertEqual(result.status_code, HTTPStatus.OK) - self.assertEqual(result.headers["Content-Type"], "application/octet-stream") - df = decode_fbs.decode_matrix_FBS(result.content) - self.assertEqual(df['n_rows'], 2638) - self.assertEqual(df['n_cols'], 3) - self.assertIsNotNone(df['columns']) - self.assertIsNotNone(df['col_idx']) - self.assertIsNone(df['row_idx']) - self.assertEqual(len(df['columns']), df['n_cols']) - self.assertListEqual(df['col_idx'].tolist(), [0, 1, 4]) - - def test_data_put_single_var(self): - endpoint = f"data/var" - url = f"{URL_BASE}{endpoint}" - header = {"Accept": "application/octet-stream"} - index_col_name = self.schema["schema"]["annotations"]["var"]["index"] - var_filter = {"filter": {"var": {"annotation_value": [{"name": index_col_name, "values": ["RER1"]}]}}} - result = self.session.put(url, headers=header, json=var_filter) - self.assertEqual(result.status_code, HTTPStatus.OK) - self.assertEqual(result.headers["Content-Type"], "application/octet-stream") - df = decode_fbs.decode_matrix_FBS(result.content) - self.assertEqual(df["n_rows"], 2638) - self.assertEqual(df["n_cols"], 1) - - def test_static(self): - endpoint = "static" - file = "js/service-worker.js" - url = f"{LOCAL_URL}{endpoint}/{file}" - result = self.session.get(url) - self.assertEqual(result.status_code, HTTPStatus.OK) diff --git a/server/test/test_datasets/nan.h5ad b/server/test/test_datasets/nan.h5ad deleted file mode 100644 index d97078b0..00000000 Binary files a/server/test/test_datasets/nan.h5ad and /dev/null differ diff --git a/server/test/test_nan_rest.py b/server/test/test_nan_rest.py deleted file mode 100644 index a9ba4dd7..00000000 --- a/server/test/test_nan_rest.py +++ /dev/null @@ -1,74 +0,0 @@ -from http import HTTPStatus -from subprocess import Popen -import unittest -import time -import math - -import decode_fbs - -import requests - -LOCAL_URL = "http://127.0.0.1:5006/" -VERSION = "v0.2" -URL_BASE = f"{LOCAL_URL}api/{VERSION}/" - -BAD_FILTER = {"filter": {"obs": {"annotation_value": [{"name": "xyz"}]}}} - - -class WithNaNs(unittest.TestCase): - """Test Case for endpoints""" - - @classmethod - def setUpClass(cls): - cls.ps = Popen( - ["cellxgene", "launch", "server/test/test_datasets/nan.h5ad", "--verbose", "--port", "5006"] - ) - session = requests.Session() - for i in range(90): - try: - session.get(f"{URL_BASE}schema") - except requests.exceptions.ConnectionError: - time.sleep(1) - - @classmethod - def tearDownClass(cls): - try: - cls.ps.terminate() - except ProcessLookupError: - pass - - def setUp(self): - self.session = requests.Session() - - def test_initialize(self): - endpoint = "schema" - url = f"{URL_BASE}{endpoint}" - result = self.session.get(url) - self.assertEqual(result.status_code, HTTPStatus.OK) - - def test_data(self): - endpoint = "data/var" - url = f"{URL_BASE}{endpoint}" - result = self.session.put(url) - self.assertEqual(result.status_code, HTTPStatus.OK) - self.assertEqual(result.headers["Content-Type"], "application/octet-stream") - df = decode_fbs.decode_matrix_FBS(result.content) - self.assertTrue(math.isnan(df["columns"][3][3])) - - def test_annotation_obs(self): - endpoint = "annotations/obs" - url = f"{URL_BASE}{endpoint}" - result = self.session.get(url) - self.assertEqual(result.status_code, HTTPStatus.OK) - self.assertEqual(result.headers["Content-Type"], "application/octet-stream") - df = decode_fbs.decode_matrix_FBS(result.content) - self.assertTrue(math.isnan(df["columns"][2][0])) - - def test_annotation_var(self): - endpoint = "annotations/var" - url = f"{URL_BASE}{endpoint}" - result = self.session.get(url) - self.assertEqual(result.status_code, HTTPStatus.OK) - self.assertEqual(result.headers["Content-Type"], "application/octet-stream") - df = decode_fbs.decode_matrix_FBS(result.content) - self.assertTrue(math.isnan(df["columns"][2][0])) diff --git a/server/test/test_nan_scanpy_engine.py b/server/test/test_nan_scanpy_engine.py deleted file mode 100644 index ffe62cf6..00000000 --- a/server/test/test_nan_scanpy_engine.py +++ /dev/null @@ -1,73 +0,0 @@ -import pytest -import unittest -import warnings -import math - -import decode_fbs - -from server.app.scanpy_engine.scanpy_engine import ScanpyEngine -from server.app.util.errors import FilterError - - -class NaNTest(unittest.TestCase): - def setUp(self): - self.args = { - "layout": ["umap"], - "diffexp": "ttest", - "max_category_items": 100, - "obs_names": None, - "var_names": None, - "diffexp_lfc_cutoff": 0.01, - } - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=UserWarning) - self.data = ScanpyEngine("server/test/test_datasets/nan.h5ad", self.args) - self.data._create_schema() - - def test_load(self): - with self.assertWarns(UserWarning): - ScanpyEngine("server/test/test_datasets/nan.h5ad", self.args) - - def test_init(self): - self.assertEqual(self.data.cell_count, 100) - self.assertEqual(self.data.gene_count, 100) - epsilon = 0.000_005 - self.assertTrue(self.data.data.X[0, 0] - -0.171_469_51 < epsilon) - - def test_dataframe(self): - data_frame_var = decode_fbs.decode_matrix_FBS(self.data.data_frame_to_fbs_matrix(None, "var")) - self.assertIsNotNone(data_frame_var) - self.assertEqual(data_frame_var["n_rows"], 100) - self.assertEqual(data_frame_var["n_cols"], 100) - self.assertTrue(math.isnan(data_frame_var["columns"][3][3])) - - with pytest.raises(FilterError): - self.data.data_frame_to_fbs_matrix("an erroneous filter", "var") - with pytest.raises(FilterError): - filter_ = { - "filter": { - "obs": {"index": [1, 99, [200, 300]]} - } - } - self.data.data_frame_to_fbs_matrix(filter_["filter"], "var") - - def test_dataframe_obs_not_implemented(self): - with self.assertRaises(ValueError) as cm: - decode_fbs.decode_matrix_FBS(self.data.data_frame_to_fbs_matrix(None, "obs")) - self.assertIsNotNone(cm.exception) - - def test_annotation(self): - annotations = decode_fbs.decode_matrix_FBS(self.data.annotation_to_fbs_matrix("obs")) - obs_index_col_name = self.data.schema["annotations"]["obs"]["index"] - self.assertEqual( - annotations["col_idx"], - [obs_index_col_name, "n_genes", "percent_mito", "n_counts", "louvain"] - ) - self.assertEqual(annotations["n_rows"], 100) - self.assertTrue(math.isnan(annotations["columns"][2][0])) - - annotations = decode_fbs.decode_matrix_FBS(self.data.annotation_to_fbs_matrix("var")) - var_index_col_name = self.data.schema["annotations"]["var"]["index"] - self.assertEqual(annotations["col_idx"], [var_index_col_name, "n_cells", "var_with_nans"]) - self.assertEqual(annotations["n_rows"], 100) - self.assertTrue(math.isnan(annotations["columns"][2][0])) diff --git a/server/test/test_scanpy_engine.py b/server/test/test_scanpy_engine.py deleted file mode 100644 index d793ff69..00000000 --- a/server/test/test_scanpy_engine.py +++ /dev/null @@ -1,195 +0,0 @@ -import json -from os import path -import pytest -import time -import unittest -import decode_fbs - -import numpy as np -from pandas import Series - -from server.app.scanpy_engine.scanpy_engine import ScanpyEngine -from server.app.util.errors import FilterError - - -class EngineTest(unittest.TestCase): - def setUp(self): - args = { - "layout": ["umap"], - "diffexp": "ttest", - "max_category_items": 100, - "obs_names": None, - "var_names": None, - "diffexp_lfc_cutoff": 0.01, - } - self.data = ScanpyEngine("example-dataset/pbmc3k.h5ad", args) - - def test_init(self): - self.assertEqual(self.data.cell_count, 2638) - self.assertEqual(self.data.gene_count, 1838) - epsilon = 0.000_005 - self.assertTrue(self.data.data.X[0, 0] - -0.171_469_51 < epsilon) - - def test_mandatory_annotations(self): - obs_index_col_name = self.data.schema["annotations"]["obs"]["index"] - self.assertIn(obs_index_col_name, self.data.data.obs) - self.assertEqual(list(self.data.data.obs.index), list(range(2638))) - var_index_col_name = self.data.schema["annotations"]["var"]["index"] - self.assertIn(var_index_col_name, self.data.data.var) - self.assertEqual(list(self.data.data.var.index), list(range(1838))) - - @pytest.mark.filterwarnings("ignore:Scanpy data matrix") - def test_data_type(self): - self.data.data.X = self.data.data.X.astype("float64") - with self.assertWarns(UserWarning): - self.data._validate_data_types() - - def test_filter_idx(self): - filter_ = { - "filter": { - "var": {"index": [1, 99, [200, 300]]} - } - } - fbs = self.data.data_frame_to_fbs_matrix(filter_["filter"], "var") - data = decode_fbs.decode_matrix_FBS(fbs) - self.assertEqual(data["n_rows"], 2638) - self.assertEqual(data["n_cols"], 102) - - def test_filter_complex(self): - filter_ = { - "filter": { - "var": { - "annotation_value": [ - {"name": "n_cells", "min": 10} - ], - "index": [1, 99, [200, 300]] - } - } - } - fbs = self.data.data_frame_to_fbs_matrix(filter_["filter"], "var") - data = decode_fbs.decode_matrix_FBS(fbs) - self.assertEqual(data["n_rows"], 2638) - self.assertEqual(data["n_cols"], 91) - - def test_obs_and_var_names(self): - self.assertEqual(np.sum(self.data.data.var[self.data.schema["annotations"]["var"]["index"]].isna()), 0) - self.assertEqual(np.sum(self.data.data.obs[self.data.schema["annotations"]["obs"]["index"]].isna()), 0) - - def test_schema(self): - with open(path.join(path.dirname(__file__), "schema.json")) as fh: - schema = json.load(fh) - self.assertEqual(self.data.schema, schema) - - def test_schema_produces_error(self): - self.data.data.obs["time"] = Series( - list([time.time() for i in range(self.data.cell_count)]), - dtype="datetime64[ns]", - ) - with pytest.raises(TypeError): - self.data._create_schema() - - def test_config(self): - self.assertEqual( - self.data.features["layout"]["obs"], - {"available": True, "interactiveLimit": 50000}, - ) - - def test_layout(self): - fbs = self.data.layout_to_fbs_matrix() - layout = decode_fbs.decode_matrix_FBS(fbs) - self.assertEqual(layout["n_cols"], 2) - self.assertEqual(layout["n_rows"], 2638) - - X = layout["columns"][0] - self.assertTrue((X >= 0).all() and (X <= 1).all()) - Y = layout["columns"][1] - self.assertTrue((Y >= 0).all() and (Y <= 1).all()) - - def test_annotations(self): - fbs = self.data.annotation_to_fbs_matrix("obs") - annotations = decode_fbs.decode_matrix_FBS(fbs) - self.assertEqual(annotations["n_rows"], 2638) - self.assertEqual(annotations["n_cols"], 5) - obs_index_col_name = self.data.schema["annotations"]["obs"]["index"] - self.assertEqual( - annotations["col_idx"], - [obs_index_col_name, "n_genes", "percent_mito", "n_counts", "louvain"], - ) - - fbs = self.data.annotation_to_fbs_matrix("var") - annotations = decode_fbs.decode_matrix_FBS(fbs) - self.assertEqual(annotations['n_rows'], 1838) - self.assertEqual(annotations['n_cols'], 2) - var_index_col_name = self.data.schema["annotations"]["var"]["index"] - self.assertEqual(annotations["col_idx"], [var_index_col_name, "n_cells"]) - - def test_annotation_fields(self): - fbs = self.data.annotation_to_fbs_matrix("obs", ["n_genes", "n_counts"]) - annotations = decode_fbs.decode_matrix_FBS(fbs) - self.assertEqual(annotations["n_rows"], 2638) - self.assertEqual(annotations['n_cols'], 2) - - var_index_col_name = self.data.schema["annotations"]["var"]["index"] - fbs = self.data.annotation_to_fbs_matrix("var", [var_index_col_name]) - annotations = decode_fbs.decode_matrix_FBS(fbs) - self.assertEqual(annotations['n_rows'], 1838) - self.assertEqual(annotations['n_cols'], 1) - - def test_diffexp_topN(self): - f1 = {"filter": {"obs": {"index": [[0, 500]]}}} - f2 = {"filter": {"obs": {"index": [[500, 1000]]}}} - result = json.loads(self.data.diffexp_topN(f1["filter"], f2["filter"])) - self.assertEqual(len(result), 10) - result = json.loads(self.data.diffexp_topN(f1["filter"], f2["filter"], 20)) - self.assertEqual(len(result), 20) - - def test_data_frame(self): - fbs = self.data.data_frame_to_fbs_matrix(None, "var") - data = decode_fbs.decode_matrix_FBS(fbs) - self.assertEqual(data["n_rows"], 2638) - self.assertEqual(data["n_cols"], 1838) - - with self.assertRaises(ValueError): - self.data.data_frame_to_fbs_matrix(None, "obs") - - def test_filtered_data_frame(self): - filter_ = { - "filter": {"var": {"annotation_value": [{"name": "n_cells", "min": 100}]}} - } - fbs = self.data.data_frame_to_fbs_matrix(filter_["filter"], "var") - data = decode_fbs.decode_matrix_FBS(fbs) - self.assertEqual(data["n_rows"], 2638) - self.assertEqual(data["n_cols"], 1040) - - filter_ = { - "filter": {"obs": {"annotation_value": [{"name": "n_counts", "min": 3000}]}} - } - with self.assertRaises(FilterError): - self.data.data_frame_to_fbs_matrix(filter_["filter"], "var") - - def test_data_named_gene(self): - var_index_col_name = self.data.schema["annotations"]["var"]["index"] - filter_ = { - "filter": { - "var": {"annotation_value": [{"name": var_index_col_name, "values": ["RER1"]}]} - } - } - fbs = self.data.data_frame_to_fbs_matrix(filter_["filter"], "var") - data = decode_fbs.decode_matrix_FBS(fbs) - self.assertEqual(data["n_rows"], 2638) - self.assertEqual(data["n_cols"], 1) - self.assertEqual(data["col_idx"], [4]) - - filter_ = { - "filter": { - "var": {"annotation_value": [{"name": var_index_col_name, "values": ["SPEN", "TYMP", "PRMT2"]}]} - } - } - fbs = self.data.data_frame_to_fbs_matrix(filter_["filter"], "var") - data = decode_fbs.decode_matrix_FBS(fbs) - self.assertEqual(data["n_rows"], 2638) - self.assertEqual(data["n_cols"], 3) - self.assertTrue((data["col_idx"] == [15, 1818, 1837]).all()) - - if __name__ == "__main__": - unittest.main() diff --git a/server/test/test_scanpy_engine_data_load.py b/server/test/test_scanpy_engine_data_load.py deleted file mode 100644 index 9fdcfd9d..00000000 --- a/server/test/test_scanpy_engine_data_load.py +++ /dev/null @@ -1,50 +0,0 @@ -import unittest -import json - -from server.app.scanpy_engine.scanpy_engine import ScanpyEngine -from server.app.util.errors import DriverError - - -class DataLoadEngineTest(unittest.TestCase): - def setUp(self): - self.data_file = "example-dataset/pbmc3k.h5ad" - self.data = ScanpyEngine() - - def test_init(self): - self.assertIsNone(self.data.data) - - def test_delayed_load_args(self): - args = { - "layout": ["tsne"], - "diffexp": "ttest", - "max_category_items": 1000, - "obs_names": "foo", - "var_names": "bar", - "diffexp_lfc_cutoff": 0.1, - } - self.data.update(args=args) - self.assertEqual(args, self.data.config) - - def test_requires_data(self): - with self.assertRaises(DriverError): - self.data._create_schema() - - def test_delayed_load_data(self): - self.data.update(data=self.data_file) - self.data._create_schema() - self.assertEqual(self.data.cell_count, 2638) - self.assertEqual(self.data.gene_count, 1838) - epsilon = 0.000_005 - self.assertTrue(self.data.data.X[0, 0] - -0.171_469_51 < epsilon) - - def test_diffexp_topN(self): - self.data.update(data=self.data_file) - f1 = {"filter": {"obs": {"index": [[0, 500]]}}} - f2 = {"filter": {"obs": {"index": [[500, 1000]]}}} - result = json.loads(self.data.diffexp_topN(f1["filter"], f2["filter"])) - self.assertEqual(len(result), 10) - result = json.loads(self.data.diffexp_topN(f1["filter"], f2["filter"], 20)) - self.assertEqual(len(result), 20) - - if __name__ == "__main__": - unittest.main() diff --git a/server/utils/__init__.py b/server/utils/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/server/utils/constants.py b/server/utils/constants.py deleted file mode 100644 index 48f05329..00000000 --- a/server/utils/constants.py +++ /dev/null @@ -1 +0,0 @@ -MODES = ["umap", "tsne", "draw_graph_fa", "draw_graph_fr", "diffmap", "phate"] diff --git a/server/utils/utils.py b/server/utils/utils.py deleted file mode 100644 index 239f45bd..00000000 --- a/server/utils/utils.py +++ /dev/null @@ -1,26 +0,0 @@ -import contextlib -import errno -import socket - - -def find_available_port(host, port=5005): - """ - Helper method to find open port on host. Tries 5000 ports incremented from the specified port - """ - # Takes approx 2 seconds to do a scan of 5000 ports on my laptop - num_ports_to_try = 5000 - for port_to_try in range(port, port + num_ports_to_try): - if is_port_available(host, port_to_try): - return port_to_try - raise socket.error(errno.EADDRINUSE, f"No port in range {port} - {port + num_ports_to_try - 1} available.") - - -def is_port_available(host, port): - is_available = False - with contextlib.closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s: - try: - s.bind((host, port)) - is_available = True - except socket.error: - pass - return is_available diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 9b380f1d..00000000 --- a/setup.cfg +++ /dev/null @@ -1,4 +0,0 @@ -[flake8] -max-line-length = 120 -ignore = E203, W503 -exclude = server/app/util/fbs/NetEncoding/ diff --git a/setup.py b/setup.py deleted file mode 100644 index 3773323d..00000000 --- a/setup.py +++ /dev/null @@ -1,40 +0,0 @@ -from setuptools import setup, find_packages - -with open("README.md", "rb") as fh: - long_description = fh.read().decode() - -with open("server/requirements.txt") as fh: - requirements = fh.read().splitlines() - -setup( - name="cellxgene", - version="0.10.1", - packages=find_packages(), - url="https://github.com/chanzuckerberg/cellxgene", - license="MIT", - author="Colin Megill, Charlotte Weaver", - author_email="cweaver@chanzuckerberg.com", - description="Web application for exploration of large scale scRNA-seq datasets", - long_description=long_description, - long_description_content_type="text/markdown", - install_requires=requirements, - include_package_data=True, - zip_safe=False, - classifiers=[ - "Framework :: Flask", - "Intended Audience :: Science/Research", - "License :: OSI Approved :: MIT License", - "Natural Language :: English", - "Operating System :: POSIX", - "Operating System :: Unix", - "Operating System :: MacOS :: MacOS X", - "Programming Language :: JavaScript", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3 :: Only", - "Topic :: Scientific/Engineering :: Bio-Informatics", - ], - entry_points={"console_scripts": ["cellxgene = server.cli.cli:cli"]}, - extras_require=dict(louvain=["python-igraph", "louvain>=0.6"], gui=["PySide2>=5.12.3", "cefpython3>=66", "requests"]), -) diff --git a/travis-build.sh b/travis-build.sh deleted file mode 100755 index 9f043d0e..00000000 --- a/travis-build.sh +++ /dev/null @@ -1,5 +0,0 @@ -set -eo pipefail -flake8 server -npm run --prefix client/ build -npm run --prefix client/ unit-test -pytest -s server/test