diff --git a/client/__tests__/e2e/__snapshots__/e2eAnnotations.test.js.snap b/client/__tests__/e2e/__snapshots__/e2eAnnotations.test.js.snap index a16e2dcb..818426cd 100644 --- a/client/__tests__/e2e/__snapshots__/e2eAnnotations.test.js.snap +++ b/client/__tests__/e2e/__snapshots__/e2eAnnotations.test.js.snap @@ -2,14 +2,14 @@ exports[`annotations stacked bar graph renders 1`] = ` Array [ - "
TEST-LABELLABEL
0
", - "
unassignedigned
2132
", + "
TEST-LABELLABEL
0
", + "
unassignedigned
2132
", ] `; exports[`annotations stacked bar graph renders 2`] = ` Array [ - "
TEST-LABELLABEL
0
", - "
unassignedigned
2638
", + "
TEST-LABELLABEL
0
", + "
unassignedigned
2638
", ] `; diff --git a/client/__tests__/e2e/e2eAnnotations.test.js b/client/__tests__/e2e/e2eAnnotations.test.js index 3e930237..e284845c 100644 --- a/client/__tests__/e2e/e2eAnnotations.test.js +++ b/client/__tests__/e2e/e2eAnnotations.test.js @@ -304,7 +304,7 @@ describe.each([ node.getAttribute("aria-label") ); - expect(result).toBe(categoryName); + return expect(result).toBe(categoryName); } async function assertLabelExists(categoryName, labelName) { diff --git a/client/__tests__/util/annoMatrix/annoMatrix.test.js b/client/__tests__/util/annoMatrix/annoMatrix.test.js new file mode 100644 index 00000000..17513d19 --- /dev/null +++ b/client/__tests__/util/annoMatrix/annoMatrix.test.js @@ -0,0 +1,316 @@ +// these TWO statements MUST be first in the file, before any other imports +import { enableFetchMocks } from "jest-fetch-mock"; +import * as serverMocks from "./serverMocks"; +// OK, continue on! + +import { + AnnoMatrixLoader, + clip, + isubset, + isubsetMask, +} from "../../../src/annoMatrix"; +import { Dataframe } from "../../../src/util/dataframe"; + +enableFetchMocks(); + +describe("AnnoMatrix", () => { + let annoMatrix; + + beforeEach(async () => { + fetch.resetMocks(); // reset all fetch mocking state + annoMatrix = new AnnoMatrixLoader( + serverMocks.baseDataURL, + serverMocks.schema.schema + ); + }); + + describe("basics", () => { + test("annomatrix static checks", () => { + expect(annoMatrix).toBeDefined(); + expect(annoMatrix.schema).toMatchObject(serverMocks.schema.schema); + expect(annoMatrix.nObs).toEqual(serverMocks.schema.schema.dataframe.nObs); + expect(annoMatrix.nVar).toEqual(serverMocks.schema.schema.dataframe.nVar); + expect(annoMatrix.isView).toBeFalsy(); + expect(annoMatrix.viewOf).toBeUndefined(); + expect(annoMatrix.rowIndex).toBeDefined(); + }); + + test("simple single column fetch", async () => { + fetch.once(serverMocks.annotationsObs(["name_0"])); + + const df = await annoMatrix.fetch("obs", "name_0"); + expect(df).toBeInstanceOf(Dataframe); + expect(df.colIndex.labels()).toEqual(["name_0"]); + expect(df.dims).toEqual([annoMatrix.nObs, 1]); + }); + + test("simple multi column fetch", async () => { + fetch + .once(serverMocks.annotationsObs(["name_0"])) + .once(serverMocks.annotationsObs(["n_genes"])); + + await expect( + annoMatrix.fetch("obs", ["name_0", "n_genes"]) + ).resolves.toBeInstanceOf(Dataframe); + }); + + describe("fetch from field", () => { + const getLastTwo = async (field) => { + const columnNames = annoMatrix.getMatrixColumns(field).slice(-2); + fetch.mockResponses(...columnNames.map(() => serverMocks.responder)); + await expect( + annoMatrix.fetch(field, columnNames) + ).resolves.toBeInstanceOf(Dataframe); + }; + + test("obs", async () => getLastTwo("obs")); + test("var", async () => getLastTwo("var")); + test("emb", async () => getLastTwo("emb")); + }); + + test("fetch - test all query forms", async () => { + // single string is a column name + fetch.once(serverMocks.annotationsObs(["n_genes"])); + await expect(annoMatrix.fetch("obs", "n_genes")).resolves.toBeInstanceOf( + Dataframe + ); + + // array of column names, expecting n_genes to be cached. + fetch.once(serverMocks.annotationsObs(["percent_mito"])); + await expect( + annoMatrix.fetch("obs", ["n_genes", "percent_mito"]) + ).resolves.toBeInstanceOf(Dataframe); + + // more complex value filter query, enumerated + fetch.once(serverMocks.responder); + await expect( + annoMatrix.fetch("X", { + field: "var", + column: annoMatrix.schema.annotations.var.index, + value: "TYMP", + }) + ).resolves.toBeInstanceOf(Dataframe); + + // more complex value filter query, range + const varIndex = annoMatrix.schema.annotations.var.index; + fetch + .once( + serverMocks.withExpected("/data/var", [[`var:${varIndex}`, "SUMO3"]]) + ) + .once( + serverMocks.withExpected("/data/var", [[`var:${varIndex}`, "TYMP"]]) + ); + await expect( + annoMatrix.fetch("X", [ + { + field: "var", + column: varIndex, + value: "SUMO3", + }, + { + field: "var", + column: varIndex, + value: "TYMP", + }, + ]) + ).resolves.toBeInstanceOf(Dataframe); + // XXX inspect the wherecache? + }); + + test("push and pop views", async () => { + const am1 = clip(annoMatrix, 0.1, 0.9); + expect(am1.viewOf).toBe(annoMatrix); + expect(am1.nObs).toEqual(annoMatrix.nObs); + expect(am1.nVar).toEqual(annoMatrix.nVar); + expect(am1.rowIndex).toBe(annoMatrix.rowIndex); + + const am2 = clip(annoMatrix, 0.1, 0.9); + expect(am2.viewOf).toBe(annoMatrix); + expect(am2).not.toBe(am1); + expect(am2.rowIndex).toBe(annoMatrix.rowIndex); + }); + + test("schema accessors", () => { + expect(annoMatrix.getMatrixFields()).toEqual( + expect.arrayContaining(["X", "obs", "emb", "var"]) + ); + expect(annoMatrix.getMatrixColumns("obs")).toEqual( + expect.arrayContaining(["name_0", "n_genes", "louvain"]) + ); + expect(annoMatrix.getColumnSchema("emb", "umap")).toEqual({ + name: "umap", + dims: ["umap_0", "umap_1"], + type: "float32", + }); + expect(annoMatrix.getColumnDimensions("emb", "umap")).toEqual([ + "umap_0", + "umap_1", + ]); + }); + + /* + test the mask & label access to subset via isubset and isubsetMask + */ + test("isubset", async () => { + const rowList = [0, 10]; + const rowMask = new Uint8Array(annoMatrix.nObs); + for (let i = 0; i < rowList.length; i += 1) { + rowMask[rowList[i]] = 1; + } + + const am1 = isubset(annoMatrix, rowList); + const am2 = isubsetMask(annoMatrix, rowMask); + expect(am1).not.toBe(am2); + expect(am1.nObs).toEqual(2); + expect(am1.nObs).toEqual(am2.nObs); + expect(am1.nVar).toEqual(am2.nVar); + + fetch + .once(serverMocks.annotationsObs(["n_genes"])) + .once(serverMocks.annotationsObs(["n_genes"])); + const ng1 = await am1.fetch("obs", "n_genes"); + const ng2 = await am2.fetch("obs", "n_genes"); + expect(ng1).toHaveLength(ng2.length); + expect(ng1.colIndex.labels()).toEqual(ng2.colIndex.labels()); + expect(ng1.col("n_genes").asArray()).toEqual( + ng2.col("n_genes").asArray() + ); + }); + }); + + describe("add/drop column", () => { + async function addDrop(base) { + expect(base.getMatrixColumns("obs")).not.toContain("foo"); + fetch.mockRejectOnce(new Error("unknown column name")); + await expect(base.fetch("obs", "foo")).rejects.toThrow( + "unknown column name" + ); + + /* add */ + const am1 = base.addObsColumn( + { name: "foo", type: "float32", writable: true }, + Float32Array, + 0 + ); + expect(base.getMatrixColumns("obs")).not.toContain("foo"); + expect(am1.getMatrixColumns("obs")).toContain("foo"); + const foo = await am1.fetch("obs", "foo"); + expect(foo).toBeDefined(); + expect(foo).toBeInstanceOf(Dataframe); + expect(foo).toHaveLength(am1.nObs); + expect(foo.col("foo").asArray()).toEqual( + new Float32Array(am1.nObs).fill(0) + ); + + /* drop */ + const am2 = am1.dropObsColumn("foo"); + expect(base.getMatrixColumns("obs")).not.toContain("foo"); + expect(am2.getMatrixColumns("obs")).not.toContain("foo"); + fetch.mockRejectOnce(new Error("unknown column name")); + await expect(am2.fetch("obs", "foo")).rejects.toThrow( + "unknown column name" + ); + } + + test("add/drop column, without view", async () => { + await addDrop(annoMatrix); + }); + + test("add/drop column, with view", async () => { + const am1 = clip(annoMatrix, 0.1, 0.9); + await addDrop(am1); + + const am2 = isubset(am1, [0, 1, 2, 20, 30, 400]); + await addDrop(am2); + + const am3 = isubset(annoMatrix, [10, 0, 7, 3]); + await addDrop(am3); + + const am4 = clip(am3, 0, 1); + await addDrop(am4); + + fetch.mockResponse(serverMocks.responder); + + await am1.fetch("obs", am1.getMatrixColumns("obs")); + await am2.fetch("obs", am2.getMatrixColumns("obs")); + await am3.fetch("obs", am3.getMatrixColumns("obs")); + await am4.fetch("obs", am4.getMatrixColumns("obs")); + + fetch.resetMocks(); + + await addDrop(am1); + await addDrop(am2); + await addDrop(am3); + await addDrop(am4); + }); + }); + + describe("setObsColumnValues", () => { + async function addSetDrop(base) { + /* add column */ + let am = base.addObsColumn( + { + name: "test", + type: "categorical", + categories: ["unassigned", "red", "green"], + writable: true, + }, + Array, + "unassigned" + ); + + const testVal = await am.fetch("obs", "test"); + expect(testVal.col("test").asArray()).toEqual( + new Array(am.nObs).fill("unassigned") + ); + + /* set values in column */ + const whichRows = [1, 2, 10]; + const am1 = await am.setObsColumnValues("test", whichRows, "yo"); + const testVal1 = await am1.fetch("obs", "test"); + const expt = new Array(am1.nObs).fill("unassigned"); + for (let i = 0; i < whichRows.length; i += 1) { + const offset = am1.rowIndex.getOffset(whichRows[i]); + expt[offset] = "yo"; + } + expect(testVal1).not.toBe(testVal); + expect(testVal1.col("test").asArray()).toEqual(expt); + expect(am1.getColumnSchema("obs", "test").type).toBe("categorical"); + expect(am1.getColumnSchema("obs", "test").categories).toEqual( + expect.arrayContaining(["unassigned", "red", "green", "yo"]) + ); + + /* drop column */ + fetch.mockRejectOnce(new Error("unknown column name")); + am = am1.dropObsColumn("test"); + await expect(am.fetch("obs", "test")).rejects.toThrow( + "unknown column name" + ); + } + + test("set, without a view", async () => { + await addSetDrop(annoMatrix); + }); + + test("set, with a view", async () => { + const am1 = clip(annoMatrix, 0.1, 0.9); + await addSetDrop(am1); + + const am2 = isubset(am1, [0, 1, 2, 10, 20, 30, 400]); + await addSetDrop(am2); + + const am3 = isubset(annoMatrix, [10, 1, 0, 30, 2]); + await addSetDrop(am3); + + fetch.mockResponse(serverMocks.responder); + + await am1.fetch("obs", am1.getMatrixColumns("obs")); + await am2.fetch("obs", am2.getMatrixColumns("obs")); + await am3.fetch("obs", am3.getMatrixColumns("obs")); + + await addSetDrop(am1); + await addSetDrop(am2); + await addSetDrop(am3); + }); + }); +}); diff --git a/client/__tests__/util/annoMatrix/crossfilter.test.js b/client/__tests__/util/annoMatrix/crossfilter.test.js new file mode 100644 index 00000000..8ee0758b --- /dev/null +++ b/client/__tests__/util/annoMatrix/crossfilter.test.js @@ -0,0 +1,688 @@ +// these TWO statements MUST be first in the file, before any other imports +import { enableFetchMocks } from "jest-fetch-mock"; +import * as serverMocks from "./serverMocks"; +// OK, continue on! + +import obsLouvain from "./louvain.json"; +import obsNGenes from "./n_genes.json"; +import embUmap from "./umap.json"; + +import { + AnnoMatrixLoader, + AnnoMatrixObsCrossfilter, + isubsetMask, +} from "../../../src/annoMatrix"; +import { rangeFill } from "../../../src/util/range"; + +enableFetchMocks(); + +describe("AnnoMatrixCrossfilter", () => { + let annoMatrix; + let crossfilter; + + beforeEach(async () => { + fetch.resetMocks(); // reset all fetch mocking state + annoMatrix = new AnnoMatrixLoader( + serverMocks.baseDataURL, + serverMocks.schema.schema + ); + crossfilter = new AnnoMatrixObsCrossfilter(annoMatrix); + }); + + test("initial state of crossfilter", () => { + const { nObs } = annoMatrix; + + expect(crossfilter).toBeDefined(); + expect(crossfilter.size()).toEqual(nObs); + expect(crossfilter.annoMatrix).toBe(annoMatrix); + + /* by default, everything should be selected, even if no data in cache */ + expect(crossfilter.countSelected()).toEqual(nObs); + expect(crossfilter.allSelectedLabels()).toEqual( + rangeFill(new Int32Array(nObs)) + ); + expect(crossfilter.allSelectedMask()).toEqual(new Uint8Array(nObs).fill(1)); + expect(crossfilter.fillByIsSelected(new Uint8Array(nObs), 2, 1)).toEqual( + new Uint8Array(nObs).fill(2) + ); + }); + + describe("select", () => { + /* + test the selection state via crossfilter proxy + */ + + test("select loads index", async () => { + /* + Select should transparently load/create dimension index. + + Internal dimension names are field/col:col:col..., eg, + + obs:louvain + emb:umap_0:umap_1 + + */ + expect(crossfilter.obsCrossfilter.dimensionNames()).toEqual([]); + expect( + crossfilter.obsCrossfilter.hasDimension("obs/louvain") + ).toBeFalsy(); + + fetch.once(serverMocks.dataframeResponse(["louvain"], [obsLouvain])); + let newCrossfilter = await crossfilter.select("obs", "louvain", { + mode: "none", + }); + + expect(newCrossfilter.countSelected()).toEqual(0); + expect( + newCrossfilter.obsCrossfilter.hasDimension("obs/louvain") + ).toBeTruthy(); + expect(fetch.mock.calls).toHaveLength(1); + + newCrossfilter = await crossfilter.select("obs", "louvain", { + mode: "all", + }); + expect(newCrossfilter.countSelected()).toEqual(annoMatrix.nObs); + }); + + test("simple column select", async () => { + let xfltr; + + fetch.once(serverMocks.dataframeResponse(["louvain"], [obsLouvain])); + xfltr = await crossfilter.select("obs", "louvain", { + mode: "exact", + values: ["NK cells", "B cells"], + }); + + expect(xfltr).toBeDefined(); + expect(xfltr.countSelected()).toEqual(496); + expect(xfltr.allSelectedMask()).toEqual( + Uint8Array.from( + obsLouvain.map((val) => + val === "NK cells" || val === "B cells" ? 1 : 0 + ) + ) + ); + expect(xfltr.allSelectedLabels()).toEqual( + Int32Array.from( + obsLouvain.reduce((acc, val, idx) => { + if (val === "NK cells" || val === "B cells") acc.push(idx); + return acc; + }, []) + ) + ); + expect( + xfltr.fillByIsSelected(new Uint8Array(annoMatrix.nObs), 3, 1) + ).toEqual( + Uint8Array.from( + obsLouvain.map((val) => + val === "NK cells" || val === "B cells" ? 3 : 1 + ) + ) + ); + + const df = await annoMatrix.fetch("obs", "louvain"); + const values = df.col("louvain").asArray(); + const selected = xfltr.allSelectedMask(); + values.every( + (val, idx) => !["NK cells", "B cells"].includes(val) !== !selected[idx] + ); + + fetch.once( + serverMocks.dataframeResponse(["n_genes"], [new Int32Array(obsNGenes)]) + ); + xfltr = await xfltr.select("obs", "n_genes", { + mode: "range", + lo: 0, + hi: 500, + inclusive: false, + }); + expect(xfltr.countSelected()).toEqual(33); + expect(xfltr.allSelectedLabels()).toEqual( + Int32Array.from( + obsNGenes.reduce((acc, val, idx) => { + const louvain = obsLouvain[idx]; + if ( + val >= 0 && + val < 500 && + (louvain === "NK cells" || louvain === "B cells") + ) + acc.push(idx); + return acc; + }, []) + ) + ); + + xfltr = await xfltr.selectAll(); + expect(xfltr.countSelected()).toEqual(annoMatrix.nObs); + }); + + test("join column select", async () => { + const varIndex = annoMatrix.schema.annotations.var.index; + + const { nObs } = annoMatrix.schema.dataframe; + fetch.once( + serverMocks.dataframeResponse( + ["TEST"], + [rangeFill(new Float32Array(nObs), 0, 0.1)] + ) + ); + + const xfltr = await crossfilter.select( + "X", + { + field: "var", + column: varIndex, + value: "TYMP", + }, + { + mode: "range", + lo: 0, + hi: 50, + inclusive: true, + } + ); + + expect(xfltr).toBeDefined(); + expect(xfltr.countSelected()).toEqual(501); + + const df = await annoMatrix.fetch("X", { + field: "var", + column: varIndex, + value: "TYMP", + }); + const values = df.icol(0).asArray(); + const selected = xfltr.allSelectedMask(); + values.every((val, idx) => !(val >= 0 && val <= 50) !== !selected[idx]); + expect(selected.reduce((acc, val) => (val ? acc + 1 : acc), 0)).toEqual( + xfltr.countSelected() + ); + }); + + test("spatial column select", async () => { + fetch.once( + serverMocks.dataframeResponse( + ["umap_0", "umap_1"], + [Float32Array.from(embUmap[0]), Float32Array.from(embUmap[1])] + ) + ); + const xfltr = await crossfilter.select("emb", "umap", { + mode: "within-rect", + minX: 0, + minY: 0, + maxX: 0.5, + maxY: 0.5, + }); + expect(xfltr.countSelected()).toEqual(16); + }); + + test("select on subset", async () => { + const mask = new Uint8Array(annoMatrix.nObs).fill(0); + for (let i = 0; i < mask.length; i += 2) { + mask[i] = true; + } + const annoMatrixSubset = isubsetMask(annoMatrix, mask); + expect(annoMatrixSubset.nObs).toEqual(Math.floor(annoMatrix.nObs / 2)); + + let xfltr = new AnnoMatrixObsCrossfilter(annoMatrixSubset); + expect(xfltr.countSelected()).toEqual(annoMatrixSubset.nObs); + + fetch.once(serverMocks.dataframeResponse(["louvain"], [obsLouvain])); + xfltr = await xfltr.select("obs", "louvain", { + mode: "exact", + values: ["NK cells", "B cells"], + }); + + expect(xfltr).toBeDefined(); + expect(xfltr.countSelected()).toEqual(240); + + const df = await annoMatrixSubset.fetch("obs", "louvain"); + const values = df.col("louvain").asArray(); + const selected = xfltr.allSelectedMask(); + values.every( + (val, idx) => !["NK cells", "B cells"].includes(val) !== !selected[idx] + ); + }); + + test("select catches errors", async () => { + await expect(crossfilter.select("NADA", "foo")).rejects.toThrow( + "Unknown field name" + ); + + await expect(crossfilter.select("var", "foo")).rejects.toThrow( + "unable to obsSelect upon the var dimension" + ); + + fetch.mockRejectOnce(new Error("unknown column name")); + await expect(crossfilter.select("obs", "foo")).rejects.toThrow( + "unknown column name" + ); + }); + }); + + describe("mutate matrix", () => { + /* + test the matrix mutators via crossfilter proxy + */ + async function helperAddTestCol(cf, colName, colSchema = null) { + expect( + cf.annoMatrix.getMatrixColumns("obs").includes(colName) + ).toBeFalsy(); + + if (colSchema === null) { + colSchema = { + name: colName, + type: "categorical", + categories: ["toasty"], + }; + } + colSchema.name = colName; + const initValue = colSchema.categories[0]; + const xfltr = cf.addObsColumn(colSchema, Array, initValue); + expect( + xfltr.annoMatrix.schema.annotations.obs.columns.filter( + (v) => v.name === colName + ) + ).toHaveLength(1); + const df = await xfltr.annoMatrix.fetch("obs", colName); + expect(df.hasCol(colName)).toBeTruthy(); + return xfltr; + } + + test("addObsColumn", async () => { + expect(crossfilter.countSelected()).toBe(annoMatrix.nObs); + expect( + crossfilter.annoMatrix.getMatrixColumns("obs").includes("foo") + ).toBeFalsy(); + const xfltr = crossfilter.addObsColumn( + { name: "foo", type: "categorical", categories: ["A"] }, + Array, + "A" + ); + + // check schema updates correctly. + expect(xfltr.countSelected()).toBe(annoMatrix.nObs); + expect( + xfltr.annoMatrix.getMatrixColumns("obs").includes("foo") + ).toBeTruthy(); + expect(xfltr.annoMatrix.schema.annotations.obsByName.foo).toMatchObject({ + name: "foo", + type: "categorical", + }); + expect( + xfltr.annoMatrix.schema.annotations.obs.columns.filter( + (v) => v.name === "foo" + ) + ).toHaveLength(1); + + // check data update. + const df = await xfltr.annoMatrix.fetch("obs", "foo"); + expect( + df + .col("foo") + .asArray() + .every((v) => v === "A") + ).toBeTruthy(); + + // check that we catch dups + expect(() => + xfltr.addObsColumn( + { name: "foo", type: "categorical" }, + Array, + "toasty" + ) + ).toThrow("column already exists"); + expect(() => + xfltr.addObsColumn( + { name: "louvain", type: "categorical" }, + Array, + "toasty" + ) + ).toThrow("column already exists"); + }); + + test("dropObsColumn", async () => { + let xfltr; + + /* check that we catch attempt to drop readonly dimension */ + expect(() => crossfilter.dropObsColumn("louvain")).toThrow( + "Unknown or readonly obs column" + ); + /* non-existent column */ + expect(() => crossfilter.dropObsColumn("does-not-exist")).toThrow( + "Unknown or readonly obs column" + ); + + // add a column, then drop it. + xfltr = await helperAddTestCol(crossfilter, "foo"); + xfltr = xfltr.dropObsColumn("foo"); + expect( + xfltr.annoMatrix.schema.annotations.obs.columns.filter( + (v) => v.name === "foo" + ) + ).toHaveLength(0); + expect(xfltr.annoMatrix.schema.annotations.obsByName.foo).toBeUndefined(); + fetch.mockRejectOnce(new Error("unknown column name")); + await expect(xfltr.annoMatrix.fetch("obs", "foo")).rejects.toThrow( + "unknown column name" + ); + + // now same, but ensure we have built an index before doing the drop + xfltr = await helperAddTestCol(crossfilter, "bar"); + xfltr = await xfltr.select("obs", "bar", { + mode: "exact", + values: "whatever", + }); + xfltr = xfltr.dropObsColumn("bar"); + + fetch.mockRejectOnce(new Error("unknown column name")); + await expect(xfltr.select("obs", "bar", { mode: "all" })).rejects.toThrow( + "unknown column name" + ); + }); + + test("renameObsColumn", async () => { + let xfltr; + + /* catch attempts to rename non-existent or readonly columns */ + expect(() => + crossfilter.renameObsColumn("does-not-exist", "foo") + ).toThrow("Unknown or readonly obs column"); + expect(() => crossfilter.renameObsColumn("louvain", "foo")).toThrow( + "Unknown or readonly obs column" + ); + + // add a column, then rename it. + xfltr = await helperAddTestCol(crossfilter, "foo"); + xfltr = xfltr.renameObsColumn("foo", "bar"); + expect(xfltr.annoMatrix.getColumnSchema("obs", "foo")).toBeUndefined(); + expect(xfltr.annoMatrix.getColumnSchema("obs", "bar")).toMatchObject({ + name: "bar", + type: "categorical", + }); + + fetch.mockRejectOnce(new Error("unknown column name")); + await expect(xfltr.annoMatrix.fetch("obs", "foo")).rejects.toThrow( + "unknown column name" + ); + const df = await xfltr.annoMatrix.fetch("obs", "bar"); + expect(df.hasCol("bar")).toBeTruthy(); + + // now same, but ensure we have built an index before doing the rename + xfltr = await helperAddTestCol(crossfilter, "bar"); + xfltr = await xfltr.select("obs", "bar", { + mode: "exact", + values: "whatever", + }); + xfltr = xfltr.renameObsColumn("bar", "xyz"); + + fetch.mockRejectOnce(new Error("unknown column name")); + await expect(xfltr.select("obs", "bar", { mode: "all" })).rejects.toThrow( + "unknown column name" + ); + await expect( + xfltr.select("obs", "xyz", { mode: "none" }) + ).resolves.toBeInstanceOf(AnnoMatrixObsCrossfilter); + }); + + test("addObsAnnoCategory", async () => { + let xfltr; + + // catch unknown or readonly columns + expect(() => crossfilter.addObsAnnoCategory("louvain", "mumble")).toThrow( + "Unknown or readonly obs column" + ); + expect(() => + crossfilter.addObsAnnoCategory("undefined-name", "mumble") + ).toThrow("Unknown or readonly obs column"); + + // add a column and then add category to it + xfltr = await helperAddTestCol(crossfilter, "foo", { + name: "foo", + type: "categorical", + categories: ["unassigned"], + }); + xfltr = xfltr.addObsAnnoCategory("foo", "a-new-label"); + expect(xfltr.annoMatrix.getColumnSchema("obs", "foo")).toMatchObject({ + name: "foo", + type: "categorical", + categories: expect.arrayContaining(["a-new-label", "unassigned"]), + }); + + // do it again, dup; should throw + expect(() => xfltr.addObsAnnoCategory("foo", "a-new-label")).toThrow( + "category already exists" + ); + + // now same, but ensure we have built an index before doing the operation + xfltr = await helperAddTestCol(crossfilter, "bar", { + name: "bar", + type: "categorical", + categories: ["unassigned"], + }); + xfltr = await xfltr.select("obs", "bar", { + mode: "exact", + values: "something", + }); + xfltr = xfltr.addObsAnnoCategory("bar", "a-new-label"); + expect(xfltr.annoMatrix.getColumnSchema("obs", "bar")).toMatchObject({ + name: "bar", + type: "categorical", + categories: expect.arrayContaining(["a-new-label", "unassigned"]), + }); + }); + + test("removeObsAnnoCategory", async () => { + let xfltr; + + // catch unknown or readonly categories + await expect(() => + crossfilter.removeObsAnnoCategory("louvain", "mumble", "unassigned") + ).rejects.toThrow("Unknown or readonly obs column"); + await expect(() => + crossfilter.removeObsAnnoCategory("undefined-name", "mumble") + ).rejects.toThrow("Unknown or readonly obs column"); + + xfltr = await helperAddTestCol(crossfilter, "foo", { + name: "foo", + type: "categorical", + categories: ["unassigned", "red", "green", "blue"], + }); + xfltr = await xfltr.select("obs", "foo", { mode: "all" }); + expect( + (await xfltr.annoMatrix.fetch("obs", "foo")) + .col("foo") + .asArray() + .every((v) => v === "unassigned") + ).toBeTruthy(); + expect(xfltr.annoMatrix.getColumnSchema("obs", "foo")).toMatchObject({ + name: "foo", + type: "categorical", + categories: expect.arrayContaining([ + "unassigned", + "red", + "green", + "blue", + ]), + }); + + // remove an unused category + const xfltr1 = await xfltr.removeObsAnnoCategory("foo", "red", "mumble"); + expect( + (await xfltr1.annoMatrix.fetch("obs", "foo")) + .col("foo") + .asArray() + .every((v) => v === "unassigned") + ).toBeTruthy(); + expect(xfltr1.annoMatrix.getColumnSchema("obs", "foo")).toMatchObject({ + name: "foo", + type: "categorical", + categories: expect.arrayContaining([ + "unassigned", + "green", + "blue", + "mumble", + ]), + }); + + // remove a used category + const xfltr2 = await xfltr.removeObsAnnoCategory( + "foo", + "unassigned", + "red" + ); + expect( + (await xfltr2.annoMatrix.fetch("obs", "foo")) + .col("foo") + .asArray() + .every((v) => v === "red") + ).toBeTruthy(); + expect(xfltr2.annoMatrix.getColumnSchema("obs", "foo")).toMatchObject({ + name: "foo", + type: "categorical", + categories: expect.arrayContaining(["green", "blue", "red"]), + }); + }); + + test("setObsColumnValues", async () => { + // catch unknown or readonly categories + await expect(() => + crossfilter.setObsColumnValues("louvain", [0, 1], "unassigned") + ).rejects.toThrow("Unknown or readonly obs column"); + await expect(() => + crossfilter.setObsColumnValues("undefined-name", [0], "mumble") + ).rejects.toThrow("Unknown or readonly obs column"); + + let xfltr = await helperAddTestCol(crossfilter, "foo", { + name: "foo", + type: "categorical", + categories: ["unassigned", "red", "green", "blue"], + }); + xfltr = await xfltr.select("obs", "foo", { mode: "all" }); + + // catch unknown row label + await expect(() => + xfltr.setObsColumnValues("foo", [-1], "red") + ).rejects.toThrow("Unknown row label"); + + // set a few rows + expect( + (await xfltr.annoMatrix.fetch("obs", "foo")) + .col("foo") + .asArray() + .every((v) => v === "unassigned") + ).toBeTruthy(); + const xfltr1 = await xfltr.setObsColumnValues("foo", [0, 10], "purple"); + expect( + (await xfltr1.annoMatrix.fetch("obs", "foo")) + .col("foo") + .asArray() + .every( + (v, i) => + v === "unassigned" || (v === "purple" && (i === 0 || i === 10)) + ) + ).toBeTruthy(); + expect(xfltr1.annoMatrix.getColumnSchema("obs", "foo")).toMatchObject({ + name: "foo", + type: "categorical", + categories: expect.arrayContaining([ + "unassigned", + "red", + "green", + "blue", + "purple", + ]), + }); + + expect(xfltr1.countSelected()).toEqual(xfltr1.annoMatrix.nObs); + const xfltr2 = await xfltr1.select("obs", "foo", { + mode: "exact", + values: ["purple"], + }); + expect(xfltr2.countSelected()).toEqual(2); + expect(xfltr2.allSelectedLabels()).toEqual(Int32Array.from([0, 10])); + }); + + test("resetObsColumnValues", async () => { + // catch unknown or readonly categories + await expect(() => + crossfilter.resetObsColumnValues("louvain", "red", "blue") + ).rejects.toThrow("Unknown or readonly obs column"); + await expect(() => + crossfilter.resetObsColumnValues("undefined-name", "red", "blue") + ).rejects.toThrow("Unknown or readonly obs column"); + + let xfltr = await helperAddTestCol(crossfilter, "foo", { + name: "foo", + type: "categorical", + categories: ["unassigned", "red", "green", "blue"], + }); + xfltr = await xfltr.select("obs", "foo", { + mode: "exact", + values: "red", + }); + + // catch unknown category name label + await expect(() => + xfltr.resetObsColumnValues("foo", "unknown-label", "red") + ).rejects.toThrow("unknown category"); + + let xfltr1 = await xfltr.setObsColumnValues("foo", [0, 10], "purple"); + xfltr1 = await xfltr1.select("obs", "foo", { + mode: "exact", + values: "purple", + }); + expect( + (await xfltr1.annoMatrix.fetch("obs", "foo")) + .col("foo") + .asArray() + .filter((v) => v === "purple") + ).toHaveLength(2); + + xfltr1 = await xfltr1.resetObsColumnValues("foo", "purple", "magenta"); + expect( + (await xfltr1.annoMatrix.fetch("obs", "foo")) + .col("foo") + .asArray() + .filter((v) => v === "magenta") + ).toHaveLength(2); + expect( + (await xfltr1.annoMatrix.fetch("obs", "foo")) + .col("foo") + .asArray() + .filter((v) => v === "purple") + ).toHaveLength(0); + expect(xfltr1.annoMatrix.getColumnSchema("obs", "foo")).toMatchObject({ + name: "foo", + type: "categorical", + categories: expect.arrayContaining([ + "unassigned", + "red", + "green", + "blue", + "purple", + "magenta", + ]), + }); + }); + }); + + describe("edge cases", () => { + test("transition from empty annoMatrix", async () => { + // select before fetch needs to work + fetch.once(serverMocks.dataframeResponse(["louvain"], [obsLouvain])); + const xfltr = await crossfilter.select("obs", "louvain", { + mode: "exact", + values: "B cells", + }); + expect(fetch.mock.calls).toHaveLength(1); + expect(xfltr.obsCrossfilter.hasDimension("obs/louvain")).toBeTruthy(); + expect(xfltr.obsCrossfilter.all()).toBe(xfltr.annoMatrix._cache.obs); + expect(xfltr.countSelected()).toEqual( + obsLouvain.reduce( + (count, v) => (v === "B cells" ? count + 1 : count), + 0 + ) + ); + }); + }); +}); diff --git a/client/__tests__/util/annoMatrix/louvain.json b/client/__tests__/util/annoMatrix/louvain.json new file mode 100644 index 00000000..ac60509f --- /dev/null +++ b/client/__tests__/util/annoMatrix/louvain.json @@ -0,0 +1,2640 @@ +[ + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD14+ Monocytes", + "NK cells", + "CD8 T cells", + "CD8 T cells", + "CD8 T cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "B cells", + "B cells", + "B cells", + "CD4 T cells", + "CD14+ Monocytes", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD8 T cells", + "B cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "CD8 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD14+ Monocytes", + "FCGR3A+ Monocytes", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "CD4 T cells", + "NK cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD8 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "B cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "B cells", + "CD4 T cells", + "CD14+ Monocytes", + "B cells", + "B cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "Dendritic cells", + "NK cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "NK cells", + "B cells", + "CD8 T cells", + "B cells", + "CD4 T cells", + "Dendritic cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "FCGR3A+ Monocytes", + "CD4 T cells", + "B cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "CD14+ Monocytes", + "B cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "B cells", + "NK cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "B cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD8 T cells", + "NK cells", + "B cells", + "CD8 T cells", + "CD8 T cells", + "B cells", + "CD4 T cells", + "CD14+ Monocytes", + "NK cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "CD8 T cells", + "CD14+ Monocytes", + "B cells", + "Dendritic cells", + "B cells", + "CD14+ Monocytes", + "CD8 T cells", + "B cells", + "CD4 T cells", + "CD8 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "Dendritic cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "B cells", + "FCGR3A+ Monocytes", + "B cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD14+ Monocytes", + "NK cells", + "NK cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "B cells", + "CD4 T cells", + "B cells", + "CD8 T cells", + "NK cells", + "CD8 T cells", + "CD8 T cells", + "NK cells", + "CD4 T cells", + "CD8 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "NK cells", + "CD4 T cells", + "CD8 T cells", + "CD8 T cells", + "CD8 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "B cells", + "CD14+ Monocytes", + "CD4 T cells", + "Megakaryocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "B cells", + "CD4 T cells", + "Dendritic cells", + "CD4 T cells", + "B cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "B cells", + "B cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "Megakaryocytes", + "NK cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "FCGR3A+ Monocytes", + "NK cells", + "Megakaryocytes", + "CD4 T cells", + "B cells", + "FCGR3A+ Monocytes", + "CD8 T cells", + "B cells", + "CD4 T cells", + "Dendritic cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "B cells", + "NK cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "B cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "B cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "NK cells", + "FCGR3A+ Monocytes", + "CD14+ Monocytes", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD8 T cells", + "CD14+ Monocytes", + "B cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "NK cells", + "CD14+ Monocytes", + "B cells", + "Dendritic cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "NK cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD14+ Monocytes", + "NK cells", + "B cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "FCGR3A+ Monocytes", + "B cells", + "CD8 T cells", + "NK cells", + "NK cells", + "CD8 T cells", + "B cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "Dendritic cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "NK cells", + "CD4 T cells", + "B cells", + "B cells", + "CD14+ Monocytes", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "B cells", + "CD8 T cells", + "CD14+ Monocytes", + "NK cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "B cells", + "Dendritic cells", + "FCGR3A+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "B cells", + "B cells", + "CD4 T cells", + "CD14+ Monocytes", + "B cells", + "CD4 T cells", + "CD14+ Monocytes", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "B cells", + "NK cells", + "CD4 T cells", + "B cells", + "CD8 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "B cells", + "B cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "NK cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "Dendritic cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "B cells", + "NK cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "B cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "B cells", + "NK cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "CD4 T cells", + "CD8 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "B cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD14+ Monocytes", + "B cells", + "CD8 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD8 T cells", + "CD8 T cells", + "FCGR3A+ Monocytes", + "FCGR3A+ Monocytes", + "CD4 T cells", + "NK cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "Megakaryocytes", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "B cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "Dendritic cells", + "CD8 T cells", + "Dendritic cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "B cells", + "B cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD14+ Monocytes", + "NK cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD8 T cells", + "CD4 T cells", + "B cells", + "Megakaryocytes", + "CD8 T cells", + "B cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "FCGR3A+ Monocytes", + "NK cells", + "B cells", + "B cells", + "B cells", + "B cells", + "CD8 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "FCGR3A+ Monocytes", + "B cells", + "CD8 T cells", + "CD4 T cells", + "B cells", + "CD14+ Monocytes", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD8 T cells", + "NK cells", + "CD8 T cells", + "CD4 T cells", + "CD8 T cells", + "B cells", + "B cells", + "B cells", + "NK cells", + "B cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "CD14+ Monocytes", + "B cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD14+ Monocytes", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "B cells", + "CD4 T cells", + "NK cells", + "B cells", + "NK cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "Dendritic cells", + "CD8 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "CD8 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "Megakaryocytes", + "CD14+ Monocytes", + "NK cells", + "CD8 T cells", + "CD14+ Monocytes", + "FCGR3A+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "FCGR3A+ Monocytes", + "B cells", + "B cells", + "B cells", + "Dendritic cells", + "CD4 T cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "B cells", + "NK cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD14+ Monocytes", + "CD8 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "B cells", + "B cells", + "FCGR3A+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "B cells", + "NK cells", + "B cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "NK cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD8 T cells", + "CD8 T cells", + "CD8 T cells", + "B cells", + "CD4 T cells", + "CD8 T cells", + "CD8 T cells", + "CD8 T cells", + "B cells", + "CD4 T cells", + "B cells", + "B cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD8 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "B cells", + "B cells", + "CD8 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD8 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "CD4 T cells", + "NK cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "CD14+ Monocytes", + "NK cells", + "CD8 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "B cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "B cells", + "B cells", + "CD8 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "NK cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "Dendritic cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "NK cells", + "FCGR3A+ Monocytes", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD14+ Monocytes", + "B cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD14+ Monocytes", + "B cells", + "NK cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD14+ Monocytes", + "FCGR3A+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "CD8 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "B cells", + "NK cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "NK cells", + "CD8 T cells", + "CD8 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD8 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "B cells", + "B cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "B cells", + "CD14+ Monocytes", + "CD4 T cells", + "FCGR3A+ Monocytes", + "B cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD14+ Monocytes", + "B cells", + "FCGR3A+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "B cells", + "CD8 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "B cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD8 T cells", + "B cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD8 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "B cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "FCGR3A+ Monocytes", + "NK cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "B cells", + "CD14+ Monocytes", + "CD4 T cells", + "B cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "B cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD8 T cells", + "B cells", + "B cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "B cells", + "NK cells", + "NK cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "B cells", + "CD4 T cells", + "CD8 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "NK cells", + "CD14+ Monocytes", + "B cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "B cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "Dendritic cells", + "CD8 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "B cells", + "NK cells", + "Dendritic cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD8 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "CD14+ Monocytes", + "NK cells", + "CD4 T cells", + "NK cells", + "CD4 T cells", + "CD8 T cells", + "Megakaryocytes", + "NK cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "CD14+ Monocytes", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "NK cells", + "CD14+ Monocytes", + "NK cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "NK cells", + "B cells", + "CD4 T cells", + "CD14+ Monocytes", + "NK cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD8 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "NK cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD8 T cells", + "B cells", + "NK cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "B cells", + "B cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "B cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "NK cells", + "NK cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "Dendritic cells", + "CD4 T cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "FCGR3A+ Monocytes", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD8 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "CD8 T cells", + "CD8 T cells", + "CD8 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "B cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD8 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "B cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD8 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "B cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "NK cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "B cells", + "Dendritic cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "B cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "FCGR3A+ Monocytes", + "FCGR3A+ Monocytes", + "CD4 T cells", + "NK cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "Dendritic cells", + "CD8 T cells", + "FCGR3A+ Monocytes", + "CD14+ Monocytes", + "B cells", + "B cells", + "CD4 T cells", + "NK cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "FCGR3A+ Monocytes", + "B cells", + "Dendritic cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "Megakaryocytes", + "NK cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "B cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "B cells", + "B cells", + "CD14+ Monocytes", + "FCGR3A+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "NK cells", + "CD14+ Monocytes", + "B cells", + "CD8 T cells", + "B cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "NK cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "NK cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "B cells", + "CD4 T cells", + "CD8 T cells", + "B cells", + "B cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD14+ Monocytes", + "Megakaryocytes", + "FCGR3A+ Monocytes", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "NK cells", + "CD4 T cells", + "Dendritic cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "NK cells", + "CD8 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "B cells", + "B cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "FCGR3A+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "B cells", + "B cells", + "B cells", + "B cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "B cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "Dendritic cells", + "NK cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "NK cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "B cells", + "FCGR3A+ Monocytes", + "B cells", + "NK cells", + "CD4 T cells", + "CD8 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD8 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD8 T cells", + "NK cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "NK cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "CD8 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "Megakaryocytes", + "CD4 T cells", + "NK cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "NK cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD14+ Monocytes", + "Dendritic cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "B cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "Megakaryocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "B cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "NK cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "B cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "Dendritic cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "NK cells", + "B cells", + "B cells", + "B cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "NK cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "B cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "NK cells", + "CD4 T cells", + "Dendritic cells", + "CD4 T cells", + "B cells", + "CD8 T cells", + "B cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "NK cells", + "Megakaryocytes", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD8 T cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "FCGR3A+ Monocytes", + "B cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "B cells", + "B cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "Dendritic cells", + "B cells", + "CD14+ Monocytes", + "B cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "NK cells", + "NK cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD14+ Monocytes", + "B cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "B cells", + "CD14+ Monocytes", + "CD4 T cells", + "NK cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD8 T cells", + "CD14+ Monocytes", + "B cells", + "B cells", + "B cells", + "CD4 T cells", + "B cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD8 T cells", + "CD8 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "B cells", + "CD14+ Monocytes", + "B cells", + "CD14+ Monocytes", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "NK cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD8 T cells", + "Megakaryocytes", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "NK cells", + "CD4 T cells", + "B cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "B cells", + "CD8 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "NK cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD8 T cells", + "B cells", + "CD8 T cells", + "NK cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "Dendritic cells", + "CD4 T cells", + "CD14+ Monocytes", + "FCGR3A+ Monocytes", + "B cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "CD4 T cells", + "NK cells", + "NK cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "FCGR3A+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "Megakaryocytes", + "B cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "B cells", + "CD14+ Monocytes", + "NK cells", + "CD14+ Monocytes", + "B cells", + "NK cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "B cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "B cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "B cells", + "B cells", + "NK cells", + "CD8 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD14+ Monocytes", + "B cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "B cells", + "CD4 T cells", + "NK cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "B cells", + "CD8 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "B cells", + "CD8 T cells", + "NK cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "NK cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD8 T cells", + "CD14+ Monocytes", + "B cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "B cells", + "B cells", + "Dendritic cells", + "CD8 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "NK cells", + "NK cells", + "CD4 T cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "FCGR3A+ Monocytes", + "CD14+ Monocytes", + "FCGR3A+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD14+ Monocytes", + "CD8 T cells", + "NK cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "CD8 T cells", + "CD4 T cells", + "NK cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "NK cells", + "B cells", + "CD4 T cells", + "B cells", + "CD14+ Monocytes", + "B cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD14+ Monocytes", + "NK cells", + "CD4 T cells", + "NK cells", + "CD4 T cells", + "CD8 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "FCGR3A+ Monocytes", + "CD8 T cells", + "FCGR3A+ Monocytes", + "B cells", + "CD8 T cells", + "B cells", + "CD4 T cells", + "NK cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "B cells", + "CD8 T cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "NK cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "FCGR3A+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "B cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "NK cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "B cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "CD8 T cells", + "CD4 T cells", + "CD8 T cells", + "CD8 T cells", + "FCGR3A+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "NK cells", + "CD14+ Monocytes", + "B cells", + "CD8 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "Dendritic cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "FCGR3A+ Monocytes", + "CD14+ Monocytes", + "B cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD8 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD8 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "FCGR3A+ Monocytes", + "FCGR3A+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD8 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "FCGR3A+ Monocytes", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "FCGR3A+ Monocytes", + "B cells", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "B cells", + "FCGR3A+ Monocytes", + "B cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "B cells", + "NK cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "NK cells", + "CD4 T cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "B cells", + "B cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "B cells", + "B cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD14+ Monocytes", + "B cells", + "FCGR3A+ Monocytes", + "CD8 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "NK cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "NK cells", + "CD8 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "B cells", + "B cells", + "CD8 T cells", + "B cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD14+ Monocytes", + "CD8 T cells", + "NK cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "NK cells", + "NK cells", + "FCGR3A+ Monocytes", + "B cells", + "CD4 T cells", + "Dendritic cells", + "CD4 T cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD8 T cells", + "B cells", + "CD4 T cells", + "B cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "FCGR3A+ Monocytes", + "FCGR3A+ Monocytes", + "CD8 T cells", + "CD8 T cells", + "CD4 T cells", + "CD8 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "B cells", + "CD4 T cells", + "CD14+ Monocytes", + "Megakaryocytes", + "CD14+ Monocytes", + "Dendritic cells", + "CD4 T cells", + "NK cells", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "B cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "B cells", + "NK cells", + "CD4 T cells", + "B cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "B cells", + "Dendritic cells", + "CD14+ Monocytes", + "B cells", + "B cells", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "FCGR3A+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "Dendritic cells", + "CD4 T cells", + "B cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "B cells", + "NK cells", + "CD8 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "NK cells", + "CD14+ Monocytes", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "CD8 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD4 T cells", + "CD4 T cells", + "CD14+ Monocytes", + "NK cells", + "CD14+ Monocytes", + "CD4 T cells", + "B cells", + "CD4 T cells", + "NK cells", + "FCGR3A+ Monocytes", + "B cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "B cells", + "CD4 T cells", + "B cells", + "CD14+ Monocytes", + "Dendritic cells", + "CD4 T cells", + "CD8 T cells", + "CD14+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "CD14+ Monocytes", + "FCGR3A+ Monocytes", + "CD8 T cells", + "CD14+ Monocytes", + "B cells", + "CD4 T cells", + "FCGR3A+ Monocytes", + "CD4 T cells", + "B cells", + "B cells", + "B cells", + "CD4 T cells", + "CD4 T cells", + "B cells", + "NK cells", + "B cells", + "CD4 T cells", + "CD14+ Monocytes", + "Dendritic cells", + "CD14+ Monocytes", + "CD14+ Monocytes", + "CD4 T cells", + "CD8 T cells", + "B cells", + "CD14+ Monocytes", + "B cells", + "CD4 T cells", + "Dendritic cells", + "CD14+ Monocytes", + "B cells", + "B cells", + "B cells", + "CD4 T cells" +] diff --git a/client/__tests__/util/annoMatrix/n_genes.json b/client/__tests__/util/annoMatrix/n_genes.json new file mode 100644 index 00000000..29a4fece --- /dev/null +++ b/client/__tests__/util/annoMatrix/n_genes.json @@ -0,0 +1,2640 @@ +[ + 781, + 1352, + 1131, + 960, + 522, + 782, + 783, + 790, + 533, + 550, + 1116, + 751, + 866, + 1059, + 458, + 335, + 1424, + 1014, + 1446, + 446, + 1020, + 417, + 878, + 789, + 510, + 824, + 1545, + 996, + 937, + 1368, + 428, + 406, + 1020, + 786, + 1019, + 750, + 822, + 982, + 876, + 930, + 838, + 1014, + 732, + 877, + 782, + 787, + 791, + 880, + 801, + 1215, + 343, + 1460, + 1250, + 756, + 836, + 824, + 827, + 1238, + 1243, + 1652, + 843, + 825, + 656, + 776, + 766, + 1465, + 790, + 871, + 803, + 965, + 800, + 876, + 690, + 988, + 906, + 741, + 620, + 867, + 916, + 969, + 803, + 732, + 555, + 790, + 862, + 900, + 674, + 397, + 663, + 563, + 786, + 859, + 568, + 412, + 1043, + 1206, + 702, + 1263, + 929, + 1079, + 938, + 316, + 900, + 919, + 862, + 903, + 390, + 1717, + 819, + 1877, + 660, + 791, + 478, + 769, + 481, + 819, + 866, + 600, + 1185, + 650, + 775, + 699, + 642, + 857, + 832, + 388, + 710, + 341, + 894, + 935, + 604, + 1008, + 985, + 679, + 603, + 864, + 1031, + 887, + 603, + 610, + 1119, + 669, + 794, + 963, + 756, + 637, + 1032, + 776, + 860, + 825, + 852, + 745, + 858, + 925, + 1228, + 806, + 715, + 668, + 779, + 1197, + 888, + 1273, + 873, + 847, + 781, + 959, + 805, + 554, + 604, + 785, + 978, + 910, + 936, + 997, + 961, + 1314, + 799, + 1112, + 677, + 775, + 1298, + 657, + 626, + 1313, + 467, + 936, + 977, + 780, + 1311, + 432, + 579, + 850, + 736, + 800, + 892, + 860, + 720, + 822, + 681, + 954, + 889, + 1265, + 919, + 799, + 833, + 496, + 1476, + 848, + 869, + 1059, + 490, + 897, + 832, + 864, + 419, + 856, + 907, + 791, + 756, + 771, + 957, + 1190, + 680, + 524, + 908, + 506, + 851, + 775, + 793, + 748, + 951, + 643, + 1277, + 828, + 480, + 969, + 1112, + 648, + 805, + 1223, + 1023, + 669, + 489, + 390, + 350, + 1113, + 837, + 1547, + 840, + 581, + 748, + 1861, + 735, + 488, + 1016, + 585, + 797, + 769, + 490, + 1307, + 895, + 686, + 602, + 772, + 704, + 892, + 1169, + 1375, + 1189, + 892, + 2455, + 355, + 1856, + 1317, + 703, + 825, + 736, + 1997, + 892, + 1034, + 545, + 1188, + 659, + 1056, + 819, + 979, + 632, + 598, + 690, + 310, + 803, + 743, + 560, + 1073, + 844, + 882, + 841, + 815, + 771, + 976, + 986, + 820, + 957, + 640, + 1012, + 927, + 794, + 753, + 791, + 366, + 539, + 752, + 769, + 650, + 947, + 771, + 824, + 744, + 837, + 723, + 640, + 923, + 1174, + 1597, + 699, + 618, + 1418, + 820, + 1047, + 981, + 866, + 527, + 762, + 717, + 860, + 603, + 828, + 476, + 1071, + 775, + 614, + 913, + 836, + 669, + 942, + 792, + 871, + 1046, + 859, + 793, + 822, + 751, + 435, + 1142, + 781, + 718, + 471, + 1750, + 892, + 841, + 1156, + 1031, + 912, + 873, + 824, + 1233, + 1312, + 575, + 605, + 717, + 1019, + 1215, + 928, + 1780, + 657, + 718, + 646, + 808, + 1120, + 750, + 390, + 1100, + 456, + 755, + 1118, + 571, + 867, + 728, + 916, + 491, + 960, + 625, + 1090, + 772, + 968, + 480, + 810, + 725, + 1016, + 1011, + 1075, + 808, + 672, + 950, + 862, + 766, + 963, + 507, + 570, + 678, + 768, + 1037, + 885, + 1426, + 1496, + 587, + 879, + 924, + 783, + 696, + 1057, + 783, + 867, + 919, + 1251, + 1023, + 727, + 645, + 1217, + 929, + 792, + 994, + 1025, + 946, + 600, + 881, + 975, + 1609, + 758, + 772, + 682, + 998, + 979, + 1045, + 706, + 808, + 855, + 819, + 1147, + 742, + 914, + 969, + 704, + 1398, + 581, + 809, + 921, + 805, + 542, + 888, + 519, + 1092, + 762, + 698, + 752, + 771, + 899, + 1101, + 760, + 881, + 1124, + 809, + 445, + 1703, + 789, + 641, + 819, + 890, + 767, + 806, + 1323, + 942, + 807, + 981, + 888, + 726, + 1190, + 826, + 661, + 713, + 816, + 822, + 806, + 864, + 464, + 664, + 931, + 860, + 674, + 803, + 464, + 788, + 1068, + 781, + 843, + 779, + 873, + 707, + 492, + 669, + 982, + 749, + 789, + 780, + 561, + 655, + 432, + 801, + 945, + 770, + 503, + 766, + 776, + 970, + 989, + 654, + 762, + 882, + 1236, + 1499, + 626, + 1380, + 1170, + 491, + 833, + 924, + 581, + 842, + 596, + 811, + 542, + 1572, + 758, + 854, + 274, + 1413, + 872, + 559, + 907, + 951, + 1175, + 946, + 905, + 737, + 604, + 843, + 606, + 1079, + 668, + 785, + 726, + 978, + 941, + 994, + 655, + 1013, + 987, + 591, + 1041, + 625, + 582, + 814, + 570, + 775, + 822, + 715, + 738, + 956, + 1178, + 743, + 1861, + 841, + 944, + 783, + 643, + 924, + 936, + 431, + 490, + 532, + 524, + 620, + 749, + 1334, + 834, + 790, + 702, + 892, + 693, + 784, + 944, + 471, + 839, + 529, + 729, + 1084, + 802, + 886, + 815, + 856, + 746, + 1318, + 545, + 696, + 872, + 1154, + 467, + 725, + 1027, + 479, + 728, + 926, + 1282, + 907, + 833, + 1024, + 838, + 900, + 737, + 367, + 459, + 1030, + 1279, + 756, + 662, + 1323, + 1003, + 359, + 770, + 813, + 634, + 924, + 1184, + 901, + 816, + 1421, + 771, + 706, + 953, + 348, + 716, + 870, + 715, + 550, + 689, + 947, + 1157, + 690, + 383, + 374, + 882, + 697, + 246, + 833, + 1006, + 1181, + 974, + 856, + 978, + 1551, + 965, + 907, + 565, + 417, + 907, + 927, + 966, + 658, + 727, + 743, + 381, + 385, + 905, + 645, + 1167, + 936, + 724, + 618, + 1038, + 853, + 808, + 1403, + 762, + 741, + 687, + 932, + 1096, + 601, + 652, + 895, + 682, + 1553, + 604, + 823, + 803, + 646, + 917, + 942, + 850, + 795, + 771, + 949, + 516, + 664, + 1001, + 637, + 619, + 907, + 961, + 812, + 793, + 1043, + 1343, + 1326, + 981, + 675, + 937, + 631, + 1026, + 1135, + 499, + 948, + 801, + 848, + 741, + 604, + 864, + 1076, + 1106, + 1111, + 624, + 1008, + 908, + 815, + 346, + 1062, + 803, + 749, + 779, + 1027, + 1032, + 1040, + 654, + 631, + 755, + 854, + 850, + 798, + 864, + 1078, + 690, + 864, + 1523, + 838, + 966, + 389, + 1654, + 808, + 885, + 1665, + 920, + 855, + 807, + 859, + 1276, + 987, + 1079, + 678, + 626, + 831, + 829, + 1009, + 547, + 893, + 722, + 656, + 415, + 773, + 1262, + 1218, + 365, + 661, + 805, + 1409, + 1094, + 779, + 898, + 830, + 1242, + 864, + 576, + 901, + 1274, + 852, + 1006, + 719, + 763, + 683, + 957, + 831, + 724, + 354, + 763, + 910, + 749, + 626, + 870, + 795, + 1078, + 736, + 835, + 402, + 1203, + 699, + 755, + 697, + 1229, + 637, + 666, + 846, + 1036, + 1027, + 831, + 999, + 942, + 891, + 1007, + 712, + 990, + 725, + 745, + 921, + 333, + 1042, + 895, + 873, + 1612, + 724, + 929, + 601, + 862, + 908, + 658, + 775, + 724, + 753, + 741, + 690, + 379, + 608, + 927, + 777, + 969, + 827, + 709, + 385, + 690, + 769, + 554, + 892, + 761, + 367, + 731, + 1103, + 944, + 832, + 675, + 652, + 418, + 727, + 745, + 872, + 1336, + 863, + 934, + 844, + 721, + 432, + 782, + 1006, + 834, + 840, + 840, + 819, + 699, + 489, + 665, + 576, + 1291, + 1102, + 826, + 880, + 738, + 904, + 686, + 874, + 887, + 873, + 560, + 766, + 710, + 1135, + 1054, + 805, + 724, + 973, + 1201, + 575, + 838, + 865, + 546, + 811, + 884, + 886, + 791, + 1026, + 2000, + 644, + 763, + 969, + 800, + 359, + 624, + 993, + 800, + 1167, + 833, + 1871, + 616, + 822, + 647, + 1000, + 618, + 734, + 618, + 1938, + 861, + 945, + 1032, + 723, + 984, + 994, + 771, + 738, + 1583, + 1113, + 614, + 1146, + 615, + 848, + 983, + 677, + 972, + 791, + 827, + 804, + 395, + 843, + 493, + 741, + 941, + 1659, + 742, + 1517, + 559, + 937, + 740, + 781, + 819, + 813, + 578, + 1022, + 1191, + 824, + 1146, + 757, + 638, + 830, + 713, + 609, + 1271, + 680, + 769, + 1119, + 731, + 804, + 781, + 916, + 735, + 835, + 1257, + 472, + 879, + 851, + 1023, + 661, + 1008, + 748, + 845, + 393, + 675, + 843, + 876, + 939, + 932, + 760, + 735, + 1561, + 752, + 940, + 705, + 405, + 690, + 1071, + 544, + 927, + 817, + 388, + 560, + 1322, + 640, + 886, + 1075, + 689, + 524, + 606, + 802, + 868, + 939, + 753, + 770, + 1105, + 841, + 786, + 445, + 703, + 593, + 875, + 901, + 927, + 798, + 1221, + 415, + 1381, + 949, + 1322, + 1169, + 745, + 727, + 799, + 490, + 767, + 943, + 808, + 926, + 664, + 569, + 843, + 727, + 1222, + 457, + 1515, + 1138, + 1174, + 525, + 878, + 525, + 999, + 778, + 772, + 819, + 1015, + 939, + 856, + 715, + 793, + 837, + 1193, + 764, + 834, + 677, + 625, + 420, + 837, + 874, + 679, + 811, + 546, + 787, + 587, + 821, + 669, + 813, + 780, + 649, + 924, + 1322, + 701, + 792, + 817, + 667, + 804, + 593, + 740, + 1243, + 859, + 685, + 596, + 1193, + 859, + 775, + 947, + 689, + 907, + 734, + 621, + 336, + 922, + 802, + 812, + 941, + 943, + 868, + 947, + 767, + 701, + 692, + 423, + 775, + 500, + 1100, + 812, + 728, + 616, + 928, + 835, + 454, + 812, + 769, + 1058, + 914, + 628, + 649, + 452, + 754, + 1327, + 776, + 670, + 1017, + 599, + 431, + 967, + 1077, + 2033, + 731, + 533, + 926, + 725, + 559, + 870, + 964, + 1341, + 1981, + 1103, + 326, + 428, + 808, + 821, + 554, + 596, + 680, + 1034, + 849, + 566, + 879, + 1091, + 823, + 447, + 1688, + 868, + 1254, + 942, + 462, + 1055, + 852, + 738, + 804, + 775, + 726, + 993, + 1462, + 1007, + 798, + 1036, + 786, + 948, + 743, + 761, + 838, + 1040, + 859, + 867, + 1188, + 846, + 687, + 672, + 629, + 725, + 660, + 809, + 469, + 600, + 812, + 856, + 397, + 786, + 895, + 882, + 449, + 890, + 823, + 1051, + 823, + 1055, + 741, + 999, + 1241, + 790, + 878, + 778, + 1066, + 815, + 465, + 1079, + 743, + 1098, + 807, + 1120, + 1025, + 805, + 676, + 828, + 763, + 997, + 852, + 866, + 1118, + 508, + 928, + 958, + 932, + 892, + 905, + 494, + 710, + 1068, + 795, + 787, + 951, + 720, + 842, + 890, + 1355, + 1005, + 872, + 1185, + 912, + 869, + 894, + 997, + 770, + 554, + 806, + 1426, + 1012, + 452, + 896, + 426, + 239, + 829, + 895, + 787, + 1139, + 925, + 1015, + 1360, + 1097, + 650, + 853, + 549, + 1052, + 307, + 1152, + 907, + 1628, + 731, + 897, + 1749, + 762, + 712, + 1195, + 851, + 864, + 968, + 845, + 331, + 840, + 734, + 948, + 842, + 1543, + 661, + 981, + 912, + 912, + 1063, + 683, + 823, + 996, + 695, + 1483, + 927, + 574, + 1052, + 571, + 1028, + 1263, + 671, + 958, + 747, + 866, + 896, + 489, + 643, + 923, + 820, + 1466, + 550, + 1112, + 1006, + 1448, + 727, + 899, + 998, + 563, + 870, + 903, + 516, + 754, + 879, + 588, + 740, + 798, + 798, + 653, + 902, + 990, + 724, + 953, + 891, + 1437, + 653, + 714, + 956, + 877, + 1012, + 824, + 1077, + 740, + 692, + 1063, + 771, + 808, + 1389, + 1264, + 952, + 816, + 795, + 795, + 760, + 886, + 349, + 868, + 842, + 819, + 626, + 418, + 903, + 838, + 723, + 436, + 1112, + 724, + 1299, + 719, + 843, + 1090, + 696, + 885, + 627, + 809, + 423, + 729, + 853, + 855, + 608, + 627, + 823, + 1063, + 575, + 743, + 1528, + 681, + 544, + 422, + 731, + 920, + 761, + 884, + 982, + 784, + 496, + 573, + 521, + 663, + 794, + 975, + 856, + 978, + 590, + 905, + 695, + 816, + 976, + 816, + 753, + 791, + 858, + 813, + 841, + 1085, + 1692, + 716, + 955, + 1467, + 741, + 296, + 738, + 1573, + 1119, + 918, + 283, + 703, + 842, + 1253, + 676, + 1636, + 1273, + 380, + 799, + 1491, + 878, + 939, + 725, + 1365, + 818, + 719, + 1343, + 905, + 837, + 803, + 990, + 1084, + 976, + 1630, + 795, + 1408, + 771, + 650, + 779, + 648, + 817, + 1127, + 882, + 954, + 830, + 732, + 783, + 756, + 708, + 976, + 718, + 887, + 809, + 795, + 662, + 912, + 1550, + 1509, + 1021, + 1751, + 776, + 910, + 714, + 530, + 846, + 631, + 1152, + 1118, + 755, + 573, + 1176, + 267, + 918, + 1132, + 849, + 938, + 1140, + 909, + 840, + 806, + 904, + 788, + 778, + 715, + 869, + 714, + 883, + 767, + 858, + 788, + 553, + 634, + 1230, + 1131, + 849, + 811, + 827, + 1753, + 713, + 484, + 783, + 722, + 596, + 514, + 372, + 925, + 747, + 840, + 673, + 955, + 796, + 719, + 718, + 857, + 578, + 1063, + 594, + 997, + 1268, + 341, + 388, + 753, + 834, + 593, + 1189, + 911, + 738, + 476, + 816, + 641, + 746, + 635, + 953, + 801, + 326, + 849, + 499, + 865, + 1420, + 487, + 876, + 797, + 981, + 756, + 850, + 1097, + 998, + 829, + 1159, + 955, + 1061, + 696, + 786, + 743, + 1211, + 893, + 491, + 744, + 1447, + 767, + 1050, + 853, + 1118, + 1428, + 555, + 612, + 854, + 789, + 889, + 784, + 712, + 1323, + 921, + 682, + 794, + 846, + 1012, + 839, + 807, + 587, + 881, + 781, + 1010, + 1182, + 1149, + 812, + 607, + 1155, + 714, + 642, + 1088, + 1291, + 1196, + 325, + 804, + 370, + 359, + 855, + 1165, + 836, + 923, + 863, + 1284, + 1011, + 889, + 984, + 512, + 939, + 883, + 697, + 1211, + 362, + 969, + 1135, + 1239, + 580, + 1103, + 975, + 825, + 1170, + 921, + 640, + 1180, + 378, + 982, + 916, + 1122, + 792, + 619, + 750, + 913, + 775, + 661, + 766, + 768, + 675, + 944, + 940, + 761, + 727, + 767, + 873, + 1043, + 850, + 995, + 680, + 595, + 700, + 753, + 736, + 891, + 685, + 780, + 986, + 989, + 830, + 810, + 685, + 784, + 642, + 742, + 961, + 906, + 829, + 621, + 822, + 717, + 1210, + 800, + 1963, + 749, + 757, + 570, + 831, + 721, + 336, + 802, + 1001, + 886, + 631, + 759, + 631, + 550, + 984, + 767, + 835, + 777, + 639, + 860, + 1413, + 747, + 779, + 540, + 367, + 1629, + 1380, + 689, + 1001, + 809, + 337, + 1103, + 796, + 966, + 782, + 1018, + 642, + 967, + 436, + 826, + 779, + 1000, + 601, + 796, + 945, + 1679, + 1123, + 596, + 995, + 720, + 588, + 759, + 452, + 780, + 836, + 515, + 846, + 392, + 283, + 710, + 1158, + 796, + 895, + 585, + 559, + 859, + 879, + 858, + 842, + 643, + 1308, + 595, + 1181, + 909, + 710, + 821, + 817, + 841, + 1197, + 640, + 1425, + 947, + 900, + 852, + 460, + 1100, + 824, + 780, + 932, + 542, + 1137, + 1225, + 997, + 572, + 780, + 765, + 906, + 793, + 753, + 772, + 854, + 936, + 1048, + 819, + 645, + 619, + 314, + 726, + 737, + 1162, + 1081, + 868, + 1032, + 913, + 476, + 490, + 799, + 1201, + 997, + 898, + 212, + 1586, + 427, + 947, + 937, + 724, + 380, + 715, + 739, + 931, + 973, + 773, + 1497, + 906, + 798, + 953, + 471, + 821, + 806, + 714, + 828, + 727, + 773, + 976, + 856, + 727, + 761, + 1084, + 1557, + 693, + 559, + 627, + 795, + 750, + 838, + 803, + 453, + 734, + 607, + 1029, + 805, + 669, + 505, + 858, + 832, + 1019, + 585, + 1225, + 1287, + 903, + 752, + 2020, + 774, + 666, + 843, + 857, + 887, + 1082, + 656, + 674, + 911, + 734, + 910, + 672, + 802, + 539, + 699, + 941, + 828, + 800, + 642, + 733, + 607, + 992, + 379, + 562, + 847, + 787, + 1461, + 732, + 941, + 785, + 696, + 795, + 809, + 828, + 651, + 882, + 972, + 1323, + 679, + 774, + 784, + 766, + 520, + 671, + 796, + 644, + 1549, + 756, + 723, + 788, + 643, + 856, + 825, + 730, + 831, + 653, + 429, + 641, + 637, + 812, + 1527, + 859, + 972, + 744, + 869, + 508, + 624, + 923, + 976, + 801, + 1014, + 1429, + 586, + 692, + 704, + 1176, + 806, + 883, + 1249, + 765, + 743, + 907, + 666, + 669, + 364, + 794, + 959, + 766, + 937, + 1398, + 942, + 1469, + 905, + 812, + 572, + 1378, + 1058, + 1215, + 697, + 531, + 676, + 1819, + 503, + 801, + 943, + 874, + 813, + 694, + 494, + 660, + 1467, + 976, + 833, + 689, + 921, + 625, + 1428, + 817, + 909, + 956, + 765, + 1207, + 829, + 1648, + 554, + 1500, + 953, + 647, + 537, + 786, + 814, + 761, + 862, + 838, + 1102, + 1392, + 1042, + 372, + 971, + 1364, + 1137, + 847, + 935, + 710, + 1070, + 914, + 855, + 759, + 654, + 981, + 1193, + 397, + 1123, + 616, + 747, + 876, + 949, + 965, + 789, + 752, + 717, + 1198, + 952, + 625, + 784, + 914, + 659, + 913, + 863, + 959, + 1637, + 796, + 1508, + 906, + 714, + 1195, + 867, + 819, + 375, + 656, + 1047, + 745, + 866, + 1186, + 1669, + 539, + 942, + 839, + 927, + 796, + 734, + 831, + 967, + 620, + 814, + 605, + 1391, + 655, + 1512, + 625, + 719, + 547, + 864, + 902, + 853, + 1143, + 990, + 858, + 604, + 1291, + 701, + 859, + 768, + 1621, + 715, + 594, + 783, + 1608, + 927, + 740, + 805, + 705, + 491, + 514, + 768, + 880, + 993, + 356, + 748, + 993, + 801, + 843, + 1194, + 794, + 606, + 810, + 882, + 682, + 1126, + 792, + 829, + 1657, + 786, + 610, + 664, + 790, + 852, + 678, + 589, + 930, + 690, + 840, + 647, + 622, + 427, + 856, + 522, + 1262, + 722, + 641, + 997, + 983, + 1099, + 607, + 704, + 947, + 886, + 1062, + 659, + 995, + 492, + 754, + 676, + 837, + 532, + 598, + 1274, + 921, + 700, + 828, + 828, + 543, + 815, + 709, + 842, + 1200, + 1126, + 1327, + 849, + 1160, + 707, + 675, + 709, + 990, + 270, + 445, + 892, + 1040, + 832, + 815, + 437, + 1401, + 1341, + 480, + 1517, + 704, + 622, + 823, + 731, + 781, + 849, + 1209, + 472, + 838, + 643, + 1101, + 879, + 895, + 408, + 832, + 845, + 928, + 920, + 1269, + 652, + 1463, + 840, + 706, + 956, + 1219, + 612, + 970, + 789, + 382, + 533, + 627, + 388, + 655, + 1103, + 782, + 1231, + 617, + 589, + 758, + 882, + 909, + 661, + 558, + 947, + 491, + 1386, + 1481, + 823, + 1064, + 681, + 652, + 363, + 824, + 933, + 611, + 886, + 806, + 531, + 864, + 648, + 1142, + 736, + 955, + 826, + 725, + 1174, + 841, + 1006, + 1418, + 955, + 829, + 1038, + 632, + 750, + 846, + 1033, + 859, + 890, + 893, + 1030, + 913, + 1385, + 750, + 416, + 703, + 522, + 856, + 914, + 742, + 381, + 1079, + 671, + 1101, + 858, + 663, + 581, + 845, + 881, + 823, + 835, + 1290, + 598, + 1130, + 793, + 838, + 765, + 768, + 815, + 810, + 912, + 818, + 790, + 665, + 703, + 875, + 657, + 1567, + 688, + 749, + 1076, + 782, + 543, + 1100, + 841, + 809, + 789, + 1263, + 758, + 368, + 1022, + 730, + 1101, + 478, + 488, + 618, + 940, + 771, + 784, + 847, + 1303, + 821, + 1111, + 752, + 1128, + 958, + 742, + 782, + 824, + 751, + 872, + 939, + 606, + 698, + 705, + 963, + 607, + 621, + 1650, + 1093, + 545, + 670, + 617, + 723, + 1194, + 1019, + 1291, + 758, + 855, + 1549, + 743, + 1372, + 802, + 337, + 1121, + 1028, + 1524, + 645, + 847, + 866, + 941, + 751, + 583, + 796, + 793, + 975, + 936, + 524, + 659, + 607, + 1433, + 562, + 696, + 927, + 517, + 719, + 599, + 639, + 977, + 1019, + 816, + 672, + 1903, + 1162, + 964, + 936, + 947, + 989, + 790, + 902, + 982, + 673, + 856, + 629, + 692, + 843, + 940, + 795, + 780, + 821, + 471, + 702, + 631, + 1557, + 868, + 901, + 798, + 1020, + 885, + 881, + 719, + 1043, + 1238, + 565, + 776, + 696, + 725, + 365, + 811, + 1212, + 1178, + 1132, + 661, + 831, + 786, + 471, + 835, + 564, + 929, + 958, + 706, + 388, + 842, + 965, + 1088, + 511, + 794, + 900, + 865, + 789, + 504, + 701, + 817, + 796, + 972, + 906, + 871, + 922, + 724, + 628, + 1479, + 533, + 1101, + 1913, + 855, + 1266, + 884, + 817, + 619, + 591, + 685, + 887, + 1336, + 656, + 1227, + 980, + 817, + 582, + 1370, + 460, + 638, + 471, + 650, + 414, + 907, + 1147, + 732, + 992, + 801, + 822, + 529, + 737, + 806, + 816, + 889, + 1305, + 588, + 657, + 1154, + 713, + 326, + 1129, + 1603, + 879, + 1156, + 642, + 285, + 825, + 823, + 719, + 1253, + 971, + 853, + 916, + 1053, + 515, + 1017, + 953, + 832, + 645, + 667, + 1326, + 547, + 636, + 1783, + 1211, + 788, + 807, + 1104, + 884, + 848, + 788, + 1013, + 1003, + 916, + 818, + 828, + 882, + 959, + 395, + 368, + 787, + 929, + 1379, + 711, + 733, + 752, + 464, + 626, + 735, + 946, + 876, + 647, + 536, + 954, + 486, + 712, + 786, + 438, + 807, + 1016, + 551, + 841, + 929, + 757, + 971, + 708, + 567, + 881, + 801, + 873, + 805, + 1359, + 866, + 945, + 1068, + 819, + 815, + 1058, + 845, + 881, + 1051, + 1179, + 718, + 657, + 882, + 709, + 754, + 735, + 603, + 944, + 1794, + 712, + 721, + 1097, + 813, + 788, + 917, + 656, + 1104, + 1268, + 1239, + 862, + 739, + 858, + 1066, + 752, + 615, + 721, + 571, + 861, + 933, + 807, + 1082, + 820, + 887, + 850, + 1567, + 803, + 1156, + 721, + 692, + 700, + 458, + 637, + 873, + 1544, + 1155, + 1227, + 622, + 454, + 724 +] diff --git a/client/__tests__/util/annoMatrix/serverMocks/index.js b/client/__tests__/util/annoMatrix/serverMocks/index.js new file mode 100644 index 00000000..a8ef1285 --- /dev/null +++ b/client/__tests__/util/annoMatrix/serverMocks/index.js @@ -0,0 +1,11 @@ +export const baseDataURL = "https://a.fake.url/api/v0.2"; + +window.CELLXGENE = { + API: { + prefix: baseDataURL, + version: "v0.2/", + }, +}; + +export { schema } from "./schema"; +export * from "./routes"; diff --git a/client/__tests__/util/annoMatrix/serverMocks/routes.js b/client/__tests__/util/annoMatrix/serverMocks/routes.js new file mode 100644 index 00000000..94fbe3f6 --- /dev/null +++ b/client/__tests__/util/annoMatrix/serverMocks/routes.js @@ -0,0 +1,211 @@ +import { schema } from "./schema"; +import { Dataframe, KeyIndex } from "../../../../src/util/dataframe"; +import { encodeMatrixFBS } from "../../../../src/util/stateManager/matrix"; + +const indexedSchema = { + obsByName: Object.fromEntries( + schema.schema.annotations.obs.columns.map((v) => [v.name, v]) ?? [] + ), + varByName: Object.fromEntries( + schema.schema.annotations.var.columns.map((v) => [v.name, v]) ?? [] + ), + embByName: Object.fromEntries( + schema.schema.layout.obs.map((v) => [v.name, v]) ?? [] + ), +}; + +function makeMockColumn(s, length) { + const { type } = s; + switch (type) { + case "int32": + return new Int32Array(length).fill(Math.floor(99 * Math.random())); + + case "string": + return new Array(length).fill("test"); + + case "float32": + return new Float32Array(length).fill(99 * Math.random()); + + case "boolean": + return new Array(length).fill(false); + + case "categorical": + return new Array(length).fill(s.categories[0]); + + default: + throw new Error("unkonwn type"); + } +} + +function getEncodedDataframe(colNames, length, colSchemas) { + const colIndex = new KeyIndex(colNames); + const columns = colSchemas.map((s) => makeMockColumn(s, length)); + const df = new Dataframe([length, colNames.length], columns, null, colIndex); + const body = encodeMatrixFBS(df); + return body; +} + +export function dataframeResponse(colNames, columns) { + const colIndex = new KeyIndex(colNames); + const df = new Dataframe( + [columns[0].length, colNames.length], + columns, + null, + colIndex + ); + const body = encodeMatrixFBS(df); + const headers = new Headers({ + "Content-Type": "application/octet-stream", + }); + return () => Promise.resolve({ body, init: { status: 200, headers } }); +} + +function annotationObsResponse(request) { + const url = new URL(request.url); + const params = Array.from(url.searchParams.entries()); + const names = params + .filter(([k]) => k === "annotation-name") + .map(([, v]) => v); + if (!names.every((n) => indexedSchema.obsByName[n])) { + return Promise.reject(new Error("bad obs annotation name in URL")); + } + const colSchemas = names.map((n) => indexedSchema.obsByName[n]); + const body = getEncodedDataframe( + names, + schema.schema.dataframe.nObs, + colSchemas + ); + + const headers = new Headers({ + "Content-Type": "application/octet-stream", + }); + return Promise.resolve({ + body, + init: { status: 200, headers }, + }); +} + +function annotationVarResponse(request) { + const url = new URL(request.url); + const params = Array.from(url.searchParams.entries()); + const names = params + .filter(([k]) => k === "annotation-name") + .map(([, v]) => v); + if (!names.every((n) => indexedSchema.varByName[n])) { + return Promise.reject(new Error("bad var annotation name in URL")); + } + const colSchemas = names.map((n) => indexedSchema.varByName[n]); + const body = getEncodedDataframe( + names, + schema.schema.dataframe.nVar, + colSchemas + ); + + const headers = new Headers({ + "Content-Type": "application/octet-stream", + }); + return Promise.resolve({ + body, + init: { status: 200, headers }, + }); +} + +function layoutObsResponse(request) { + const url = new URL(request.url); + const params = Array.from(url.searchParams.entries()); + const names = params.filter(([k]) => k === "layout-name").map(([, v]) => v); + if (!names.every((n) => indexedSchema.embByName[n])) { + return Promise.reject(new Error("bad layout name in URL")); + } + const dims = names.map((n) => indexedSchema.embByName[n].dims).flat(); + const colSchemas = names + .map((n) => [indexedSchema.embByName[n], indexedSchema.embByName[n]]) + .flat(); + const body = getEncodedDataframe( + dims, + schema.schema.dataframe.nObs, + colSchemas + ); + + const headers = new Headers({ + "Content-Type": "application/octet-stream", + }); + return Promise.resolve({ + body, + init: { status: 200, headers }, + }); +} + +function dataVarResponse(request) { + const url = new URL(request.url); + const params = Array.from(url.searchParams.entries()); + + const colNames = params.map((v) => `${v[0]}/${v[1]}`); + const colSchemas = colNames.map(() => schema.schema.dataframe); + const body = getEncodedDataframe( + colNames, + schema.schema.dataframe.nObs, + colSchemas + ); + + const headers = new Headers({ + "Content-Type": "application/octet-stream", + }); + return Promise.resolve({ + body, + init: { status: 200, headers }, + }); +} + +export function responder(request) { + const url = new URL(request.url); + const { pathname } = url; + if (pathname.endsWith("/annotations/obs")) { + return annotationObsResponse(request); + } + if (pathname.endsWith("/annotations/var")) { + return annotationVarResponse(request); + } + if (pathname.endsWith("/layout/obs")) { + return layoutObsResponse(request); + } + if (pathname.endsWith("/data/var")) { + return dataVarResponse(request); + } + return Promise.reject(new Error("bad URL")); +} + +export function withExpected(expectedURL, expectedParams) { + /* + Do some additional error checking + */ + return (request) => { + // if URL is bogus, reject the promise + const url = new URL(request.url); + if (!url.pathname.endsWith(expectedURL)) { + return Promise.reject(new Error("Unexpected URL!")); + } + const params = Array.from(url.searchParams.entries()).sort( + (a, b) => a[0] < b[0] + ); + expectedParams = expectedParams.slice().sort((a, b) => a[0] < b[0]); + + if ( + params.length !== expectedParams.length || + !params.every( + (p, i) => p[0] === expectedParams[i][0] && p[1] === expectedParams[i][1] + ) + ) { + return Promise.reject(new Error("unexpected name requested in URL")); + } + + return responder(request); + }; +} + +export function annotationsObs(names) { + return withExpected( + "/annotations/obs", + names.map((name) => ["annotation-name", name]) + ); +} diff --git a/client/__tests__/util/annoMatrix/serverMocks/schema.js b/client/__tests__/util/annoMatrix/serverMocks/schema.js new file mode 100644 index 00000000..fe995b05 --- /dev/null +++ b/client/__tests__/util/annoMatrix/serverMocks/schema.js @@ -0,0 +1,80 @@ +export const schema = { + schema: { + annotations: { + obs: { + columns: [ + { + name: "name_0", + type: "string", + writable: false, + }, + { + name: "n_genes", + type: "int32", + writable: false, + }, + { + name: "percent_mito", + type: "float32", + writable: false, + }, + { + name: "n_counts", + type: "float32", + writable: false, + }, + { + name: "louvain", + type: "string", + writable: false, + }, + ], + index: "name_0", + }, + var: { + columns: [ + { + name: "name_0", + type: "string", + writable: false, + }, + { + name: "n_cells", + type: "int32", + writable: false, + }, + ], + index: "name_0", + }, + }, + dataframe: { + nObs: 2638, + nVar: 1838, + type: "float32", + }, + layout: { + obs: [ + { + dims: ["draw_graph_fr_0", "draw_graph_fr_1"], + name: "draw_graph_fr", + type: "float32", + }, + { + dims: ["pca_0", "pca_1"], + name: "pca", + type: "float32", + }, + { + dims: ["tsne_0", "tsne_1"], + name: "tsne", + type: "float32", + }, + { + dims: ["umap_0", "umap_1"], + name: "umap", + type: "float32", + }, + ], + }, + }, +}; diff --git a/client/__tests__/util/annoMatrix/umap.json b/client/__tests__/util/annoMatrix/umap.json new file mode 100644 index 00000000..5d7134da --- /dev/null +++ b/client/__tests__/util/annoMatrix/umap.json @@ -0,0 +1,5282 @@ +[ + [ + 1.35285573560809, + -0.47802448287846216, + 2.165888749165179, + -8.69549315663449, + 2.0652175331122584, + 0.49520189144034144, + 1.051380238311061, + 1.8418954223210249, + 2.0126272839211943, + -8.406228786835502, + -0.7475822029589808, + 1.0398710174106818, + 2.4614340402375072, + -7.780092250827048, + 2.121988331057579, + 2.8140497988665834, + -9.117412304441292, + 0.7443418440268849, + -0.12612876310385598, + 1.1799221119905499, + -0.6887250690467789, + 3.0125133545646734, + -7.143523307396688, + 0.49794596192762824, + 2.1879318296159007, + -0.04074260948831929, + -1.6304621183653067, + 0.9185120650755632, + 3.330950131646921, + -8.249406782019472, + 2.9837694164624335, + 0.6013868697229028, + -7.973320868544591, + 1.2557275472097205, + -9.037432741704768, + 0.5272899942439975, + -7.3620383459740015, + -8.710739809962973, + 0.04565450184186127, + 0.9995702400422025, + 1.3707704392248314, + 0.4646969234541549, + 3.6660903522856345, + 1.5514822021133166, + 1.8014230367905788, + 0.8562655175966818, + 2.528521602131749, + -8.054206350208998, + 0.6986495744500447, + -8.360293645270179, + -7.028788335956598, + -8.117341523020128, + -8.975178374370575, + -0.12287461935615306, + 0.14568919159482138, + -0.22935322444119527, + -7.197623936888145, + 0.13018576439615953, + -8.262411494404354, + -9.10852755735242, + 0.7589399187829838, + 1.8111041361908446, + 2.0805181051825774, + -8.016584746538891, + 3.1365046821015947, + -8.174435180354182, + 1.8663362319091807, + 3.1219524797361493, + 0.6892426837009439, + 2.244499608696403, + 1.5623054194259811, + 2.3384273670301057, + 0.5378190418426231, + 2.7951515340684123, + -0.5755601568500485, + -0.5065756491480748, + 2.92485140197707, + 1.488221487102054, + 2.209257327297217, + -8.149923929399492, + -8.283361558328565, + 1.2593016566196622, + -7.614791259843514, + 2.0060290322527274, + -7.1957257296755675, + -0.6153994129631802, + 0.647621585176719, + 3.825122324575844, + -0.617206942026205, + -0.0723953060051589, + 2.7502344438270008, + -8.316773079712256, + -0.12703127087171923, + 0.032541047575098556, + -8.366227754298668, + -6.412773785556261, + 1.3622057178832003, + -7.233870565183863, + -7.394245052883867, + -4.525292336025702, + 2.10907364705061, + -7.274801711351849, + 0.4465992849099335, + 3.4309033386585783, + 1.9395204719917993, + 0.023946918636043358, + 2.833582913855396, + -0.3272931840397848, + 0.5946590476457169, + -4.118204094213762, + -8.85541744506898, + 2.4091434789774158, + 3.6121360006509553, + 2.574694350221088, + -8.386406382028346, + -8.087563939058514, + 0.4934831447839657, + 0.9560922248955092, + -9.01149783933102, + 0.8918896398008918, + 2.2792529871367955, + -6.807368181855979, + -0.016013842245326707, + 1.1749914467666223, + 0.5240606442534225, + 3.5680691256852217, + -7.131107288833797, + 0.5345528022913716, + 2.2504990630744834, + 0.43808793952614933, + 0.7054144783650844, + 0.010596177968814935, + -0.44663998860576865, + 2.365041933566587, + 0.8319562986581416, + -8.069079321353989, + 0.3509974873712663, + 1.997283773551994, + 2.4671002696984927, + 1.2639763865572942, + 0.5352041836925608, + 1.8841680192540224, + 2.905270971072112, + 0.1267833933956664, + 2.1242670578822405, + 2.6031580899982645, + -0.4312188043186363, + 0.9370138686596056, + -8.613621000724036, + 3.401549960736509, + 2.2160376306817655, + 0.9212426942869542, + 2.7430630793423205, + -0.5279990283280503, + -8.705539904925752, + 2.4868955732930313, + 1.9190472369020852, + 2.6080540180978486, + 3.2231396825311593, + -9.276648677138272, + 0.30053384369225394, + -5.1928361913065695, + 0.7561482591752087, + -7.571335606375206, + 1.046280867035641, + 0.1068853107329791, + 0.017020521216728503, + 1.8023357156616873, + 2.3724127779605406, + 0.9466561207261283, + 1.110832640530842, + 0.4122690719532064, + 2.5583580087383386, + -7.3448006321193695, + 2.7344695021869314, + -4.7003697238665945, + 0.20345204016224966, + 0.629245474557074, + 0.9436280743194153, + 0.1950924896942039, + -8.39321990754313, + 1.2933036946172936, + 0.7299735362981703, + -8.126373566878598, + -7.3662003803191425, + 1.6881029482355105, + 1.3907142000183166, + 2.5272762201075922, + -6.930322251680463, + 3.0775809978141795, + -0.07918514822520616, + 2.953742730539597, + -0.0017677535678961968, + 2.1886055501621033, + 1.7453622702042706, + 1.4951683077935347, + 1.9836262513675331, + 2.501592487882715, + 1.889625190366337, + 0.7353185031725933, + -6.550417322255917, + 0.7162273209727067, + 1.991404254201194, + -0.37267481858485996, + 0.5938391769786189, + -7.411420218588439, + -8.212939911852136, + 1.7480198752263079, + 1.370594139234737, + 2.6366163782948586, + 4.02933338975498, + 2.0320398581703496, + 1.6817961799238048, + 0.8887318601823143, + 1.8216291886789278, + -8.027511231072134, + -8.486856587037046, + 2.735438151582886, + 0.4550883264617053, + 3.3275023814892997, + 1.914579541768007, + -6.3584026650996535, + 0.8204178209761795, + 1.3315424166556709, + 1.3732448825623564, + 0.9586113508612994, + 1.2104802142030255, + 1.1705199569069644, + 3.2612705045348678, + 1.0492599729227439, + 1.3956439354853487, + -0.026480386394438813, + -8.989715611105217, + 0.8107151133592344, + -7.903104872958988, + 0.5039574785355939, + 2.4828705420315065, + 2.4115742227072956, + 2.8268743166688304, + -8.105979455165985, + 1.8751676391425214, + 0.47027295054406565, + -7.137759512944296, + 2.778992280455717, + -6.954649903722504, + -8.99402332832767, + 1.737009099149306, + -6.436508767115393, + 0.09142569258522178, + 1.4447896979783696, + 3.4407425738326562, + -2.6245975457760076, + 2.7538737258437753, + 1.4538389571409434, + -8.714958685582125, + -7.441383816313331, + 0.7510754908666685, + 0.3637562228618128, + 0.8399532151071638, + -8.08883603972419, + 1.6310142994433159, + 0.6705970298646083, + 2.1442899819450436, + 0.556478231632356, + -7.145388982529729, + 3.0647569182319536, + -8.977111424649609, + -7.711063727771787, + -8.833675637290202, + 2.1741483255925496, + -6.835536842196359, + 3.445543676407967, + -0.5822709787922326, + -8.080212990395578, + 0.713903340693877, + 0.40152969027500107, + 1.036963501913817, + -3.9835806018617936, + 1.4744144211922672, + 0.411592050621132, + 0.9364087576033505, + 0.1137082665706626, + 0.7301752132908366, + -8.266130955066824, + 0.2735006004760863, + 2.184378247744145, + 2.354302848998888, + 2.5485981453437163, + 1.2137100558133114, + -7.247752798634288, + -8.43194359917085, + 3.383507975280563, + 2.2450433484087418, + -9.051751477158193, + 2.8161048993789337, + -0.4262984388451197, + 2.464041660712454, + 1.994465005190346, + 0.22374471688588723, + 2.643110468109436, + 2.0702070585902668, + -7.581863095749053, + -7.512666918910498, + 0.6128565944808063, + 0.3835269276924121, + 1.6392539494894134, + 0.8320845595954978, + 1.5106365905385972, + 1.7720586658631, + 1.0291963010592182, + 1.6418052848350662, + -7.188246151127988, + 0.09198371062898206, + 0.5215544251331047, + 0.6371849805552529, + 3.1589727819067033, + 1.268813153780352, + 0.16681320384399848, + 2.1280869821189468, + -9.082202106655455, + 3.3172849637356663, + -6.590274113813707, + -9.111097213275565, + 1.8592883936797295, + -5.673139437865187, + 0.2980193119458896, + -4.389165578227743, + 1.8052387859523857, + -7.713983531304735, + 2.2016552664769455, + 3.3577319679959863, + 2.7035250626548364, + 2.8625973320310107, + 2.219272719282033, + 2.0324556166791155, + -7.081447243116417, + 1.9937689198645958, + 0.9748403495325826, + -8.927681326460663, + 1.3041703425278484, + 1.82168902095041, + -6.571880276673609, + 2.1953614271043365, + 1.3750668155272605, + 1.512305343143351, + 2.1735048519170364, + 1.2434736921614349, + 2.245753919843488, + 1.0011469888166296, + 1.5077489169341487, + 2.861366444731574, + -0.12975319864357043, + -7.759798128516371, + 1.0486401152193379, + -8.567409130119385, + -8.776709766409404, + 2.0523959720629676, + 0.9702463176224747, + 1.2464262446181968, + 1.2126888337289463, + -9.064260688467613, + 0.2681989849780224, + 2.0648972895331412, + 2.0441326659108716, + 3.0831011651576077, + 2.830334712439813, + 0.08059882892829817, + -7.234692017738882, + 0.6632578036213854, + 1.7306600879590341, + -4.864581457973878, + -9.091852992643224, + 0.07669833990740652, + 0.02245052822235808, + 2.8223097687713388, + 0.6186772967822212, + 0.5475132602144684, + 0.09821139171848503, + -7.1523470860208445, + 2.6794128585643873, + -7.894703748856715, + -7.997464605281789, + 1.9629575179131227, + 1.1035892676473205, + -8.637887747398574, + 1.2205319340415708, + -0.2938745402049933, + -7.00559485207825, + 2.818656762828998, + 1.3729772425199083, + 0.8097641508866747, + 0.8410347707617063, + 1.0747633751558396, + 2.773461678987149, + 0.3807626926184541, + 1.959034468961967, + 2.050909653235062, + -7.630535795118942, + -8.455389050665026, + -0.13534377253691748, + 1.0909180055505898, + 2.2125493767531754, + 0.05717055980198288, + -5.835409235490379, + -8.907589902674092, + 0.6672827627892893, + -0.49655582121382663, + 1.1645365854688123, + 1.034959444203318, + -0.4064455989921577, + -8.907457084740749, + 0.09626630962876037, + -0.30648636877226526, + -8.087071864621759, + -8.984939584746886, + 3.254164419936194, + 0.7377131144975152, + 0.12653586552659382, + 1.5657379656562431, + 0.16448804787290544, + 2.5266905700100337, + 0.20972239604758247, + 1.137118048499744, + 2.7570215436697003, + -8.655373197480369, + -8.228388519208059, + -0.5078345067119657, + 1.5229586758713414, + -7.971332052123057, + 1.6870682706355173, + -7.545705194606324, + -6.862974180738561, + 2.0236854098282038, + 0.33415671891411824, + 1.7267148126287122, + 2.698653301230399, + 2.8048534205439735, + -4.087625024003784, + 0.16022206804150776, + 2.841513133217909, + -0.5047976304668866, + -7.263502458048841, + 1.123338299216964, + -7.710038123638572, + 2.0799806194502155, + -8.73419889042598, + 0.6330292914853181, + 2.1243368489395085, + -0.43313493099211564, + 1.1823642197137227, + -0.42859023726553575, + -0.5091823498451181, + 1.9973258492370753, + -8.38836915444216, + 0.7375029392148328, + 2.1736812780490955, + 1.738419181698482, + 1.6883070352583138, + 0.5537049352527356, + 2.6506657614629896, + 2.8142428026527817, + 1.2235332414634719, + 3.397442150460223, + 1.030353665351954, + 1.3116442758866809, + 1.6452859224830017, + 0.5011618044043213, + -9.088557246507733, + -0.01240447994027973, + 2.581084952945822, + -8.463302117338767, + 0.7118590055640489, + -8.130625360247175, + 1.0834207438804553, + 2.640358178533733, + 0.5653084044265729, + 2.6702250700203165, + 1.6468270224796138, + 0.846414266068259, + 3.1615434834361, + -7.691410230354492, + 1.2557341558261315, + 0.20992282186654185, + 2.477869816844542, + 0.3771536857972718, + 1.9971442195608315, + 0.12915305927423792, + 3.2829579226358123, + 0.07537013445606805, + 2.3092976438959476, + 0.2329424582448878, + 0.8357888209020299, + 1.2180893842173306, + 0.04856351346397796, + 3.5744979379043267, + 1.669535155953666, + 2.3207366356258663, + 0.32681234021190014, + 1.5151106657587101, + 1.8281001077630386, + 3.831754713753577, + 0.8185537560162401, + -5.962281540225824, + 0.9255740617320212, + 0.7820050943170265, + -7.9288721156215605, + 1.0142181524892333, + -7.707662624985399, + 0.6503250943985074, + 1.380100923803612, + -8.05200622560956, + 1.6043286625304296, + 0.8451562069463898, + 0.943690736995706, + -6.704163983926268, + -7.651675754191798, + 2.8069842371494023, + 2.9804034094200733, + 2.512406670381577, + 1.6303008295865888, + 1.2743826657141013, + 2.968429649588173, + 0.07981202025095484, + 0.4659555505286844, + 2.4135861375911265, + 0.3470879379696273, + 2.096925716384769, + 1.7104732337010358, + -8.93244670648019, + -9.054077418131602, + 3.3272023035340923, + 1.9533029770509784, + -0.3578635861118119, + 1.7387635157985106, + 1.0622233414382256, + 1.166330984649243, + 2.290374493758918, + 1.6007386358604836, + 0.4069471299462136, + 3.183141909139203, + -8.289505399777722, + -8.735818891934889, + 1.1927270785407693, + 0.8117335091610367, + -6.839778391731439, + -8.560621059700626, + 3.2327524190054517, + 0.2351280054174659, + -8.069124435396784, + 1.5934052451891958, + -8.016886503130472, + 2.5166474443118187, + 1.8077703556852782, + -6.89917040548509, + 2.331295274393149, + 0.028341084904075293, + 2.45294414025638, + 2.4982955100934663, + 2.6528746304421698, + 0.86949660097944, + 2.9663214606223556, + -0.3283375313402265, + -0.38263116649533446, + -8.617634243641117, + 0.9891606519058725, + 0.11258801303709674, + 2.5995061442041325, + 1.1101383528502828, + 1.7915850107514966, + -0.39955038419449096, + -8.952822233799061, + 0.4338327371453445, + 1.5069996136757637, + 2.2547717788501167, + 2.7922905917041314, + -6.966528492028682, + 0.2707484789662109, + -8.526624100991679, + -4.546150834872335, + 1.968289743381796, + -4.085814940218094, + -8.75651859188767, + 1.0492010807174819, + -8.467274974407134, + 1.593124734813376, + 0.3481380884273988, + -0.15583301716388614, + -6.592603378805345, + -7.117952854897646, + 0.6178371906010927, + 0.9282037231419841, + -7.644178100661795, + -6.847530121470798, + -8.612602760529702, + 1.9615040056293946, + 1.0813198807975406, + -0.07916973209749419, + 0.22878581671923293, + 1.5852329652618449, + 2.6568979004682602, + -8.247382625035774, + -7.31905533769166, + 1.7230334499316773, + 2.1442150754629377, + -0.3913867311853231, + -8.565731523795018, + 0.48836673876161385, + -8.550981123529034, + 2.0765156901835993, + 1.1791232493554893, + 0.4628133554809247, + -8.570612481576456, + 0.31743916249521, + 1.3689304528387372, + 0.9202159446125323, + -0.2270501708321309, + -6.940721954427759, + -7.061116366059591, + -0.2987105617995114, + 3.283450614839531, + 2.1621752246384274, + -8.016471862737987, + -8.167882170647548, + 3.0123025587277055, + -0.12776957890467389, + 0.6576203647895076, + 1.2206369385103255, + 1.7771949646291936, + 0.870129676006453, + -6.896514945865017, + 2.72395569606518, + -0.708821266359798, + -8.178817525909968, + 2.599012631508349, + -7.4413028157194345, + -8.860103070305422, + 3.0123938603834906, + 0.6840626894874334, + 0.10459117875634981, + -0.3227860929645202, + -0.11296946986870815, + 1.8379339717420056, + -7.758420382491858, + 0.2711735110428051, + 3.008850738872426, + -8.328873089908148, + -0.6434888208653092, + 1.381037343424551, + 0.4312276140533374, + 0.5825181663460871, + -6.970927901949955, + 2.0695117930134517, + 0.6152340280892129, + 3.49595199368453, + 0.6938727172986299, + 1.4475996148194237, + -8.609142018851355, + -7.7772768844184315, + -7.544894885797945, + -6.908331226910449, + 0.9089285567242396, + 0.9387047714748711, + 3.7148527852348368, + -7.2476532791433765, + -8.998549396936156, + -6.595387338748019, + 1.438191259922225, + 2.288214765306113, + -0.014760414413903546, + -7.770974287483711, + 0.6453677444470884, + 2.087141400809658, + -8.01699871457284, + 2.400436142220324, + 0.9203163748065302, + 2.9250246221734177, + 1.739337623547591, + 1.0263075400063275, + 1.2133803601560305, + 0.8477548670296504, + 0.2776668814036649, + 1.476863079748618, + 2.38986676603255, + 0.8190010540303145, + -8.600849114974137, + 2.466931763065777, + 0.6942828631671454, + -0.48472949679486216, + 1.3690461720825393, + 1.1386794631201784, + 2.3000790452898423, + -0.5737801992789094, + 1.4894503198246298, + 2.20744903509327, + 2.346612854012468, + 1.9262174442593494, + -8.518158953023475, + 1.2307221290531465, + 2.1206736732011553, + 0.4899710406891571, + 3.1006385409976778, + -7.521213500124812, + 0.5237439927790518, + 1.1519603194807617, + 1.6256078327550763, + -8.914362686096426, + -7.845587362754366, + -8.485506628202467, + 1.9268753736882878, + 1.3412107413635537, + 1.7626357558833163, + -0.3848726650847515, + 2.0535608777636822, + 0.6386486562532143, + 2.1668214225250875, + 1.7621364606735226, + 0.144604733364891, + 3.2287546365823094, + 2.1172647727359086, + 3.2498858720172166, + 2.910690182828211, + 0.5560038424597556, + -5.053249259330138, + 0.1267741317413434, + -7.7520600294259765, + 3.0099939457558884, + 2.530862687511373, + -7.569045043727168, + 3.100572766345718, + -7.598035533924212, + 3.0812254019580134, + 2.665871025642842, + -7.783751872917371, + 0.7792923984254838, + 0.13550411979575336, + 1.9555605803234042, + 2.8109919605641784, + 1.4302939303354323, + -7.077889093884513, + -8.81451549884432, + 1.7948047097881592, + 1.3136659105045494, + -0.05431808661282797, + 2.1745446641766275, + -6.744529238152495, + -7.639481164896453, + 1.9508598546238556, + 2.994506265077541, + -8.047126695755605, + -7.9055428000056445, + -6.880506675107962, + 1.2064549161519937, + 2.40303126836893, + 3.721222591763574, + 1.0392622357574917, + 0.989513670148371, + 1.111295715578283, + 1.5197712593885788, + -0.003830666980349542, + -8.871502646042059, + -7.710228767479039, + 1.316567130437144, + -8.153346603694768, + -0.3065364380133689, + 1.1701046228289196, + 1.4383032441394334, + -5.3338371145364105, + -0.4210221997984218, + 1.6444079959956204, + -8.877769334741437, + 3.103492802343518, + 1.516321164532454, + 0.04550566552419573, + 2.0988285005728082, + -8.805073663059847, + 2.238823922089626, + -7.760574953561919, + 1.7399929534231842, + -6.362716239042421, + 1.342770941232623, + 0.2942419947671681, + 2.460133192304222, + 3.1160109542981256, + -7.863404589522134, + 1.339783928604639, + 3.1828691359625, + -7.419356727249834, + 0.7064775403365423, + -0.30887312014924456, + -8.456035013610844, + -6.53454968512149, + 2.178316715644698, + 1.6842159505158856, + -9.021975554572357, + -7.507648493713097, + 3.202200540495128, + -8.100426309998456, + 0.9594422746731098, + -8.845494271765292, + 1.175056685814502, + 0.04214137587627019, + -0.5673660022469825, + -9.20656186730306, + 2.973187278981339, + 0.5737719406547798, + 1.3587860979146245, + 2.7985813475724375, + 0.7094710290003688, + 2.0531183191182882, + 0.8511068042526803, + -8.590135642397662, + -7.189965033714168, + -7.154677422733394, + 0.19090233780148586, + 2.931853633383166, + 2.2561671508640466, + 0.901416475523869, + 1.7027262016250704, + 1.971219776112316, + 2.4174107152765125, + 3.0487575561656595, + 2.924163384267368, + -7.054310537828337, + -8.28566569244672, + 0.7085209499196015, + 2.6833955075498936, + 0.8698284742955038, + 3.0152347189355497, + 1.2579402799717339, + 3.2603225427799205, + 0.1782881730598257, + -0.42615362191586, + -7.6490739603896785, + 0.3077973439238357, + -8.900953410878762, + 0.20201870504381775, + -8.423384087563129, + 2.722527284583259, + 1.3910530785859019, + 0.3946761807726644, + 2.8332292593118793, + 0.8936884956897678, + 1.1787387461648193, + 1.567348877300945, + 2.2045001284416883, + 3.3631224988915234, + 0.04816168052311782, + 2.1389438147027393, + 2.731851521918433, + 0.6069927084759039, + 1.298579162371399, + 2.476184318231664, + 1.607135746688546, + 2.873246076164953, + 0.20254332006482556, + 3.2014673304770636, + 0.6497769827541435, + -0.9030778286503153, + 3.431642966558377, + -0.5037706052510608, + -0.2718177142045583, + 2.3794794835113224, + 1.5218843153608947, + 2.7417262156712914, + 1.5111841271116933, + 3.3016623670243375, + -0.8018726566463645, + 2.0817782033989625, + 1.4761472522587435, + -0.3830622843308061, + 2.96125695796714, + -6.458820226724634, + 2.8812453262878064, + -7.737636277265019, + 0.47866633406865, + 0.7686251627279825, + 0.6575443015225664, + 2.717223225558806, + 1.4511419170513968, + 0.5653414107484783, + 1.7957559372188365, + 2.802780980402567, + -7.803092629931442, + 1.869341396799709, + 0.8432598728970127, + 0.7274576465338105, + -0.056347721327875196, + 1.8111595457186436, + 2.012187082121611, + 2.6766884228160452, + 2.2395446030241155, + 2.0220035695704683, + 2.551669192710943, + -7.084275988118852, + 3.2160814880125, + -7.512818306561267, + 0.2651693233580314, + 2.709930601235107, + -8.25308000044569, + -7.931544018548265, + 0.611974022845457, + 0.6022362872430828, + -7.881514129199376, + 0.2976247285209178, + 2.425138890802724, + 2.6235880841735737, + 3.035755884725319, + 0.08067025748216224, + -7.378277352583638, + 2.684047451077735, + 2.1405945720061563, + -8.180315407048122, + 2.8092973588605585, + 2.0990750518716936, + -7.957518989117321, + 0.47518320727383545, + 0.3367399214742779, + 2.2771983564378764, + -7.800606427520901, + -0.7464765130831107, + 1.448488093446764, + 1.712299710929907, + 2.3905323834778778, + -8.509083225983312, + 0.5528745670823761, + 0.056218070305737056, + -0.6790425174878887, + 0.3472145623189714, + 3.0225355309737423, + 1.633286832099552, + 0.5266672630777911, + 3.6459537790447833, + 2.236649898911319, + -0.40944473333247927, + 0.6850113779650434, + -8.21619992893973, + 2.1304260290468564, + 2.723864737812909, + 1.3399722497086435, + -6.775697936934701, + 2.3964616584226683, + 1.3842313378533297, + 2.7615107981919227, + 1.7408744101042368, + -7.738718786684235, + -0.919610965593588, + 2.0792344954104793, + 0.9850629393893267, + -5.201540497852601, + 0.1823978516841003, + -0.22885521322494967, + 0.21747576894502027, + 2.19322258510467, + 3.2986334553791505, + -8.619684862683377, + -8.309541341330789, + -7.835321833014975, + -8.764712564101124, + 0.4055112088733752, + 0.09600089149065433, + 1.0886691275686606, + 1.0145068198945755, + 1.0888323325702034, + 2.0683632581969045, + 0.9895286989042258, + -8.600637855511625, + 0.17563402960720662, + 2.9883877363976623, + 3.0891431470855233, + 0.4400202797717173, + 2.029800770955799, + -9.112364152978662, + -7.851845284401957, + -8.887578129757253, + -6.59489218895868, + 0.21776659706458837, + 1.9634443338982246, + 1.0414843328983996, + 0.469215937663879, + -7.298687916443551, + 0.7135889748814117, + 2.4196631407822804, + 0.3076025091214727, + 1.3017289266943042, + -7.020706662519961, + -7.268765471299113, + 2.1993518896259134, + 1.3378718213097844, + 1.1520314971425436, + 2.4649510119124662, + -8.44771242798184, + 2.148046492351158, + 1.454621827239103, + 3.0266238416161153, + 2.2834842529185346, + 1.6493444110044522, + -0.2807564957450657, + 0.9869961857361259, + 1.196311234640384, + -0.03138504857722978, + -8.302173262858318, + -7.147662968691874, + 2.4203753479775765, + -7.200195535346051, + -6.121405179007928, + 1.446540869790915, + 2.4946091536424513, + 2.5317860291442043, + 3.08885700076078, + 0.686600318537168, + 1.9712547495650867, + -0.3406283631713244, + -0.1917046102063393, + -0.34626361744265993, + -7.146658637459202, + 1.3912626769594616, + 2.101908290357388, + -8.951955330528236, + 1.3382607486557812, + 3.0197774381417752, + -6.705803972980068, + -6.319106555034937, + 1.3228667939258831, + -7.181379021616384, + 0.8231254810629439, + 1.6096906991986024, + 0.022087670079872737, + -6.322223274773663, + 1.9142490369319745, + -8.8395741689585, + -6.7066508601620445, + -7.861254754341756, + 1.1913256927324036, + 3.3503200114900453, + -0.002659230132193009, + 0.7771927695492519, + -8.499741678847203, + 0.17494165662536784, + -6.35141558349304, + 0.7622898073252403, + 0.4285016657869558, + 1.149946839332002, + 1.7000250524900118, + 0.609892291556713, + 2.2464330993860964, + 1.2761432007451525, + -7.398300341150756, + 2.5135528106361136, + -8.818914883272242, + -0.5170330719567374, + 2.1053277386851357, + -9.129669203870366, + -7.274116432853194, + -9.138206918075909, + -0.17477826504362012, + -7.755516593671772, + -8.460406129073084, + 1.615219452962977, + 1.0557400143215432, + 1.3559188758553349, + -6.746612576630017, + -8.427016362142876, + 1.5538988907147384, + 0.9101032737721856, + 1.1443464196683368, + 2.548440430962473, + 0.2644421770757484, + 1.0395390384388348, + 1.3279202568763526, + -8.753212949759881, + 1.4004389951274052, + -9.186945950420203, + 0.1958855237232713, + -7.954922238118083, + 2.9082245870181613, + 2.3287302443115694, + 2.6926289649200346, + -8.148594251795902, + -7.839235694550561, + 1.2882149500697657, + 0.11489142233158645, + -7.791723998974786, + -8.273808442110104, + 1.4749481617406814, + 2.308452285743202, + -0.19107627716397657, + 2.0404769868284305, + -7.985717654331616, + 1.2107905054682147, + 2.7728430561860087, + 2.3684759764589596, + -7.816121343046642, + 2.1400333002462677, + 1.3579178690393816, + 1.4137790688956473, + 2.0282163403938696, + -0.32634073086743637, + 0.13456806137205918, + -0.07652955293384209, + -8.729145156318214, + 1.2956587419826326, + -8.051340353153803, + 2.7730771407884194, + -7.954770826966083, + 0.6108161558439745, + 0.4636852274811087, + -6.185525634375222, + 1.9723213500676102, + 1.1661462020860072, + -0.127179504501362, + 0.22175017909053582, + -8.439166693494329, + 3.6310621292827308, + 1.1202246125640114, + -8.696202059696393, + 2.4645177606532807, + 0.03848449495185448, + -7.061346258741949, + -7.880999495057979, + -7.652141323360686, + 1.9346967782332938, + 1.8519134375341029, + 1.045358025225612, + 1.867882738618223, + -0.29347325176852873, + 0.8858271759641015, + -7.369184353233702, + 0.10839110864985028, + 1.1973366351322996, + 0.932204017410818, + 2.2627473995350016, + 2.947677434897453, + 0.9432060861694127, + 0.05395284885089928, + 0.889687036155159, + 1.7276491304778132, + 3.4987625677755085, + 2.346194337240592, + 1.4351247091556645, + 0.8923018476658329, + 0.8265583627496746, + -7.870220121771367, + 3.1760069390670287, + 1.8509322432764934, + -7.808546169673091, + 2.8237190425263408, + 2.946952661753927, + 1.7308140156045613, + 0.9977879893701315, + -0.5708308107304996, + 0.07889506960533402, + -6.643437055613775, + -0.56528230535463, + 0.870224700285937, + 2.6916260628152835, + -7.828681811589555, + 1.3279773734160638, + 2.8973836999999003, + -7.929287638220337, + 1.1193036378595262, + 3.594043711978328, + -8.13911330674277, + -8.90486254564379, + -0.2957064939222399, + 0.8663816353200503, + -8.521579935310447, + 2.3157669071904206, + -8.119140992084624, + 2.6021021719824646, + 2.2603643030736924, + 0.8741761862261239, + -0.06006143611101942, + -3.1598465288412205, + 0.7185513031937687, + 1.1488660787139893, + 1.439320893331675, + 0.6558899732621498, + 1.3376290043476202, + 0.33866355369779044, + 0.49100124614685187, + 2.0732762735938026, + -0.17658301058699613, + -0.15211639502969043, + 0.26358826632899807, + 1.5898050628603841, + -7.571158183163497, + 0.5023378932875282, + 2.5664867528077773, + -7.752126581163239, + 1.5202060246679063, + -7.586353065525089, + 2.458743346964622, + -7.117655044831394, + -0.2766314552971195, + 1.4995385114633468, + 2.047751662904562, + 1.7034799222273926, + -0.20718681963969204, + -0.09499902849411072, + 3.231440450246778, + -5.725631186022914, + 1.152706025263952, + -0.8436966252912544, + -0.42918929142871276, + 2.537290305930872, + -7.628772256548188, + -7.374689762871147, + 1.061013263717064, + 1.962147458108563, + 2.3280301886732393, + 1.24919179774868, + 0.8767430061555562, + -8.06549023313639, + 1.0800306557640134, + 3.1190590402572766, + 2.7199558294175286, + -7.759909637482053, + 3.189223805081286, + 2.7779181975289386, + 2.3847605509285104, + 3.329954274336646, + 0.9938937020826742, + 0.4984620320293087, + 2.831849103378229, + 2.265552368615562, + 1.6462215082838754, + -7.707758932585142, + 1.0978755588155906, + -7.9210876752988835, + 0.5379123234590063, + 1.8476018914385226, + -8.715940582759224, + 2.2899960983145, + -7.010182624606673, + 2.1097220914326766, + 1.8062889811487706, + -8.667243244723918, + 2.5551923428204635, + 1.7707424165846375, + 2.917138503950332, + -0.6786053381483399, + 2.7201581612918875, + 2.7011790013409924, + 1.2685343752484828, + 2.609497641079454, + 0.328462313212367, + 1.8540862208537516, + 0.9111878083785402, + -0.28426319299110525, + 1.928068219117086, + 0.9823570695344068, + 0.8036169357123898, + 0.8638327636608106, + 1.5631105979370687, + -8.579141238483885, + 1.9726952954724573, + -7.98143064290487, + -8.411479380206911, + -0.31646714121733316, + 1.9135385993316474, + 2.089089710673739, + 1.9960922223992736, + 0.6950167358804251, + 3.075747505339304, + 2.340950812292796, + -8.993901506596375, + 1.3335940745655013, + 1.5029656732086905, + 2.547186244573038, + -7.177198157100625, + 2.020910148648964, + 0.575026245669857, + -8.722887642994142, + 2.3931476392962545, + 1.9883487781163314, + 3.185344417621772, + 0.4611860136178839, + -0.5182075979179117, + -8.30489727021605, + 1.8201609543537527, + 3.0689388476915873, + 2.173698425464305, + 2.706005868873169, + -0.618837066000761, + -8.14167270676203, + 1.1838694232674125, + 0.5789388369320245, + 3.504126154437088, + 3.588433303842934, + -7.5506588361234215, + 2.061290460031166, + 2.646008007089369, + -8.528312183408428, + 1.7989478462750759, + 2.3431068088473297, + -8.607046603987758, + -7.233462314309418, + -8.236210998622116, + 0.7601890495346417, + 0.8199533451747715, + 2.8988379269433366, + -7.2460258547667395, + -0.04260741710161611, + 2.543539577935952, + -8.565529430160256, + 0.8452684387141972, + 0.02137488235097795, + -0.4171229078550339, + 0.5329862737316927, + 2.429445635061916, + -8.702289720690896, + 0.27413419442950115, + 0.778047647796611, + 3.1213402599631923, + 0.6452591727752793, + -7.067189783892758, + 1.9131896533628667, + -8.563014949448029, + 2.5352804472036112, + 2.2290075909502245, + -0.3470878959227531, + 1.105668261238746, + -8.376087342183945, + -8.15269169773546, + 1.3711035698490532, + -0.36426272454991426, + 0.7551506444748286, + -7.757463803645644, + 1.5490100734886154, + 2.313830351044823, + -0.8541111915958685, + 0.496654227916699, + 2.659244403352773, + -0.4430331751416259, + -6.629243243300451, + -9.01836562393277, + -8.267930482302894, + 0.3592826993210026, + 0.10107159656091669, + -7.887902440252809, + 0.5279084501674415, + 1.2915579555085936, + -7.355768275596031, + 2.469362161506793, + -7.102518289138929, + 1.852060918877644, + -8.718197701309421, + 2.965847630372054, + 1.6860490514540332, + -8.510593223376, + -4.536688807086601, + 2.0334442262280485, + -0.3426201204341113, + -8.453688176881816, + -8.342615611274992, + 0.9115626260883183, + -8.66913823054388, + 3.211134229958071, + 1.5064447559646312, + 2.425614930885676, + 2.1285963080005565, + 3.6280943058081268, + 2.3581021902785757, + 3.443999940056457, + 2.4524429553642957, + -7.630602134495716, + 0.9749722474485204, + 2.023063317973442, + 0.4449547911800854, + -0.6651643924572769, + -8.977257191576923, + -0.09940376983580983, + 1.0983446316458962, + 2.957566880493749, + 2.231581703132769, + 2.992648636070979, + 0.10029857579906186, + -0.10486224630613837, + 2.7052214883187347, + 1.7321038147581467, + -8.553642928754616, + 0.7678399545232438, + 1.7369801350368201, + -8.959210380086098, + 0.39429857882813435, + 1.5829811856819702, + 3.2572325544113223, + -8.304367400379208, + 2.3880265079951997, + 0.9446753401638908, + 1.8016424209164952, + 1.8749987305003346, + 2.5381615514093196, + 0.9140264280810966, + 1.6888308847238722, + 1.4701940930143365, + 1.3672739186583218, + -7.978877810221675, + 1.751007663957888, + 2.762920710051229, + 3.1207677452514786, + -8.294409328210083, + 2.118279801483606, + -9.136774027043517, + 1.2960232456627068, + -7.6203490396486115, + -7.170062781547793, + 1.4606818503734946, + 2.254454243477766, + 0.4888493896618929, + 2.445951735545381, + 3.130901934841017, + 2.0169064244671624, + 0.5781320235978845, + 1.164559165113405, + -7.085727391465785, + 1.5563134560808463, + 2.5246786453408587, + 1.8306790539513156, + 2.063158354285914, + 2.951222072815112, + -8.011694407902795, + 0.6912192194209621, + 1.7567867998704312, + 1.227745741829922, + -0.15483148523896537, + 3.080062309803283, + -7.014075101217134, + 1.7153006739574652, + 0.42563776715604157, + -0.20460280950116075, + 0.08112706131989966, + 2.3249243480067583, + 2.8599522371258654, + 2.8250697629458648, + 1.3263597524146535, + 0.6689904965545804, + 1.7211640556971661, + 2.664252570995877, + 1.9021155590745604, + 1.3039480575116782, + 1.6186179551129143, + 0.6994200226046031, + -0.2762234251771253, + 1.6463651245781266, + 1.714335455602332, + 2.863930022411937, + 1.5345625659420514, + 1.9874458198165799, + 0.6236563963009678, + 0.5487365154798838, + -7.699812241244133, + 1.9022132491593855, + 2.0582611608232324, + -8.241695983493726, + -6.6537679124807, + 2.1727184060924447, + 0.9273336782436994, + -4.084132513497494, + -6.170446784167851, + 0.16227905402765097, + -6.5818959164282065, + 0.3856228859005982, + 1.0640002227515153, + -7.938741293583762, + 2.093082240549366, + -8.7906152886363, + -8.901765187372959, + 3.8337110169924444, + 2.1154827591984047, + -0.024563247863549253, + 2.265518351996009, + 1.9006504722937423, + 1.917766167905046, + -8.633956025491374, + -8.851789595046611, + -0.38659176008223367, + -8.87236621013963, + -8.501176765733607, + 1.9191111526959272, + 2.0092982704073235, + -7.981956809324846, + 1.4206166475637727, + 0.6387858496083604, + -4.240260019432114, + 1.3929661181125392, + -8.64662315068952, + -7.421202866081349, + 0.007880371938225328, + -0.19056302410489345, + 0.503120685047802, + 2.1688687868026357, + -8.292235911623273, + 0.4841239485526286, + -0.21587616980271407, + 2.6559586365930166, + 0.8458314061382362, + 2.06133046178105, + 2.6770161025917525, + 2.2054628111229193, + 0.15780246248383983, + 1.8897321611408149, + 1.1275903344847946, + 0.7875862341118278, + 2.1134528176796077, + 0.043938642482980586, + 2.215455707867413, + -8.456452178054775, + -8.692009213476931, + -0.5101357762192221, + -4.421699075118912, + -8.284672622364882, + -8.836080267171257, + 0.2811631865005029, + 0.7142526358338925, + -0.30874882508807805, + 1.7719153462076291, + -7.779768112165598, + -0.2753804205635559, + 1.9260020511696359, + 0.05227095637109941, + 1.1788794487568979, + -6.726659315471383, + 1.6706662165839776, + -8.947952635047871, + 3.162863553794859, + 1.5389964899535564, + 1.2544440603559768, + 1.7145010953616646, + 0.4822413648004758, + 0.8802826978220544, + -6.928476477946392, + 0.10778270716980459, + -0.39126373206777293, + 0.8622491527567213, + 0.31010488292438115, + 2.03192764830168, + -8.796503623860978, + -7.25126358436173, + 0.3351051631162634, + 2.401849776359339, + 0.9593991430845032, + 0.28888122690848994, + -7.88160986575865, + -8.795296799403625, + 1.0757305446731973, + 2.1924085737667283, + 1.0972505543442912, + -0.2774989094254504, + -7.066242301005415, + 0.11467791494875802, + 1.6091405789212163, + -7.260729719065053, + 0.33316620169517214, + 2.205281933674402, + 1.172124004482102, + 2.4696000397910645, + 2.3730224401479516, + 1.3335890617350563, + 1.7094335595127936, + 0.8208981431645013, + 1.8647377935509077, + 0.36747321127457044, + -7.060084906231471, + 2.7804890302269643, + 2.9671177107298687, + -7.505782900572595, + 1.0474434372261008, + 2.9724405167414307, + -7.635142339082416, + -7.217456677559582, + 2.727741118573167, + 0.8465807600627744, + 0.7523149050591599, + 0.5201613891793713, + 1.925880550199435, + 1.9625870722855867, + 1.010200439428451, + 0.22032048692175552, + 0.7273647588069262, + -0.1355918572976582, + 1.345341378000972, + 0.5800264441178827, + 1.380656425038407, + 1.8792469186637635, + -6.514298843285813, + 2.099765193816464, + -7.509190765310397, + -6.890897830401605, + -9.222846612291947, + -7.172078812604409, + -7.789712879680477, + 1.254734993818503, + 0.5531895666629048, + -8.612093971837751, + 2.0864710531435913, + 0.45494547747797875, + -5.508790997390626, + 1.1149993703403722, + -8.0440977364811, + 1.6559663293874263, + 2.420212513816951, + 2.9853181372794784, + 1.515004941287756, + 2.547898257908868, + -6.622344884313547, + 2.279783867318617, + -7.461243943269229, + 1.083537216055773, + -8.539160423577666, + 2.5671093694551743, + 2.5120642770654174, + 2.7201046348614373, + -8.83034854050044, + 0.40813570966610907, + 0.1478548923910506, + 1.378802988881486, + -8.145853074840295, + 0.5859367793324454, + 1.2685315732446376, + 0.5816188712363226, + 0.6475648145998375, + -9.170946475716914, + 1.1255854211133745, + -7.204255242285411, + -6.735249012873698, + 2.835288408826582, + 1.3975350610213035, + 2.0655171510622794, + -7.142744911960321, + 2.5084973350342827, + 0.39401025608934637, + 0.8413376253131408, + -8.22411101126183, + -8.316357387285601, + -8.301731394638491, + 3.1850216861049936, + 0.31145632933967327, + 0.7490086616134807, + 0.16499663357513214, + 1.3340694243972384, + 1.067740313249043, + -8.778641494106067, + 0.5323884586681293, + -6.791677941954464, + 2.7181197902959324, + -8.120989910120539, + -6.566977462156488, + 0.19971778956130795, + 1.5142333116934048, + 3.049490121824, + 1.1362102180694054, + 0.388842475083235, + -9.19938011247477, + -7.435297148324332, + 0.5219091136830705, + 1.6676803894699457, + 2.9213805693889197, + 0.6423880183415991, + -5.6180731048586905, + 3.041626787445567, + -6.6873096359841275, + -7.085387574060344, + 3.074449590208328, + -9.049286753289348, + -0.07955063623519049, + 2.547868234063361, + -9.028291326562696, + 0.7130986820012628, + 2.132638517739885, + -9.065414093828675, + -0.021756493080876613, + 0.6630241370437974, + -8.042827548943682, + 0.12705662340916574, + 0.9458870828880522, + -0.3662118100714234, + 1.0640708262928484, + 0.7090274222032339, + 2.7672877694347844, + 2.766279528804059, + 0.1334718113791702, + 1.11615265120768, + 2.6488368076661493, + 2.2412978981691953, + 0.12403072980622999, + 2.5653841751762845, + 2.46840596735832, + 0.7664635895912416, + 2.1808954147589534, + 0.7780720929790442, + 1.154606096196686, + 0.10572028977310916, + 1.2851076877131016, + 0.556425323239768, + 1.0685018048754145, + 0.7454887942125477, + 1.7527547535834673, + -7.546222752678161, + 1.3532650448586065, + 2.3714247164495292, + 1.982143701618346, + 2.618916661184878, + 2.640381933720139, + -8.385071772460382, + 0.7444984479293888, + 1.5068169150034114, + 2.2271220796396216, + 2.7368767412298602, + 2.417331566583442, + 1.1871226601686244, + -6.650805662509953, + 1.8867538961050732, + -8.809438191940412, + 1.0340198085047216, + 0.09741528294006592, + 1.1144739131504813, + 3.568290861071225, + -8.675318927552368, + 0.27057758173492047, + 1.5034740911958426, + 0.6576685640069088, + -7.970029234276875, + 2.0543143183721093, + 2.4976178443593477, + 2.592234390534009, + 1.2794420349507243, + 1.3280661268357454, + -6.1397431101450515, + 0.6493502392033413, + 2.451702099349213, + 0.3856119997859295, + -7.846337417047921, + 2.198060056284684, + -6.961349711979518, + 1.7437468720369593, + 3.0464861871275026, + 2.257505905763807, + 0.30885351917698495, + 0.8297613717282993, + 1.5747759023875523, + 1.317957570410962, + 2.553616904435545, + 1.424238444833736, + -6.409821252587784, + -3.7121101731513755, + -8.07988081118465, + 1.1581201368122398, + -8.736322415995028, + 0.6153135417031852, + 1.1851950640560585, + -8.04273204084384, + 0.3753515125893134, + 0.6044410800186297, + -0.4462806348390067, + 2.4876025235421375, + 0.4243786377495357, + 2.397867885817016, + 3.042072995902169, + -7.042235067429063, + 1.4433894825003244, + 1.3503899983375744, + 1.9267961475043072, + 0.11303028424955176, + 1.2802237387762023, + 1.1807097565574458, + -8.396811638245762, + 1.966221028236108, + 2.670565206957964, + 1.941463422585001, + -7.301573843511433, + -6.791358138871878, + -7.753009533802501, + 2.0725098091559078, + 3.3508596926505434, + -8.336750014567741, + 1.328999925878136, + -7.498577814850508, + -6.909957244961768, + 1.6808666342979746, + -9.259426955145779, + 1.0529248929166835, + -8.659503064520303, + 1.0794283442346126, + -8.169354608391833, + 1.2687417154610972, + -0.05008806804418893, + 2.554466483058769, + 2.6463387561425358, + 2.0915278486625852, + 1.963493051516514, + -0.058111812310782905, + -6.719162782261123, + 2.1286532096533546, + 2.6711102253818306, + 0.8113490399413341, + 0.1691688087031465, + 0.7576914618941437, + -8.573974536623778, + -8.202351570300284, + -5.022030620598906, + 0.7378700433168435, + -7.896595642041391, + 2.715954389554796, + -7.328039581111833, + 0.4656709743306232, + 0.38780981951749893, + 2.715794355311965, + 0.6832808179661064, + 0.8165883110436294, + -0.7747965792331853, + -8.73586196187189, + 1.4133836545434357, + 2.772935925322485, + -8.717567004542799, + -7.534740709216023, + 2.925460725713395, + 2.8751008445351975, + 3.0793788378304163, + 2.1533793279196245, + 1.7978212944486598, + 0.7107077365239324, + 1.0427976193825639, + 1.7330570013400586, + 0.00556742814150668, + 1.5612831466067632, + -6.734421807039429, + 0.4013219184744089, + 3.5262979904457143, + -8.95170377984456, + 2.2141611432455224, + -0.08047619438343041, + -5.957196390708477, + 3.3226888221987836, + -0.37803085074796233, + 2.645414596563064, + -0.33862855976270245, + -8.46555558570551, + 2.76345646211871, + 2.40494295752824, + -6.840982915756616, + -0.3242636234143703, + 2.7750295264645297, + 1.8183999533967665, + -7.678438800179548, + 1.0160749550942851, + 1.7137173475535832, + 2.8083713130665635, + 3.1604421650794694, + -0.4229217013580974, + 2.1765062672188935, + 1.0311177140396095, + 0.6027953301818604, + 3.2893079766983124, + -8.419775319138095, + 1.1517915087767054, + 3.806484015949818, + 1.813709085411365, + 1.586889829445234, + 1.0553305281082588, + 2.0387604323716473, + 0.46255061828788563, + 2.5403778903660448, + 0.8978741659153907, + 2.9667459702613748, + 1.7935809280277752, + 0.9979198425330458, + -7.348823524408095, + -8.190100691652388, + 1.013482899172069, + -8.176589705475472, + -6.776804544087918, + 2.5175129624009247, + 0.07453293651955747, + 1.1677142732080015, + 0.8121530043524006, + 1.0728087393996888, + 1.3207866981412948, + 1.391487660213971, + -7.325262871111656, + 2.480512446508523, + -0.6726147334600983, + 1.286633878671537, + 1.2848571814950263, + -8.630068144410178, + 1.641074512334856, + -8.372520390263729, + 3.6220477954119166, + 1.5476109871904122, + 1.3313072985443402, + 0.4509802411964847, + -3.676572896300967, + 0.3431658561343909, + -7.394169280242893, + 1.0111153209989923, + 0.6236978299182561, + 3.2232768877654876, + 0.9889212142907509, + 0.9557662078999765, + -0.16552310968824946, + 2.557882614305319, + 3.170986388211581, + 0.562007121308636, + -0.6890649793533657, + 2.5864702274219007, + 3.147873301590315, + 1.8452327103988686, + 2.2300578670635, + -7.914572431346472, + 1.681388435406869, + -7.934181576720307, + 1.213953350024886, + -7.767409167529324, + 0.6125353497428956, + -5.862074779777948, + -0.05579432135103433, + 1.86187714887813, + -6.831639403701684, + -8.368366805004245, + 0.7141650842394128, + -7.2296156596447805, + 1.9658449157112, + 1.203402773136181, + 3.151848920524179, + 0.8134834215896044, + -0.0015891696015058621, + -6.6206322071046335, + 2.442034190696451, + 1.4642792894623475, + -7.662456653055396, + 0.5578606457466265, + 0.4827129085038648, + 0.2447768553210342, + 1.2446398205960199, + -0.25148268369407345, + -7.866482447918518, + -6.778773007540573, + 2.364212567590591, + -8.876829124962727, + 2.3709111578808897, + 2.443004766387172, + 0.7629475448244297, + 2.5660739576610623, + 1.543324993772585, + -7.445412828672346, + 1.3108578718592787, + -7.38077750258055, + -0.06035745322128779, + -8.065274811827726, + 1.3228399598913307, + -6.855601930353441, + 0.48550708554397876, + -8.118421982992933, + 0.47680284185583893, + -7.882497296555905, + 1.250031921419141, + 1.9815759668868294, + -7.84848669377249, + 3.367580290061463, + -8.440463565239842, + 3.0765713208166643, + 2.047624797367175, + 0.4889432739407685, + 2.5096694366000163, + 1.5221815582718397, + -0.24634193428253054, + 1.9915302143198261, + 0.6635624893207551, + -7.891419294916788, + 1.5075850215925033, + 1.2568996983393395, + 2.378860296841117, + 3.1394575024244022, + 0.37361594939970977, + 0.935152027531952, + 3.047339993260869, + -6.773412066573279, + 1.0136520227701125, + 1.6683344851535156, + 1.3800348556849296, + 0.3710586489799465, + -8.01667358521836, + 0.990252834741967, + -9.126682421524748, + -9.193666922759986, + 1.9428961494619439, + 3.587663110515121, + -0.2622805993300625, + -8.393066513474231, + -8.596771623489156, + 2.302138698806313, + 2.0258880548889793, + 2.6910996993473884, + -0.6734939697369002, + 1.04114747684359, + 1.718185888051403, + 3.48276539461964, + -6.95854436686443, + 2.4735697481156858, + 2.041149990149579, + 0.673791469689127, + 2.9897117569477327, + -8.238988517507169, + 0.9761093805477352, + 1.8216332855723016, + 2.2164179615841872, + 0.4932126968317748, + 0.3780496352396801, + 2.0649711404519855, + -0.3613929635545015, + 1.530645089629263, + 2.457243579429912, + 0.05440985134375729, + 0.06784140843088526, + -0.003004518576022715, + -4.447565313413029, + 0.7159244478392003, + -7.4391021004275935, + -9.10853187457136, + 0.34616954373534803, + 0.3874840426360299, + -0.32695879149184703, + 0.7751933946282066, + 2.9773628087798647, + 2.2142437878977925, + 2.1843254988850855, + 3.059126414689936, + -7.692981165498031, + 0.3024228093713769, + 3.605203578070868, + 1.4644727704422869, + -8.727072293408847, + -8.091615408220388, + 0.4629390055507127, + -6.980247924896281, + 2.4171511912056944, + 0.9091940581067055, + 0.045724673301188894, + 3.0929852250357976, + 3.198135004353189, + -0.21094478484423296, + -8.564324258125765, + -8.810215664648535, + -6.815470170564254, + 0.9382340965406358, + 2.558848589992109, + -6.860407582762542, + -0.11677156318837643, + 1.7632932116246454, + -0.567984892256506, + 1.268764912958375, + -6.793588858602195, + 0.44299312590662765, + -6.859297675764476, + 2.587620793885912, + -6.352486573862255, + 1.1729864504462661, + 2.340260731160978, + -8.048569125070872, + 2.318283003374852, + 3.4853185969834053, + -8.250982909735939, + -7.642972426754199, + 3.2704918520039516, + -7.899950558873467, + 1.412053293639085, + 2.454183942959644, + -8.471969075893853, + 1.8275377932192616, + 1.1836780899054724, + 1.3203521358289407, + 0.9678677193752282, + 0.481491658469547, + 0.8447740588469119, + 0.9658229991595761, + -0.24011170869178097, + 2.839010771182523, + 1.9624349399608014, + -8.599242423056895, + 0.4211472361023467, + -0.2793320314444414, + -8.797108635586614, + 2.9392182781603906, + 1.256873152990501, + 1.5665471944473703, + 2.6547265873722368, + 0.8105761486188217, + 1.721884808291741, + -8.234430197275405, + 0.5536636270100801, + -8.355106987612643, + 3.178140091519325, + -7.73626088974614, + -7.144415677325785, + -6.691323046710537, + 0.18838902967814972, + 1.3810650697628872, + -8.528176568166815, + 1.8735027217692781, + 1.8177810237061083, + 1.8528978825048201, + -7.6581702947911285, + 0.9851936282135941, + 0.9749140655529156, + 3.4804614557444093, + -8.271591218158985, + -0.0692916061467144, + 0.23636074444602018, + 2.2536748103998634, + 0.6620572261158134, + 0.5272863441054563, + 0.31987648412936154, + 0.7902815603429416, + -6.85058804260707, + 1.0999584013545434, + 1.859646243461103, + -8.559557059496289, + 0.8252105446203934, + 1.886734510600049, + 1.6134861199944395, + -7.985644994531114, + 2.971573949701723, + 1.3672364573922098, + -0.3682007887540835, + -7.103789187918907, + 0.9405168733953734, + 3.4871526094460066, + 2.1859096620649496, + -0.06289084203356823, + -0.10258851143346698, + -0.18803971518510462, + 0.8634144021978678, + 2.1582894982893395, + -0.4677742138351482, + 2.0928793604794955, + 1.2878926947686777, + 2.336889592470472, + 0.6872067448511159, + -7.748766216039927, + 3.097444263728701, + 0.5556866987589535, + 2.6712301633728353, + 1.1849033710043753, + 0.5486668885120461, + 1.0907831266305195, + 0.8434242995293644, + 2.945520310117043, + 2.8605173091238214, + 2.19486511424655, + -8.827037046270771, + 0.8032457482846421, + 2.7703795059818543, + 0.0388776722783144, + 1.2645413629366868, + 2.1265867903136657, + 2.3385576793976584, + 1.917127276522154, + 1.0529020988066884, + 2.0148206403888405, + -8.191291663926195, + -0.12305657129402683, + -0.04938015097326366, + 1.3313074227837138, + 2.649367557322003, + -0.08737222278800186, + 1.8643940520501083, + 0.33554885638604737, + -0.10208068750017399, + -3.351687711644993, + 2.2491187462351876, + -7.264665631287486, + 2.468811775400256, + 2.236940253230926, + 1.4395837747782287, + 0.5756193294246122, + 1.566682837365336, + 1.1941344117585693, + 0.7864333853514603, + -9.130530246586128, + -7.542220059862066, + 1.57835349664291, + -0.5198230637738277, + 2.367130441925575, + -7.508216068087835, + 0.3251150387191636, + 0.4305638446644724, + 3.576750227255939, + 2.926957105266908, + 0.6489723337676349, + 2.6600070511561866, + 3.0978307529860145, + 2.14332749047063, + 0.8417453308787288, + -8.362638563146596, + 0.7655484851530803, + -7.358857148523173, + -8.795863146335122, + 1.7528113987581404, + 2.1996806032770424, + 1.1842707222509872, + 0.3370356510506722, + 1.5657862898662531, + 0.9954573129035333, + -8.362659182252077, + -8.572523895071708, + 0.6623382018746384, + 0.6994445665772647, + -8.812671581275692, + 0.7069222566198479, + -7.0821163329383, + 1.9549163979002904, + 2.7673886265006495, + 1.58020571085722, + 1.1157305393370385, + 1.8192078696921687, + -8.648738166417022, + 2.628514031659668, + 2.307267931960636, + 0.17720288819135216, + 1.7561274604591817, + 0.09524221875625612, + -9.01315111179075, + 0.6047127666500285, + -7.244699265066096, + 0.350818800626781, + -6.8329565403043935, + 2.2955591146423098, + 3.0182115153169815, + 2.3746049023107285, + -7.019640140605208, + 2.4647840205414395, + 1.3434268505839981, + 1.6854086164192068, + 2.295861317921914, + 2.2764302909930985, + -7.983364933412937, + 0.5807929594978267, + -8.894143291256958, + 2.9938838722469345, + -7.0788633945319415, + 1.2178067680325075, + 2.856905931248516, + -8.868555023102441, + -8.111576071477934, + 0.7355491495970233, + -8.884594494490006, + 1.3008471541908617, + 2.0846825428565143, + 0.940236823123737, + 2.6584950353466343, + 2.5762583674418824, + 3.3819401974882135, + 3.0910570249281393, + 1.2778711559145604, + -0.4827457256339073, + 0.8455691575019935, + 2.989985556284473, + -8.594252280966447, + 1.9197413450145182, + -7.46547909931198, + 1.6722748789866753, + 2.2138221464125243, + -8.270938951734585, + 1.7386397987695124, + 1.033678879096028, + -8.516503001822748, + 0.512090110414948, + 2.0059308250635013, + -7.781026228664235, + 0.36210339688697013, + 1.8975077592436573, + 0.7694797196827183, + 2.9175852118329306, + 0.35192891316867225, + 2.7464548788808485, + 1.8181456629054111, + -8.879105287057802, + 2.364591270774948, + -8.811648789232835, + 2.2303404870811443, + -7.212500783453163, + -0.04252695108275349, + -8.27691641027849, + 0.17389279156214596, + 0.3419299559039324, + 1.1168557615318893, + 2.6300729259371325, + 0.519545977271745, + 2.026006377830216, + 2.4542416541761796, + 0.6214882222636962, + 2.56545276551517, + 2.371782875552455, + -8.410901915894607, + -7.755180617297871, + 0.26854433319712717, + -0.29016912404990974, + 2.7855698716380655, + 0.7031441386051922, + 0.6463634663828767, + -0.14572491547363256, + 1.0846660011879703, + 2.9877269537925093, + -7.4014817343606, + 1.532485901854579, + 3.2834185214330414, + -8.395186846378069, + 0.7067177076339348, + 1.790307058312988, + 1.9045140645897747, + 0.8155319473452132, + 1.558527430207653, + -0.4562439874529775, + 0.6283683976770774, + 0.3170889013577815, + 1.2269672091211687, + 0.13239083090554576, + 0.9672200895075614, + 2.3228852138079854, + -4.508240674846544, + 0.937926825482767, + 1.9403937731548746, + 2.5476778851631727, + -8.791030377657798, + -6.90900624236735, + 0.38668188599048714, + -8.387398417504254, + -7.344755132587228, + -8.401990418987419, + -7.427545106366351, + 2.052933392737336, + 1.243724639000523, + 2.492261060779638, + 0.9113773906922337, + -0.15816106323304419, + -7.470553956059105, + -8.846441596666375, + 2.827116680018038, + -7.116712997211687, + 1.2433205851368418, + -8.346468787028506, + -7.978333067918487, + 1.172963260744282, + 0.2608874203114955, + -0.46724686695630874, + 1.0564086871641414, + 0.06130467277244044, + 1.6818877741501426, + 1.318743028841478, + 2.8373390653938273, + 2.576615766795672, + 1.2681807565928347, + 1.4580249782652615, + -6.524433867154633, + 0.09581451622485024, + 1.8231985588689075, + -7.358029518015586, + 3.511196746259249, + 1.8394281143396174, + -0.6754116281933468, + -0.020197099799179743, + 2.1780440921947566, + -7.409891359292506, + 1.052901385861119, + 1.2241086946853335, + 0.3665532298720331, + 0.14478725159505287, + -8.151941019411806, + -6.704773956752906, + -6.115756625251118, + -6.974690256772126, + 2.0210836961226892, + -9.015001772676642, + 0.2646431501384486, + 1.6940258171839286, + -0.2610635405056007, + 1.7344415107250657, + 2.4462330102590086, + -7.068860147884828, + 0.08695837348604589, + 1.8701982293321116, + 1.0504262384847716, + 0.019527985748346775, + 0.5097245787288074, + 0.2389915874027862, + -0.19218159389880246, + -8.855488867463228, + 0.6020616642969194, + -8.09265510730711, + 1.3150371364975184, + 1.4759405666726864, + 0.6125503265197201, + 1.0338508449971633, + 0.8355074008305553, + 1.5657388390288403, + 0.19060171190029251, + 1.9216391475271553, + -8.552212424405148, + -0.10872450136022829, + 1.5803257663654455, + -8.312807240378005, + 0.19254420609740558, + 1.5921086618162703, + 3.0724652883798753, + 3.119585347470178, + 2.3680946054804948, + -8.996751873936851, + 0.5450779135596553, + 0.3631979793991185, + -0.08367913106684004, + 1.523933920210135, + 1.9337098908579557, + 1.618362115241489, + -0.2920987527409687, + -0.08027388569056067, + -8.473382618231554, + -7.768478821668898, + -7.782876606094697, + 0.12206627215748748, + -8.662712369595276, + 0.7044770310142159, + 1.2240212148539857, + -6.580456328124205, + 1.0167328126861905, + 2.898843091197227, + 0.587714076153449, + 0.6216570749055097, + 1.0414748533858058, + -9.219472775085773, + 2.2001939905709453, + 2.094708624460661, + 1.746527945940157, + 3.03051313566259, + -7.249968841919131, + 1.3408108007543746, + -8.166942766681114, + 0.979736597403027, + -7.324885568806586, + 2.6278233426622917, + 2.854107404015036, + -8.315196696814988, + 1.64949805448383, + 0.9702802532651041, + 0.7306766790583936, + 0.5492725402351939, + 0.9757266772327314, + 0.5017028463917943, + 3.1800094770093223, + -9.145099532522584, + 0.5049374526866867, + 1.386725096088461, + 1.3587664430780686, + -7.140336167363484, + 1.8245134915391863, + 2.073295079791952, + 0.751375960285473, + -7.606091978393865, + 2.0767323624294556, + 1.6065306443491023, + 3.235065720540448, + -8.721850121309014, + 0.7196711904884685, + 0.9816788132475567, + -8.84696255216795, + 2.626809215666006, + 2.549815866941008, + -8.911171932324867, + 0.9509628399304939, + -0.23927259256889213, + -3.875366962750523, + 1.3908433382198686, + 1.8916021423621145, + 0.7357495578652032, + 1.8923717026364686, + 0.3266032798067937, + 2.300474414349679, + 1.5890325507701302, + -0.06549343928613015, + -7.538525770096769, + 2.318466047257237, + 2.8665125668115454, + -8.153893833561344, + 2.376837200867359, + 1.4709715498404656, + -7.706214252240921, + 2.740327301519449, + 0.09028636054530002, + 3.228455947564836, + 0.5513794682867488, + -7.295604836083069, + -7.759906957734117, + -8.09777432729763, + -8.756864903683306, + 1.2763511405271517, + 2.945562275839741, + 1.3838398593194638, + 2.6854401964823555, + 1.656261678163789, + 1.7533428744601358, + 1.3611257362588403, + -8.655329830508306, + 1.2443715368140775, + -0.1609367996719012, + 0.4399722778070814, + 1.4550332512164776, + -8.299697577767187, + -6.791177821351162, + -7.296317217122517, + -4.24043076076241, + 1.9744461728026013, + 3.0169108659129678, + 2.085481071313922, + 1.5796231665236808, + 0.28883706586254004, + -7.591695100980768, + 0.6925641152794203, + -9.11235095395525, + -0.2735603783775178, + 1.1818571609991912, + -0.018031404946787462, + -8.419312407491887, + -6.567851485538858, + 1.025406857208014, + 0.9351698697703867, + 1.818433527458495, + 2.572785712333763, + -0.17393935498240165, + -9.016630101374009, + 3.2792795030834743, + 0.6658242879271989, + -4.152270499925638, + -8.157087288303547, + 0.335924182306347, + 0.9271775190846979, + -0.03618409014146321, + 1.479665880222401, + 3.3166424655428544, + -6.576355835404252, + -8.394098885439393, + -8.303510483979204, + 0.9062729084027537, + 0.899839531830259, + 1.8193688012574165, + 1.4364119637784045, + -8.767490172955053, + -8.595866558481568, + 2.894212712135655, + 3.0533541874355197, + 1.476114993804659, + -0.6692909143542188, + -7.138202852784044, + 0.21695706225651779, + -5.7962601631962904, + 3.7264192571904973, + 1.0442589644292544, + -7.076269965204877, + 1.367915964860723, + -7.544790546463144, + 1.266952685212199, + -7.804861505331333, + 0.47349039096221374, + 1.5930269805126527, + 2.5782435864991484, + -8.074721153378004, + 3.5025819239037292, + 2.9573712662329408, + -8.193982940881433, + 3.2661504885678805, + -8.948840147357354, + 0.6610691084305056, + 2.6835411548086467, + -8.934693065916505, + 2.10816139136865, + -7.548985911968935, + 2.4765230790798833, + 2.4141491397851342, + 2.739887738696199, + 1.1338375590537584, + -7.31405143327513, + 1.869248007035211, + 2.1401939557341008, + 1.2329394233202717, + -6.723686979858944, + 2.258413389809931, + -8.768330194156949, + 1.2589709754909724, + -0.3036390540432826, + 1.9527663384310978, + 1.9649532855712855, + -7.981643134186547, + 0.8111442705420386, + -8.775732723411297, + 1.2730604738899645, + -0.22849125581746754, + 1.5137214298375352, + -0.5539006361886138, + -6.960063658385964, + -3.89830422876073, + 2.8616026407122552, + 2.9130500271371296, + -6.828704833371259, + 1.0667775059630333, + 1.151566623944205, + -8.838089952827328, + 2.2104783974902467, + -7.902865224549581, + -8.302070760649276, + 0.5350976393159605, + -6.716064221510766, + 1.267120489241334, + 0.6953533727748282, + -8.502301241872468, + 1.5904998311089247, + 0.8035782114725972, + 0.4045367467954588, + -0.08084644425017389, + 3.1042891336787104, + -0.39528854294041516, + 0.568607566886694, + 3.2746664864400366, + 0.1352387754209979, + -0.43424640761761435, + -8.46713488994006, + -3.400403057005808, + -6.655886343018463, + -7.836082067870872, + 1.4006280436181675, + 1.8310149135386509, + -0.2144436816897158, + -7.662535451019991, + -0.28366331573350656, + 3.6834853049456493, + -2.8908696938583747, + -9.092271857083439, + -0.129077551303571, + 0.3467097889851762, + 0.19864145631650879, + 2.628033215265815 + ], + [ + 2.26612718696679, + 7.877304234882818, + -0.2448122627872643, + 4.516540904583949, + -4.185123130466475, + -0.3563651520223698, + -0.8099006227273609, + -0.7273773022983425, + -1.0774494744803462, + 3.3295276863355623, + 8.016118846675909, + 1.566371903855566, + 2.527331443604742, + 6.0204023210462285, + -1.2725524662708634, + 1.6069492541685462, + 5.49339016416923, + -0.14877824811217932, + 7.541563678570806, + 7.828715198686858, + 8.78143506494612, + 3.0724004942655743, + 6.910282329799579, + 8.520562682219973, + 3.034769667067511, + 1.9556477732053121, + 1.391521150600587, + 3.4951871559764998, + -0.17956672474858976, + 2.26349058202461, + -0.5435329290392841, + 8.741637845270487, + 6.471130230045931, + 0.4137720216823771, + 5.314452356601059, + 2.6065034944063608, + 6.069220385698501, + 3.3364078398310455, + 3.328331508170648, + -3.3524926199567573, + -1.435579284246872, + -0.9751170795016273, + 2.0364383183517596, + 1.960212653802774, + 0.7864521202540135, + 0.5657911702861587, + 3.092853905227683, + 5.01806876941713, + 3.8663921379199855, + 2.821039512684959, + 4.622541969701707, + 2.9858484817831323, + 3.6261134085537847, + 1.1724037712799356, + 1.3838290511047608, + 8.806048701083698, + 5.668478923267121, + 0.8018037690672242, + 5.142631152815043, + 5.162406058696506, + 2.6062536456732297, + 3.2943461105091645, + -4.990164104959657, + 3.806517214224376, + 1.8949308089289694, + 3.6353370033600987, + -0.27148904339677543, + 2.7380806442893926, + 3.006631409999392, + 2.0071121263434377, + 0.04628078340754096, + -5.1377197960285175, + 1.0483368306122867, + -4.784796367966544, + 1.5862439101500176, + 8.756330051078708, + 1.5805286130098075, + 1.7098684717663246, + 3.200383653069714, + 6.709460319064008, + 6.307911810299551, + -1.739665781844903, + 4.245169768183147, + 1.666455373730062, + 6.837709319524681, + 8.3481618163966, + 2.9875858571030065, + 2.8139839176386436, + 9.099261178847472, + 8.948801052040421, + 2.006202530800494, + 6.73880301635272, + 8.17611168200775, + 8.773745685376184, + 5.91600439185124, + 6.104433949863836, + 3.7052400992485914, + 6.192779846035659, + 6.600091346107618, + 5.923022741981018, + -5.250204433842824, + 5.223817204947422, + 0.696206808198047, + -2.9769151747495926, + -4.130522494380668, + 7.584957421710554, + -1.5598149353078123, + 7.718639481716779, + 3.0013319833107084, + 5.928077111182149, + 5.152861273092454, + 0.9286834020630569, + 1.15570293124766, + -0.11413301755132071, + 5.3564497446505275, + 3.8928146168617306, + 2.967358817084032, + 9.258863388399549, + 3.629272759041251, + 1.6426518374793857, + -2.8569863552591284, + 6.781260075162287, + 7.624208895126389, + -2.251232030343179, + 0.46027511263626997, + 2.1982523757635377, + 4.5679304243381935, + 7.908571146138121, + -4.853529006657921, + 1.5476649669885307, + 0.8422206760808629, + 3.314306441122399, + 1.435163153918387, + -1.729483134322791, + 9.254698656553444, + 4.161313073939071, + -0.19285497459513173, + 1.471278108190918, + 0.1733342761260208, + 9.03087243734635, + 3.4629830189459407, + -2.6264808461106277, + -4.71657716085353, + 7.869469706260304, + -2.620405496749864, + -1.578270511956123, + 8.357815415289556, + 2.752648283396273, + 5.565302741704507, + -4.805089857063229, + -2.1885042710137363, + 2.0585183776678355, + 1.1610136154324948, + 1.4912027874169376, + 5.914379471375557, + 1.3499504765324346, + 2.044807431029458, + -5.406212771764639, + -2.521892586706258, + 6.1418606819986445, + 8.415457501107205, + 5.604133372216368, + 9.143496250718814, + 7.319575349089649, + -2.06421150615848, + 8.215989933714578, + 1.6857886508980149, + -1.0878353251562816, + -1.5829661285262637, + 1.4674923894311405, + 2.7741856684983164, + 0.9185064155989479, + 1.2781037230916843, + 6.884473682406043, + 0.2934983262573301, + 5.785360809322976, + 2.453893016522862, + -0.7453305767736639, + 8.396234722381891, + 9.850354025725204, + 2.216365887386976, + 8.431236102153253, + 3.3932288637112924, + 3.6608661914291054, + 6.6026731099986815, + -4.953410384187296, + -4.4383215629895725, + 0.07462075161455901, + 5.919009879800911, + 2.95366980628499, + 9.444938044811291, + 0.9076977968117299, + 8.586093101374825, + -2.947154691692993, + -4.633162015997375, + -1.6398464357794498, + -1.2357874214259623, + -5.298383006143634, + -0.43896349898626935, + -2.3401534496139194, + 5.210537676425541, + -2.299537704354067, + -1.1292417029652433, + 1.5799175742083458, + -0.336520751004366, + 4.223746594866309, + 2.8317988965614815, + 1.8947275849516294, + -1.67358278514923, + -5.253236405380462, + 2.219133984219187, + -2.1880534239704144, + -1.3375574851781555, + -1.6072610776058833, + 1.5397232688385492, + 7.036819843042076, + 6.692937873958802, + 1.082746220482573, + 7.797508199429759, + 1.2007123936245745, + 1.8770497605398146, + 6.8994754889657735, + 0.3012191088043153, + 1.068935678724868, + 3.8501454879447587, + 1.7790542501409592, + 1.9614894543599724, + -2.3388476137009833, + 3.110452608712005, + 2.488897512781782, + 0.29446492568337673, + 9.228134471260587, + 3.063679541623608, + 3.127534142642514, + 7.2918077503859715, + 2.1074622260198823, + 0.03666378061265447, + 2.348354665889141, + -2.776578577073148, + 2.5981465208878927, + 1.9166047514477609, + 9.467577997348005, + 5.157166112164271, + 2.7420707192882334, + 0.589056280377641, + 6.296664749024959, + 0.9971281607317918, + 5.968437263241744, + 1.0571690485567766, + 8.746282612307349, + 0.34173616693893566, + 5.460155699051275, + -0.33199671017021387, + 8.506589166854504, + 4.760513163928613, + 3.9122175258157936, + 1.819189742944049, + 9.482487935152745, + 7.768142046743971, + 2.778312560578788, + 0.8924815069256471, + 0.5781476976836171, + 0.879392015766445, + -1.2513183283229647, + 2.9135625658416435, + -5.218754075977553, + 5.869987475209426, + 6.215557257640273, + 3.7965216268335875, + -3.978376269047853, + 0.6441282455417816, + 3.195727870886227, + 7.56439441284703, + 3.385762283468298, + -0.24603155345912425, + 7.62699568971826, + 3.095762191436015, + 5.973306919254448, + 2.2140268056041097, + 1.1127192954394922, + 9.6943693980643, + 7.492005482707253, + -3.6717161892789565, + 6.318002419686285, + 0.07016674311514137, + 1.6940972871714632, + -0.08558940895614194, + -3.8815121919455793, + 9.249512566645945, + 4.598958709603496, + 6.177639401450066, + 2.158306022736798, + 1.6740916570728577, + 3.2165428053675784, + 2.139437242413739, + 8.527661461666613, + -3.1283742405260058, + 3.0533838621473004, + 2.8169855715416996, + -5.02273058607592, + -5.114225383806405, + 2.7643256614152265, + 5.972647138843313, + 8.896241804531897, + 0.8767719229947301, + 0.5510963795699688, + 2.0798971261268364, + -0.20474346020074016, + -2.6385424632674277, + 3.0197194877796796, + -1.6532785804244121, + 7.350061942234777, + 8.424823833262176, + 8.59104964106905, + -0.11483904098985327, + 1.3641616272742318, + 0.9584917078748736, + 9.580245305611498, + 0.7483599943956905, + 4.655927249095971, + 0.900799899593537, + 6.2130604458748, + 4.445533750316594, + -3.932854365326212, + 5.551721373679071, + 8.407643877761046, + 5.885232771171051, + 2.4313967402599874, + 3.669780715212272, + -4.904065807718558, + 1.0158008170609327, + -1.6272293048652275, + 2.845528271618909, + 2.807947902007598, + -2.1459588093206796, + 5.608674291803838, + -4.561053175109312, + 7.69017878292755, + 4.905071561851479, + 1.3413222347068192, + -1.1824406687276312, + 6.738801169688746, + 0.9426274806618206, + 0.6751144483531506, + 2.5848520669007238, + 2.1630373426657936, + -2.7076800307547133, + 1.864371841092596, + -1.9682191074896516, + 3.4172413227955696, + 0.020744702897102174, + 9.769505400367716, + 5.798720665320329, + 0.10253613057523274, + 6.0749341819718214, + 5.357902585098012, + 2.4046800092407876, + 3.512704197882585, + 1.29949383757325, + -2.356963075208514, + 3.1765333049669566, + 7.959069530307456, + -1.966846171005304, + -4.25330821080038, + -4.9219589292228685, + -2.197056092175947, + 7.616489594588331, + 6.040817035671559, + 1.8018917838550688, + 3.5407913971559943, + 5.7890179781018425, + 3.5262942067172984, + 1.9950824464659818, + -1.2893474491122634, + -4.63882900188689, + 2.6694949944379345, + 9.674111224668177, + 8.667295005270228, + 5.737081498597616, + 0.4107629657054798, + 3.877046863822763, + 6.0243859744065915, + 1.8838291584410343, + 2.2562445930114294, + 5.4540041564283, + 8.274269306597581, + 1.2052437166236634, + 6.009785777223097, + -4.383503344244362, + 8.656175590418812, + 0.3181436886883143, + 3.1680255522231233, + 0.6533605734315873, + -2.0973750071005584, + 1.994294615835681, + 3.2127097033396788, + 3.439095899977053, + 7.2040540626614, + 6.785366464604335, + 1.9671587153234735, + -1.5827704027494358, + 0.2878181187329489, + 8.701213078576167, + 5.775702232264342, + 3.796888482981822, + -2.2656130867226323, + 3.3040363272429416, + 7.8336984140783645, + 9.341343078834914, + 0.8594581274173383, + 5.3238697665732495, + 7.72383488521013, + 2.0805761896462105, + 5.00590427740257, + 2.4523488846181003, + 1.530253353729973, + 2.2370825976050397, + 1.660862575032461, + -2.2119403145718572, + 8.457984430510653, + -4.738004795759159, + 0.4176833442710331, + 8.521874684509584, + -1.6000253603261758, + 6.170469016612993, + 5.183781673399163, + 9.17207654732024, + 8.7802004157023, + 3.315232146309697, + 1.7255707001482075, + 5.615285248049787, + 6.452463946699582, + -4.925951924112487, + 2.882800800278358, + 3.93467062640467, + 1.8659188085616192, + 0.3306654617084946, + 5.88741276107328, + 2.550510994085204, + 1.3678259326902338, + 8.663112992818656, + 7.745060334809355, + 0.2809562613068918, + 5.757109520649999, + -1.762138015439035, + 2.8786271321357484, + 1.5474645624637917, + -2.261619052143985, + 0.46857555701517345, + 2.5125424778036147, + 2.672966408932212, + 1.816597263740069, + 2.340187280432554, + 3.634287582524317, + 7.989955726847132, + -4.1380036275031475, + 2.657718947754817, + 0.013787158369857909, + 3.3229210110695737, + -4.201062825692139, + 0.6624903490617761, + 0.6944889704349064, + 2.3533513425377217, + 3.7656997519365882, + 1.788749981152106, + 0.7204561771404412, + 0.5730477522205016, + 5.847178671138482, + 2.8793680686193746, + 0.7398060516511077, + 3.4301937342836974, + 2.531013519887129, + 4.755796561060134, + 0.03385492782234758, + 2.0252506551361464, + 1.6234611019159673, + 1.228843641458273, + -2.1668553315537094, + 7.900290985046659, + 2.7050616439468067, + 3.0006973624200683, + -2.1759904645467083, + 1.5375916476358278, + -0.2253952541126304, + 3.343969551234012, + 2.8107101876639935, + 1.0346932550959453, + -3.6532774566258266, + 8.814079961490908, + -5.097909229812539, + 1.2520930353511095, + -1.2672246839647656, + 2.293286603007841, + 0.2410596637437222, + 2.4817504139530056, + 2.248527285829585, + -5.447774337207916, + 2.5923894677692516, + -1.6454701766763544, + -2.430822876606998, + 1.457649746056256, + 0.7661499751857752, + 6.0009153574611185, + 0.1712479024293902, + 9.530954997711033, + 4.983304885063888, + -1.8163769564966774, + 4.880132622934018, + 8.63294213048147, + -1.7947779683144798, + 3.8634935719553365, + -0.8598586499831854, + 1.7046802605527513, + 8.229281772842395, + 5.5246578628586045, + 5.398118716576608, + 1.5665377764811041, + 0.4245517342194059, + 0.42220229215306493, + -4.167833021757446, + 8.930094781851475, + -0.35446000339359723, + 2.0690947296225084, + 2.2632723564752517, + 2.278629737720776, + 8.756058383175949, + -2.384089444260743, + -1.438680090544982, + 4.019783025134954, + 2.770246387450514, + 1.8829131809196227, + -4.477963763370143, + 0.32452313825904894, + -0.21195692236210456, + -2.161747413311612, + -0.1104742560807789, + 2.731352200053975, + 0.13773536558903318, + 1.7722498157924262, + -0.19267629049184617, + 5.9428729939902, + 3.1430293610074944, + 3.722242795519652, + -2.1106747591923156, + 1.0177343680607016, + 2.3476469900890904, + 1.3611397625517598, + 0.5769222962931847, + 6.296183309833097, + 1.1314346686866923, + 6.996354905775925, + -0.49731034627470605, + 1.6514206933772515, + 6.520507562296869, + -2.4876418860943246, + 2.2307122674056097, + 1.2381767470195828, + -2.41036271414792, + 2.8954645460727777, + -3.2678317085548385, + 1.0575729842665866, + 8.232678699212576, + 2.4739770514016617, + 6.4903251593467965, + 0.9560520936909322, + 2.1733654915775573, + 1.7088617486581057, + 0.768606455869161, + -0.18151377978512884, + 9.302470491788773, + 2.9242421348918497, + -0.21070765493454563, + 8.523457662651651, + 1.678976168232762, + 0.4907273484125949, + 5.294112902913152, + 0.9570337069921794, + 6.309717848572128, + 5.853969411321018, + -1.3623712064374873, + 5.889476548951992, + 5.9315557030842285, + 0.9762018475430272, + 6.3787887818601385, + 3.702617423313039, + -0.4259287963564401, + 3.3940136837773305, + 6.33254888740099, + 7.22386178996473, + 9.48026934550518, + 7.942085137181178, + 6.426147842502401, + 6.193095220350733, + 4.7154873457616215, + -4.929461685703354, + 0.40851879820326237, + 1.0355585657555777, + 9.311049935380138, + 1.9601546187502212, + 0.8503197268897879, + 6.417658240356214, + 5.596080884732174, + 0.10266942032352865, + 1.979006093526083, + 2.0923008866853228, + 4.450351856234537, + 1.9178224408715299, + 6.065730723024631, + -2.1405682647230044, + 3.556202577526532, + 2.1423103843101505, + 2.5180534174094977, + 2.221808263874568, + 1.3245232660550748, + 2.846493012934731, + 0.8640007518413745, + 5.815377980880926, + 6.8076746368586845, + 3.212876640337127, + 0.6620465641915558, + 2.8358557130032866, + 5.905823174293553, + 3.0122412380018124, + 0.6042312258565233, + 2.7189140793266566, + -1.6466964841111296, + -1.433601831253046, + 1.7738882086077163, + 8.874211341294819, + 0.6374919565889643, + -2.250631300713733, + 8.398314862302222, + 3.109991448596508, + -0.10447829604288518, + 4.7465579152728905, + 2.450563286997713, + -5.3495016911775926, + 8.243966840080844, + 9.353859126160001, + 8.779407481837953, + 9.625349294803156, + -1.1690780188806251, + 6.883270495069991, + 3.420071073653294, + -2.7124407552557543, + 2.431871694876404, + 8.169142653739367, + 0.10312628339127165, + 3.1747240309485587, + 7.605851681417775, + 6.778454276450802, + -0.33823536051106673, + 9.159816946971777, + 3.009476478092635, + 2.2639212699097904, + 1.9662337900276088, + 5.456163902180738, + 6.2728300525670555, + 6.046612146481042, + 5.6254154012391755, + 1.7448738576169487, + 8.702308099646093, + 2.934366073684294, + 7.210872628959229, + 5.892411059521462, + 5.08227461046624, + -1.7395127801589216, + 0.1317231915653626, + 1.3125074882009866, + 3.0260692912174343, + 0.9616394687550338, + 1.0133611506450468, + 4.889654943041665, + -0.9629338146674636, + -2.4724254857034027, + -5.456164226742061, + -1.0820298779937545, + 1.5859048729025889, + -2.803255229805306, + 8.563567374075099, + 8.004869380926394, + 7.789872652488633, + -5.178475355138235, + 9.00872844603491, + 4.1139580524028085, + 0.8012152195681193, + 2.9547142410904255, + 9.368726650392437, + 2.573308753297779, + 0.8721362717477031, + -4.518618928892753, + 3.038737898072484, + 3.494236477379651, + 1.7802679565870878, + 1.8305970064035781, + -4.721803051184619, + 5.4894357947523185, + 9.024945875104052, + 0.33248042116655596, + 8.978545880756572, + 0.8265498387088497, + 5.253669495716501, + 7.7831176744839246, + 0.13285521333224828, + 1.962292141861213, + 6.457116157100644, + 5.325750037176592, + 5.080589681950436, + 0.9492611156552451, + 8.370070015424393, + 3.7195662602507493, + 2.306501093822457, + -1.943000191988212, + 8.935275055211779, + 0.4523697013583989, + -3.776936478186719, + 8.525555483994319, + -4.612421394543514, + 1.382647763042897, + 1.1862068046882155, + 3.502817236032331, + 1.3690705190155288, + 5.794610055631808, + -0.9857006762062972, + 5.062053934761011, + 1.6804265993382794, + 0.46795210451425295, + 5.91865159008816, + -3.274753687313677, + 7.117602921781142, + 1.1929818334326139, + 0.7770110791675934, + 5.987436755285214, + 0.6581091618990716, + 0.22582897088511306, + -4.02472552189992, + -3.281325205464603, + -0.6180917569514581, + 6.92089529271475, + 4.147765423783696, + 3.889305189579335, + 0.3056958065777463, + 3.4624723766970944, + -0.310507442372391, + 0.8709343758066993, + 5.575731062495118, + -3.9981833864785656, + -0.021001128775697733, + 6.105224149146419, + 3.198468413659969, + 5.394911906600809, + 2.5417900288708477, + -0.8225464773786691, + 1.9292366115723005, + 2.580419662349196, + 1.4624693592572309, + 0.5847068525339708, + 1.292978687785169, + 2.5908710486444897, + 3.348522105080629, + 5.70882232007594, + 2.526697142258413, + 2.8387534278746305, + 8.926971356284202, + 8.826907454040457, + 8.732591313237428, + 5.670388913071506, + 1.181592394821869, + 1.2455224165480927, + 2.612553991589734, + 0.49924191048675853, + 3.0163431566816477, + 1.2389425711694886, + -3.454256812240126, + 2.541300376969366, + 1.1855211805598396, + 3.919987236532716, + 3.108104199692774, + 6.761561693459247, + 1.6617163362455774, + 7.988216917362715, + -3.7069269200955604, + 1.8956754945791268, + 6.513191358838873, + 3.0160456028606513, + 2.556900557970246, + 5.794776377613141, + 2.8913461749272518, + 0.5584477187851367, + 5.823652757914274, + 6.652661373779732, + 2.895484766286893, + 0.004865004026195168, + 3.7362302042851407, + 5.1590787450811115, + -3.2477231320830846, + 5.160237785597462, + -0.0019050703376409799, + 4.6657067577511215, + 2.533925071913782, + 7.49038147862633, + 9.234610799969083, + 2.8091581370951944, + -3.0852521411396947, + 3.8404146939567805, + -1.7364762360283625, + -0.06075348821321888, + 7.855533253661598, + -4.606191298490445, + 8.930715901769902, + 6.433824620680145, + 5.023330657133031, + 7.176348684385988, + 1.5979034036313131, + -3.8353239436016064, + 1.9302760105322174, + 0.4906948818786122, + 0.8236304101051184, + -3.808384325093473, + -3.0486518743757527, + 1.950339059255233, + 2.8772324445470883, + 5.748787174063801, + 4.506554717027688, + 3.3188306684877382, + -0.39989321009171214, + -1.5738329587862223, + 0.8952788762998415, + 2.137591708106581, + 0.9465115280495907, + 0.05176049572996668, + 2.649712588418019, + 6.594377339599191, + -0.6593129867093952, + 3.4751822144088056, + 1.4732219752230908, + 5.704914737143688, + -2.0579211773330077, + 0.6241604121733543, + 3.5941317673956266, + 2.2980594307532347, + -3.9327004047025476, + 3.2251181402296454, + -0.8913391361706277, + -2.7466344743809117, + 1.6619318036068254, + -1.0500979205665155, + -3.0351188482866753, + -2.817952849035236, + 8.918295682184953, + 3.060364669625106, + -2.655530445833815, + 0.641848597267752, + -1.9681102112267448, + 8.525813146195036, + 1.4343982755532703, + 8.908466876353918, + 8.590443320024665, + 2.711023131254724, + 8.89947385113805, + 2.0939160057034907, + 2.65103084662334, + 1.4626356674124337, + -3.208343456221911, + -3.394173167006077, + 1.932850927448628, + 8.805758171186842, + 2.1243490534911382, + 9.028296832464864, + 8.937647726199716, + -2.676577582815389, + 5.903288211290661, + 0.6846977509561695, + 3.8156052421272375, + 0.7634478610334001, + 1.0540467411943064, + 9.699583427222773, + -2.3747586336050936, + -0.95596567080788, + 3.4920439930271123, + 3.11235139509691, + 0.9378696639670594, + 3.35621409897869, + 3.679262467886785, + 3.2029311363539437, + 0.8203047778014468, + 8.554043310004833, + -0.42993598576078784, + 2.511692309884933, + -4.756775876827685, + 2.0603485204111323, + -5.337213657671816, + 0.41725336047678596, + 6.629983319214528, + 0.8903099477880994, + 6.183510404022067, + 0.27046258864924877, + 2.8507973745983386, + 5.953930731914141, + 6.667463064136371, + -1.1853152943830194, + 0.10929547791890014, + 5.159454256150641, + 0.9575382360439176, + 1.0472819291348658, + -4.478812598974675, + 1.63438677088275, + 1.3966105306914227, + 5.436410376920915, + 0.3892766858938439, + -2.2905961508012016, + 5.229602809963853, + -4.864452941250325, + -1.0538101708637058, + 7.164793904682805, + 1.566138730677346, + 0.02704482509077386, + 0.7886677241533953, + 3.979684289364822, + 8.562397301181349, + 9.285407951263371, + 2.0223634080105914, + 0.6748346443384347, + 4.836315638695997, + 1.9106703975216532, + 0.5866544286142371, + 7.697482947447593, + 9.036618052460097, + -3.3937610246659538, + 1.4695633885567845, + 9.806593022345849, + 2.803053620065441, + 2.79080811918104, + 9.085897942228886, + 0.20786152462408714, + 4.554414906944457, + 2.2687730874822374, + -4.4758778295507735, + 3.01749403025241, + 7.009051356750071, + 3.301496781305262, + 1.5978822792179508, + 3.507883946533327, + 2.475904595046087, + 4.723309718269045, + 1.2999810793281195, + 2.6994776633835635, + 2.1776105906692953, + 5.636269224455179, + 9.227390068887006, + 1.6653064746540052, + 2.531245644163319, + -4.796726394177121, + -4.696260149586186, + 2.360303171044659, + 4.809360672005284, + 6.792224681851225, + 5.0239174109239295, + 9.758115953321388, + 1.7938014075697666, + 0.5624509627239803, + 9.136243614649574, + 3.355605586871464, + 1.5306096828306694, + 8.536995394913296, + 4.284406786162418, + 9.288709095420277, + -5.409560336785602, + 2.964373643784957, + 3.7541139426875, + 0.07436249434529059, + 2.6543557259993937, + 7.326068587028396, + 2.361487178667938, + 6.089912059689269, + 2.3291514353075016, + -2.5797469891430884, + -2.204272569458718, + 1.5814138070450303, + 6.603394036525644, + 7.97166923117362, + -5.421796406778391, + 2.310834537661771, + 3.83348926088483, + 5.924444956089124, + 5.705844596614569, + 0.14904642961421793, + 3.2627918032804986, + 9.156346620095126, + 2.3018313060026943, + 2.871420436775351, + 1.748071777193481, + -2.1699630188713317, + -4.423588831601064, + -1.752210329525351, + -2.3332531026084373, + 8.078537148930103, + 1.7468782968550507, + 3.870771091266975, + 9.570067082920405, + 6.532531074910839, + 5.312720960762101, + -3.3929967864383554, + 7.086090345521071, + 6.218945608429214, + 1.8768890922016566, + 2.607564369570601, + 2.7622849353834233, + -2.5914874773514547, + 8.198193747137957, + 2.989820306813261, + 1.7633582516145228, + 2.9524698537504466, + 2.9608279326797815, + 5.159943485109591, + 2.258405427983319, + 3.3374602636934623, + 3.0273792858264996, + 3.1487351190544213, + -2.4495969677949208, + 5.929783834857964, + 6.711771007305786, + 2.081925344296031, + 5.877244018303804, + 8.886653410012418, + 0.6264009865824708, + 1.114324963535137, + 5.582000655882144, + 0.10484985819812484, + 3.9844989239986037, + 7.045339331126441, + 7.376022206417836, + 1.237898376943498, + 1.7470287460799372, + 8.605965387841337, + 9.617446675860762, + 6.882498231126236, + 1.9704705198454397, + 6.000954757287207, + 8.630755642486054, + 0.4865847848937903, + 0.5890115071805289, + 1.4217583119146444, + 0.5759370890401244, + -0.9918362868258609, + 8.505381270758573, + 5.205225724209531, + -0.1641339289559732, + 4.271545367645099, + 8.773787765364231, + 2.8341861182864894, + 6.291040395037355, + 5.433244854956153, + 5.102936340570758, + 8.422552614349282, + 2.8196855273663104, + 5.678926500302951, + -0.5383477536287377, + 9.4253523750977, + -0.22856265846694768, + 5.45239998827267, + 6.874084499868811, + -0.18137759959854494, + 9.478295589662668, + 3.431633175563881, + 2.643320466978511, + 8.186081903820728, + 1.1989638010195498, + 8.54718584644179, + 6.557128513378248, + 1.2300152894799241, + 4.51921956332824, + 0.5693115943422644, + 7.553359478391619, + 2.893921560929296, + -0.7541870943606744, + 3.4344316370472177, + 4.4779116179245815, + 5.302441643354457, + -2.439565181008059, + 9.512899428908435, + 5.385826259956362, + 5.908299075924965, + 2.100513563369361, + 3.3266700309599027, + 8.089733332102341, + -2.6243622656468855, + 7.230528970232492, + -1.0218346404298337, + 2.5739332556555383, + 2.4854245355736633, + 6.832761487018405, + -1.5149844127528729, + 0.9201408348610742, + 2.1035468063646627, + 0.6368115250012512, + 8.214982113070004, + 8.918916811304616, + 2.7106941171332286, + 3.8123959687632794, + 1.9182183558652177, + 3.3011590899178067, + -4.268458134039248, + 4.1607923908684805, + 2.5607637404629346, + 2.537623329117577, + 6.203676444622138, + 3.2449911641283866, + -1.4863704718561914, + 2.0914264281124773, + 8.866073437504284, + 6.360534894196076, + 1.8548388788418777, + 8.546565459118982, + 3.8121042191991363, + 1.782761627693268, + 8.19409331440138, + 7.271377077104276, + 6.713834371576997, + 4.7054015736273005, + 0.6900279581442493, + -1.9950523194654137, + 0.5019672047701859, + -2.4425971792925014, + 8.397284515101749, + 8.274477379414355, + 4.320281308205884, + 2.7306208965349557, + 1.0268803986720851, + 8.041844717647683, + -4.865558143399747, + -4.43090033274667, + 2.080138753512541, + 0.3985237911488679, + 0.13915356157220796, + -0.6797187918311256, + 1.499451661991291, + 3.1062195316504724, + 1.534350188556406, + -0.42490380560179697, + -0.8824812983295844, + 6.137019298389204, + -2.442387086532495, + 3.5302750806073444, + 5.623701737032925, + -3.048559332327405, + -1.7927761727406366, + 2.2501008966231493, + -0.0434390996019198, + 2.0713629857341833, + 0.6667391023716165, + 7.138508838403462, + 9.117134927041638, + 2.844253892877252, + -3.010065734551748, + 5.3434391962662975, + -0.03387006007417064, + -4.321302231073703, + 6.015578668701478, + 8.694611177352565, + 2.840831058685417, + 3.590468322991271, + 5.925869307767682, + -0.4632539232111991, + 9.852085447425996, + 4.246050458490948, + 0.0795569848871431, + 6.342863222998658, + 1.4051373647168843, + -2.1635570082088544, + 2.2571055692634374, + 3.578015263178784, + 5.92501466937786, + -1.9992269400077587, + 7.613431338304448, + 2.0586657648069444, + 1.7131255525320113, + 2.850162142184762, + 9.387208612221631, + 2.9743469463435983, + 1.1035216432033375, + 1.5082199385262602, + 1.1770277270238634, + 8.727374689487656, + -0.5503031034740972, + 5.996322472782943, + -2.106014886932281, + 2.7145712429255298, + 2.959255404634817, + 3.113836493861464, + 2.769285267659297, + -0.03383966505561062, + 6.981888952467136, + 1.987340238395796, + 2.5935349892602932, + 0.688685934326811, + 0.6023938118323606, + 8.280187749951624, + 8.657520654006337, + -4.614428004690138, + 5.490273423799091, + -0.45654403516637426, + 2.2552766262157378, + 8.950763875630653, + -0.752654576519053, + 6.198515988684498, + 6.375645140382823, + 7.778627264011731, + 0.0679528135196061, + 3.1097816129485856, + 1.9708437429931311, + 1.2519087257185952, + 6.035823894471679, + 2.641606741377335, + 2.3611442950292063, + -1.2138498292937214, + 5.848272228719679, + 0.9787762749408776, + 0.7896001910941994, + -2.9666200974972514, + 1.2153395327671428, + 8.51216629567888, + 0.13161855421830565, + -3.1983695913395485, + 2.4048892802020774, + 3.2731024798000132, + 5.892377898027779, + 0.6485778189416769, + 5.531378659573957, + -1.8313893011262574, + 3.462656522250383, + 3.0240830103191456, + -1.3988666551884819, + 7.196571559897184, + 0.3669345964179366, + -2.8035394109630563, + 4.692363004269288, + -4.476727261762838, + 0.32343844200745103, + -4.182356703599284, + 2.928534228041603, + -2.7685368080690473, + -1.2654701804392694, + -4.052775980189864, + 2.6577769067459074, + -0.9883034373434559, + -0.3934847099090396, + 1.2274252023207477, + 0.1772146684449812, + 1.4914482504911268, + 1.8188586228106742, + 1.5897815313464005, + 1.0307870297513482, + -4.552732358353905, + 5.596899036377971, + 1.1599206410942584, + 3.4385418183942993, + 6.082213341527612, + 0.7341908164143667, + -0.2480626007455346, + -3.1575191950472297, + 2.5389293824676726, + -1.4003061229056613, + 2.6833282298497223, + -0.5467975634173019, + 5.98852696995778, + 0.21292691886096327, + -2.6893512720716815, + -5.117588668343177, + 6.794322180635246, + -4.5742625010613285, + 3.3548586996326435, + 5.0515600035370225, + -2.900449599496725, + 0.10237610962475915, + -5.064851833194207, + 8.531627029244655, + 1.5742438748697312, + 4.082638432652501, + -5.0770992768786645, + 2.119205675069604, + -0.549498327530717, + 3.5730581737051277, + 3.196962670970756, + 7.035845030498391, + 8.548505437332542, + 2.678325032018049, + 2.171957067852686, + 3.4355159234823076, + 5.376546760599714, + -3.487535293579003, + -0.7959857552014767, + 3.109184499044637, + -0.034074417858167665, + -4.463432440942218, + 4.754451040108373, + 5.977086152281335, + 6.3113939537838615, + -1.1073355025666125, + 8.245632488073356, + -5.2333112598995895, + 5.178870586953865, + 0.6180633531176227, + -2.26460261809812, + 4.353674921953694, + 2.8072255410103386, + 1.521416200823173, + 7.589366619945358, + 7.701394142184665, + 1.2116731292036584, + 3.2138291408691844, + 2.8443169344773516, + 0.8903178116947174, + -5.108687151576169, + 7.717847243550239, + 5.103365026900776, + 2.2950781198168375, + 6.397420501802669, + -4.784056687598436, + -5.164879189057719, + 2.612616948933114, + 2.368955342992421, + 6.57506537945875, + 4.983841437905054, + 2.50680520340803, + 0.9418427383823713, + 2.346670739637181, + 6.7670420058449485, + 1.1729036494953344, + 1.3973552011354742, + 1.984364716958311, + 1.1219650572007647, + 1.854444243223755, + 0.15395235570302412, + 6.207396756273517, + 4.902381553967024, + 4.057220866025185, + -0.1327819842430041, + 0.24745711201195134, + 6.1793343047872735, + 1.8642901437174684, + 0.6795484548721835, + 4.802713069866874, + 2.192594562354952, + 7.504825326157448, + 0.4360785367712627, + 3.0969826788534904, + 0.3149099546820246, + 0.631302944620672, + 4.008347085568968, + 5.796200617136318, + 2.690265077072964, + 2.4509671492071647, + 3.1794303634347654, + 5.480282099311519, + 3.793009761585952, + 6.694411441476124, + 0.14085170243202885, + 2.6005776440097637, + -5.2332737609043765, + -2.9808615128824476, + 0.8786289985481496, + 1.8675196537055403, + 1.8798070856567528, + 2.979399276715977, + 7.348120227074838, + 1.0545657973863674, + 0.35836880076700756, + -0.26505513627447935, + 8.37604052125493, + 3.8108946951851164, + 9.371530125128047, + 2.1527731861407666, + 2.22024217006518, + 1.0043860933717168, + -2.8223649837661275, + 1.4529377327135113, + 0.315286921325931, + -3.759919055529933, + 3.192557001334405, + 5.99258651557055, + 2.8135841369438124, + 2.835625816399913, + 4.784946846678241, + -1.7062587841124341, + -1.4443605566411828, + 1.599719712031716, + 4.626872133922204, + -2.066200466458587, + 3.4202402597721107, + -1.9258775472193792, + -0.7109237030323599, + -3.0633148300714814, + 0.7916130122573279, + -2.8003481525863623, + 1.2805196495791191, + 7.641070031410805, + 6.899822557600844, + 1.6947028912538717, + 2.5581600518961483, + 2.0034929243649846, + 5.857911263653215, + 1.4636246547379912, + 2.8971993456484375, + -2.251511707977432, + 5.884447799046445, + 6.7644617214749285, + 2.4674566860698466, + 2.399380076812881, + 8.639067607097326, + 1.4161103959753811, + 2.655889404446979, + 1.8657322395014868, + 8.099395562761458, + 1.2882372797576638, + 5.344775989736017, + 3.2843163197624916, + 1.3268692580615533, + 0.8743822373812074, + 3.4891420513567053, + 0.07385661891207071, + 2.922503933319907, + 8.714379542426268, + 0.6501668013700235, + -1.1375008855416793, + 1.857754709954258, + -2.9945232842258678, + 4.684544304995022, + 0.12886049121117665, + 0.03972938223109909, + 9.237892036506507, + 8.51762483804629, + 3.3724974386724624, + 3.5329200482619085, + 0.8927369288119711, + 1.098034894330614, + 0.0766899373263363, + -1.5153756903132682, + -4.859313173242173, + 2.239424598692307, + 0.3087161707098317, + 1.4471925711686535, + -0.690409487135307, + 1.6160860392915781, + 2.7508251678939137, + 1.688227865698469, + 1.8258302446153771, + 1.2362379094640268, + 2.227100763719389, + 0.013469605778813766, + -0.2565112959825642, + 2.3112040766131825, + 0.6319471784615154, + 0.20327688152306575, + 2.604414592341301, + 5.66856972968723, + 2.7980140743746076, + 8.67085362838519, + 5.918440790189692, + 5.7224556859282, + 0.857285364668508, + 5.544811900362975, + 9.4435138232807, + -0.20236353549092811, + 5.016793650411961, + 2.720699822325314, + 4.5479069977602204, + 2.715983316290806, + 1.7525824693752088, + -5.264974061070002, + -0.6496290315234062, + 2.367614338923079, + 0.6879917909303833, + 1.3029286630363872, + 3.0138918746597065, + 6.640804353774791, + 2.7172754438040307, + 5.76419355748975, + 5.017375178348365, + -0.10816025840842662, + 0.9453881730249583, + 6.720643124414721, + -0.1103631054118109, + 3.3408766939456034, + 5.794047549702596, + -2.025824021148328, + 3.252234698808337, + 5.530672000744878, + 8.64747695134738, + 9.515097696825404, + 3.2344927992474717, + -4.561284055904563, + 5.603927007833755, + 1.6239964904833328, + 1.8190481024621308, + -5.303239347416054, + 3.4400884108742207, + 2.076151202158285, + 2.1590840988805513, + -3.874933358494983, + 8.009236378478496, + 2.742251873418243, + 2.4574402301105303, + 2.1704695708632493, + 0.07441695251940642, + 9.142691879015315, + 0.09950685263139657, + 2.547726388955141, + 2.4264440031043555, + 8.995420023183437, + 5.928160199896791, + 5.10369858225642, + 6.797099162610654, + 9.891558600174584, + 1.6271653661516037, + 0.865228175916761, + 3.293969600310039, + 4.5338781574902605, + 1.8576515517527716, + 2.200264025873621, + 9.229541062816832, + -0.5258640404899334, + 0.7878662950097198, + -4.809662866623755, + 6.172140436212999, + 2.353632835509077, + 0.3245596351457882, + 0.7492161994440059, + 2.6243550810331664, + 3.6033101201399518, + 2.883674831846493, + 4.777864163973319, + 7.608122293011118, + 9.094148010265082, + 2.8586547600235472, + 3.0637538813442333, + -0.16306496309051946, + 5.978283707847633, + 7.0152850555910025, + 2.089439744209332, + 1.2699966622815706, + 7.632606479355867, + 7.426884733347851, + 4.919860769652532, + 3.4636433196516427, + -1.185439361099015, + 1.223042401715309, + -1.7204930505004916, + 1.2498940606612439, + 6.096280333402219, + 0.0255562791229087, + -4.7421129527816, + 6.9134628087321275, + 9.345092400602578, + -0.7428833847718531, + 7.751469449471995, + -3.174355411099064, + -0.4389593568137036, + 0.7889164889254271, + 0.42671004071595564, + 0.4146530713836919, + 2.9852951657239575, + 2.420596392633052, + 7.280976775617019, + -3.9833538656439123, + 1.8123279460938362, + 5.254191706507663, + 2.988686456685128, + -5.122771852329393, + 5.460160945532948, + 5.556392417120963, + 1.1989693659532146, + 1.8856338222888531, + 1.4304240447047571, + 9.103673874712799, + 0.06974033136878711, + -1.8634596072735357, + 9.181488312562077, + 7.782179970735101, + 2.0106552241346183, + 8.423518536106023, + 1.5602445629709538, + 8.192057648102823, + 1.166091512538277, + 2.924903190780516, + 5.805348422221177, + -1.870811325838246, + 5.497133289900674, + 0.6912014452029617, + 2.4494309050757823, + 6.214442790508838, + 5.532218926098756, + 1.8484648675848672, + 0.5357420048696545, + 6.168881879285348, + -4.164441414751242, + 0.8497563909106339, + 6.068046616819917, + 1.6311217190066913, + 4.935653352145963, + 2.909436523764689, + 0.3586354983898887, + 2.6396021300049144, + 3.4768890569507467, + -2.888866342024628, + 6.580642289649002, + 2.302982426221136, + 5.055472805495961, + -0.4836179568565782, + 2.6416319996966986, + 2.3803682290271317, + -4.975249596900043, + -1.4678816639945595, + 6.54720672263416, + -0.943030719721922, + 8.06944049964118, + 9.456410932217949, + 5.477892548341206, + 2.0069868969636064, + 0.33000045858721744, + 0.45137009084748314, + 9.585272571025595, + 2.6585240479374606, + 0.8401318359631659, + 5.921237265866324, + 7.0614974156154044, + 1.3234965998892707, + -0.19082410082550832, + 0.9482892986162604, + 5.1980514735827015, + 1.8648414428434004, + 3.8275970013636003, + 2.145441639200901, + 7.18545866225611, + 3.7681560899027025, + 6.7706972228375815, + 1.710843392703091, + 9.653896454935389, + 8.991595342866164, + 9.186382157891034, + 9.04406304944811, + 0.8172915176930347, + 2.7566256486526353, + 9.660910576572753, + 5.774924628332689, + 1.0341297924498363, + 6.776970079531831, + 6.870337511570097, + 3.316407570037295, + -0.7327186727816404, + 1.4785073400784159, + 3.2646800316507143, + -0.20914306937205182, + 4.64867840898082, + 5.091437827560491, + 2.543828629721811, + 2.491519287769942, + -0.13127409974118892, + -0.6849444346996574, + 5.704748046506554, + -4.580428758802872, + 5.9276986115834305, + 5.949946091737831, + -5.112703533067682, + 6.328840062412349, + 1.3725722459483412, + 1.484950850899703, + 4.922692136902319, + -0.26360087335294413, + 0.3972863993323734, + 6.43499018390537, + 1.9261614464785353, + 9.25217428814158, + 3.4887977439401117, + 7.798413137478559, + -3.859332588932147, + 1.0076305806755113, + -1.595328104377359, + -1.9345755656466876, + 1.3656568430290457, + 2.010239600296628, + 3.0842970345182317, + 1.8307019509259435, + 2.5249665379978907, + 0.21114084691696372, + 3.010763869995198, + 1.5804254258562025, + -4.520042320218686, + -2.128356334095731, + 0.7405047744804988, + 1.7154819971091906, + -0.8522515684542971, + 2.442382303303678, + 0.18784706156999456, + -2.109265073450941, + -0.45500559588426437, + 0.7348284376716618, + 1.7123931297303872, + 5.254688512601999, + 2.7456168285842244, + 0.5416658212070915, + 2.301238469829127, + -3.1464762215917483, + -0.9007639718009747, + 5.42006148595848, + -0.9861660783712057, + -2.162965897976326, + -4.493082242249678, + -3.8189365772831603, + -0.10234080732201847, + 3.8222780518158883, + 6.069040078680538, + -5.257236694544421, + 4.043152750327152, + 2.9400998090483847, + 8.123198210212966, + 1.1001939822886846, + 1.2565680855375772, + 2.245798308281623, + 2.4479921134731173, + -3.158270658414151, + -1.5481370872038624, + 6.312918687487506, + -0.37121749047904795, + 1.6919954011846836, + 0.6875554834459174, + 7.242510466143665, + 3.2722108293917302, + 5.8160212902859705, + 0.26737489698121397, + 3.0063850526444735, + 3.506124855697979, + 7.042641095704005, + 3.3569127880879672, + 1.5852174746984395, + 3.6751882693142797, + -4.165220031935649, + 1.215199283565774, + 7.90308322296078, + 1.5939925329095328, + -4.664195921536808, + 2.4132307103650708, + 1.6273049780055568, + 8.030453359488645, + 5.926901407210981, + 5.96945226815641, + 3.0528213692017427, + 0.5294928795205264, + 6.111198931739082, + 3.27826803975091, + 7.979729831606848, + 6.579075127990691, + 1.0644579389040314, + 3.102471494981852, + 1.6526255527926923, + 1.7127900541239869, + 8.283760640459029, + 2.1924222459051204, + 1.900806515428295, + 5.329619827785321, + -2.071511831927549, + 2.1229332814500315, + -2.4852779587449625, + 3.4704642694533496, + -0.37053887420146553, + 8.385984728405884, + 4.493223761008627, + 2.180319410350078, + -3.01208855357939, + 1.332983186514152, + 6.130263721597461, + 6.9534807344215785, + 5.88853010829384, + -2.8407853967671577, + 1.8033610815004772, + 5.949222221028675, + 1.9889057182006664, + 5.176870999104773, + 0.9709025783457912, + 0.4775110339612653, + 6.003172783895949, + 1.1595497820232883, + 6.039896301978691, + 8.88875053895144, + 5.660666726152205, + -0.20707853144034163, + 0.4656841519810458, + -0.46729412109856766, + -4.763487218056729, + 1.0575797208447806, + 1.4690708457781543, + 8.201368891883424, + 6.322509791870492, + 1.3428277584119488, + 2.6268228995812533, + -2.021077629389779, + 9.536296205942017, + 0.05281985636909039, + 5.667685369387791, + 6.0217023360515896, + 5.878501643642019, + 0.02298604850952251, + 5.729457157547902, + -2.2733129918903487, + 7.357345572460884, + -1.6602227754960404, + 2.8933777470936346, + -5.300255477680182, + 6.674143746202342, + 7.354433017740983, + 8.32057151070352, + 4.986715253364944, + -0.8236511115608395, + 3.000653918731319, + 5.142268479074093, + 4.991873612764421, + -4.462314080258194, + 1.4099959224940581, + 0.5140333598695296, + 0.462038563308794, + -0.5580698037160144, + 2.4156214850354365, + 2.4150056047346644, + -1.4348713625629237, + 9.37189601203025, + 2.2128480268481603, + 5.501830807729627, + 0.5970505131245347, + 1.7086361564802768, + 4.966871116438676, + -5.51059720745767, + 0.40783914628227114, + 5.655657432376619, + 2.1835504862122175, + 8.168986002754655, + -2.7193188251020834, + 9.398775404415085, + 3.2418349793456707, + 0.8676509750580216, + -4.113737353548256, + 1.024007404044521, + -0.34756324082994794, + 1.9602376164792763, + 0.7832705087881328, + 6.335484216782878, + 3.848417262983378, + 2.789319415517467, + 2.7804749683831655, + -4.209835262087807, + 2.134517779191862, + 0.20877152273204996, + -1.6896473650266692, + -1.2400823830799401, + 0.8168266270138272, + 3.003065455584158, + 2.2137576704758306, + 2.3334723718138632, + -0.8022301886454714, + -2.3591277338516994, + 3.269962993932029, + -2.0834230276898755, + 0.9951956155368633, + 0.9691860719679056, + -3.888839739561202, + 2.1629207058674904, + -1.905675289518531, + 2.69453691444355, + 6.701800635735289, + 2.5725594382816372, + 8.588496425207529, + 4.7106805174377735, + 6.410515675188265, + -2.6226997711629716, + 2.072002183326608, + 1.3526862546035745, + 2.1997548005471264, + 2.526355377006503, + 3.3454645401850533, + 1.8236407076363763, + 6.637010415903734, + 0.6187101895208668, + 9.301085778983579, + 7.726784389543951, + 0.9099830270183308, + 6.722528024137925, + 1.256374452711205, + 6.793998223541745, + 3.0663208923461376, + 2.462996441838422, + -1.2483010887074464, + 2.4878297941421663, + 5.988784487203425, + 9.308199852291192, + 5.883351018668258, + 8.143463243475388, + -0.8356458655755094, + 0.5497825145679909, + 3.236547594377735, + 9.029397780799197, + 0.22996970906321432, + -5.012819578978871, + -3.8497149088058915, + 1.7755407000814296, + 8.59826241534019, + -0.4075788598887008, + 0.5821111590672242, + 0.23063765524325244, + 0.9189563098666551, + 7.232389094906414, + -1.268420609294997, + 5.7521552238840465, + 2.203239339828908, + 7.005547360934609, + -1.1313653909462507, + 5.67130135575973, + 9.377013452063188, + 3.4162063856020475, + 6.891357953756193, + 5.572513194602001, + 9.616751460142105, + 7.3777860186219915, + 3.5562906047735603, + -4.282360119923921, + 0.6698010007632262, + -3.2554024637200842, + 0.29250443938453025, + 6.425718881533466, + -3.177602331295984, + -2.454493843289483, + 6.314811684076483, + 8.979560158834992, + 9.870736213896537, + 8.757264493637411, + -0.003453712350984302, + 8.817969535806293, + 5.843733799478246, + 7.054684804368724, + 3.1919023793630075, + 2.699640057234987, + 2.141516681314163, + -3.4472292168477012, + 1.36908126062115, + -1.9441012592167115, + -1.9236197068234189, + 5.397184307275612, + 2.0757701792890675, + 6.778390763843739, + 8.443855120253565, + 7.08532983610067, + 7.9608745265204846, + 7.225488139730609, + 1.845551458086398, + 2.6478648952083716, + 0.699909602488156, + 5.578408909149145, + 2.3707672322763456, + -4.360423816613405, + 6.34034815381996, + 2.5123983953755586, + 6.234127752856539, + -3.403233447929865, + -2.6398216695932124, + 0.11824856949582217, + -0.4560083455354007, + 1.3623351137161217, + 2.488437018998219, + -2.0996984449895026, + 0.4529341843863447, + 5.7316943952319965, + -1.6794395006623484, + -1.662952700628585, + 0.7528252276279854, + 1.86526470435675, + 0.8250661486800847, + 7.545149459365755, + -2.9784914139412986, + 0.7955122992388768, + -1.749696914354937, + 3.007033532357639, + 3.2539694960317154, + 2.325020884091079, + 3.0682380835267087, + 3.4877633370885603, + 4.755907494880818, + 6.128266753985117, + -5.363081889079644, + 1.5887297020473807, + 9.505091939439223, + 4.365143981465876, + 6.7548503826243715, + 2.5052979166231886, + 1.4353576508393462, + -0.9928554982734809, + 2.922494124533855, + 8.10081092078275, + -1.3002214585010545, + 0.8404824712467401, + 5.94837787669559, + -4.767683732401647, + 2.0017751275916296, + 7.915636914038291, + 2.5822586807239722, + 2.8495043174738024, + 0.29281685345900793, + 3.07193328710613, + 0.32780731449595507, + 1.078728624062782, + 9.212142393190357, + -3.234096488944491, + 8.99985503777453, + -2.855040830930229, + -5.295863603354861, + 2.9629710484192806, + 9.59196741817357, + 0.7743349776222785, + 5.792043147303167, + 0.7308440213299643, + 7.350415660821069, + 3.495075238229184, + 8.647182976752868, + -1.534758576855379, + 2.073463430160869, + 1.963332441387051, + -4.694354180861564, + 3.6714354834316927, + -3.9262388559123464, + -5.546500365026073, + 7.445083723470979, + 0.5343682186596866, + 3.183237056362217, + -2.403816101636754, + 2.8857332178958295, + 6.672428835630001, + 3.6953141788532005, + 5.29590329943819, + -1.8554545565340235, + -0.17610077417452594, + 8.271235986900548, + -0.8302414238237459, + 2.4378481735230384, + 9.57202802197544, + 5.557141877865675, + 6.5268978634694035, + 0.8407924426877699, + 7.979052578435055, + 3.117590024813628, + 5.96352037754916, + 2.094825703273501, + 0.07296691179388555, + 1.8079190555258078, + 1.7205218009412788, + 5.730311841139373, + 9.168679321911021, + 6.63794178471708, + -4.770023224802359, + 6.32579824811486, + 8.060919532649441, + -4.3498104635619965, + 6.056146366036844, + 1.3888378059597648, + 1.5681318199046925, + 6.934114111947524, + 4.553233986454347, + 1.4275240710301833, + 3.747256941001211, + -0.7793987571499036, + 2.4159574731318942, + 6.360954669715619, + 0.4084551726607402, + 1.7502096715103717, + 8.32356944907215, + 9.411852540453843, + -0.24749985885791767, + 3.6441437548002984, + 0.6638796329985154, + 0.49554468852439093, + -5.288299956200278, + 1.5867630719594317, + 5.266978763389619, + -0.1730881110987882, + 2.027850474654688, + 6.70043355578779, + 0.20699152625797068, + -1.4169490537802918, + 3.567763215349293, + 0.2333312464661514, + -1.4878662123333741, + 1.091060642589443, + 2.4017502719866424, + 7.9782861690822, + 3.44782406334824, + 1.0432269698264383, + 4.640994658240865, + 5.749646077991115, + 6.089790596813214, + 2.9853600065784107, + 1.585551391839002, + 4.403944696472623, + 0.29951265146255124, + 0.24389931462395698, + 0.8540781982541754, + 4.611025267027386, + -0.45181284082359413, + -3.5542731441518876, + 0.20103598685438334, + 2.3975234423332497, + 8.6230597387424, + 8.663118774999928, + -4.529682685118797, + -0.8760724945413672, + 0.25604543050627254, + 9.032110091018815, + 0.19628068798147708, + 5.911953902186222, + 8.11065712561067, + 1.5106611736082836, + 6.483990941242417, + 0.9154031653035811, + 0.4733209855793123, + 1.2729172153754813, + 6.226232576039953, + -3.7857216516839913, + 2.85210708040812, + 2.3947148486545013, + 7.649662954804599, + 9.16162216363764, + 2.2116236694661873, + -4.445492844370273, + 0.11508527097851792, + 9.076362458267964, + 0.4348146585260673, + 3.688186838147284, + -5.321770884683309, + 7.415460097453132, + -1.9534807833504129, + 8.50852643998329, + 0.8570152188041182, + 3.002819999704473, + 6.0288006491872315, + -3.2528245994908955, + 1.425515401254983, + 2.518619468789845, + 2.6021768268562155, + 2.414457971274285, + 8.811798173197223, + 8.463135654386775, + -2.3599709268161466, + -4.470741594770652, + 1.9029054733785435, + 3.5107539529337575, + 2.517914528908818, + 3.2373739399765995, + 0.9559242825654067, + -1.4022193331720425, + -4.164011090781615, + 3.5098720942728066, + -1.5152128618720702, + 1.2042015283823664, + -2.5223273022794563, + 4.6568722554070785, + 8.748779660690749, + 1.64867887605269, + -1.6451264272207415, + 2.223501415745643, + 8.983241413406752, + 0.7856413355998099, + 8.271058597380113, + 8.759786053557635, + 5.964851993500417, + -1.9854392851535787, + 5.417165541146182, + -3.6442197852501232, + 3.4828188869815073, + 8.467767959705135, + 1.2727275656548527, + 8.52188240945984, + 1.2193859287039661, + 1.6753573505177883, + 2.7578999432181828, + 5.752416727518017, + 3.4895594687669718, + 2.848717380504166, + 1.1007765674235717, + 7.3936145172341945, + 8.335571225152114, + 1.3472097319919996, + 3.1371705224091144, + 0.4215143657193758, + -2.0292815257034817, + -5.49035861680535, + -5.183291470456518, + 1.8343121382967842, + -0.30212063468598305, + 2.67347488141855, + 1.2506741064376266, + 6.705326698795701, + 2.3117777668167516, + -2.166111449868967, + 3.3066654185985973, + 0.8743752567181792, + 9.433552236501237, + 1.3666782032393632, + 1.130142268909575, + 3.2738566103698905, + 6.5968220577140055, + -1.3891345838746347, + -3.5128981904432948, + 5.0804414422022335, + -1.3058470702411964, + 7.130933426715227, + 2.4273980019954204, + -2.1608284982186197, + -0.9100596860292837, + 3.5894835328702106, + -4.751280916256097, + 2.569585024243075, + 0.2919735183319088, + -4.406471808552807, + 9.003775888319385, + 2.7525231897174263, + 8.491374502683652, + 4.376946832713647, + 8.527523053929686, + 5.632602084917159, + 1.4052219593748756, + 5.5737253668239655, + 3.4569678545306575, + 2.8171500161674357, + -1.432944115552822, + 5.697969375029198, + -4.621445460935588, + 2.0787510016080533, + -5.326683128959758, + 0.18067104116133456, + -2.502498411746137, + 5.629349911367992, + -1.6890235276676102, + 3.045990978436232, + 2.068467399984089, + 5.084093781542543, + -0.3929337578092091, + 2.5605365794849444, + 5.53869708350438, + 2.614522456632436, + -1.5480239773209241, + 3.5678945576150123, + 8.83401771512541, + -1.5963844207514608, + 7.624379929762122, + 0.5005540393521262, + -5.527784938566744, + 2.65125893195905, + 2.130758952863588, + -1.8332809453947294, + 9.30394757121743, + -1.446331856826678, + 3.2165312128841435, + 4.04687189616058, + -3.5056213436587336, + 6.666219551384144, + 1.3673469667147284, + 1.6231652648575179, + 4.4948549236610305, + 2.0815856253285214, + -0.60043979994161, + 3.590976451375838, + -0.7453152161686788, + 1.564348734142829, + 5.541589960918232, + 8.202930465965972, + 0.7444615247059749, + 9.587703248667736, + 0.4935395980500209, + 2.02135191370068, + -2.63757568386335, + 1.6931078983944303, + 6.194891401826324, + -4.755698900045702, + 3.053941263428849, + 1.738035826808363, + 5.482444085458718, + 8.578249176772534, + 6.622930301440895, + 2.125057661575636, + 0.9054222604976315, + -1.7109375809695786, + 1.4198761476337516, + 1.054867119727166, + -3.925252521521188, + -3.0436012570906885, + 2.4145304719634875, + -2.7089004374087096, + -2.5781187088980415, + 4.040219917034839, + 5.7177319240579125, + 0.41648218881027665, + 0.5737150782632761, + 1.3605157350455324, + 7.762686896051611, + -1.890859886923358, + 2.9869526962840838, + -0.8018564162424652, + 2.1407876001056168, + 5.863579989317062, + 1.1422827886056979, + -5.034414504153599, + 5.306443885502887, + 7.855634915825041, + -1.840671726488353, + 3.1453425959097934, + 9.521053083576877, + 1.1797955103350146, + 8.34289646049561, + 0.9864026286857854, + 1.5511568318528561, + 8.434271139212683, + 0.20427819364048902, + 2.0271112325590077, + -3.9356698611216534, + 5.870340206181536, + 0.05269352565779371, + 1.7920616044663369, + -2.42138548840195, + 3.475007131820548, + 7.011108331408282, + 9.515231228026291, + 6.346792668752203, + 6.494414623263908, + 4.472927613760124, + 4.524003924372726, + -1.4134186597873823, + 8.723691479901538, + 1.8639456444607319, + 0.7382556576116613, + 1.7673853588875232, + 6.396387432623712, + 5.591000021046794, + -2.2765896318071217, + 6.2962062739823965, + 2.4212794737100123, + 2.697188160050856, + 3.5064505365695777, + -0.9661428559971644, + 1.1653071361402214, + 8.832158933958235, + 1.926594996120538, + 2.2382815782643033, + -2.138572477012913, + 9.020160211222818, + 0.34070599225005027, + 3.3797625149541055, + 0.05703965444493409, + 2.305032462030537, + 6.2463349336626495, + -0.6584707349200902, + -0.38389619151492244, + 6.120169056030916, + 1.6388345686915071, + 2.205438800430803, + 9.043723892497786, + -0.05324007737301528, + 0.3651143038587914, + 7.001822659426594, + 2.5765444604895604, + -0.04862648951907117, + -0.8854025072266365, + -0.32410129627632506, + 3.0812142492739736, + 6.295356520537049, + 6.177967110174736, + 7.079564521405287, + 1.83353114343839, + 3.2388910348859192, + 8.913712343332712, + 2.4375146713115385, + 1.2634733710281858, + -4.414938136285208, + 3.4759377909794242, + 6.569247134786052, + 3.3257450358478975, + 1.0495790550303425, + 2.9650735879571166, + 3.4456722296078293, + -1.9425450759671543, + 1.599117095043776, + 8.497017451637193, + 2.6579556546365266, + 7.910087173332323, + 2.8799774369661812, + -0.07025037126624543, + 8.073942200277664, + 2.3342142619361534, + 2.8830916269966758, + 9.100411166653252, + 1.2058185139176982, + 2.675872700135464, + 0.9666762827582379, + 5.616586906563049, + 6.905637710372154, + -4.860074337814643, + 4.0158954528166, + 1.0941869678450136, + 1.3322841635744591, + -4.8525029564210955, + 1.6950143873386676, + 2.275866960775223, + 3.7546696075480233, + 8.522580158693797, + 8.479289443791219, + 9.084376204745716, + 0.524887035594922, + 1.7930496347425775, + 8.36265519445222, + 9.13207967711533, + 8.728248933808025, + 5.104280192568869, + 6.932520932186675, + 6.57954219836973, + 8.358960821108504, + 2.5870592720871555, + -1.8119057173172903, + 1.7265071935675682, + 5.343747915510189, + -2.5904371427923047, + -5.105623763927343, + 1.7555879054859012, + 7.85831749823218, + 0.3955986367670087, + 2.693545835650025, + 2.2783352602443867, + -4.107147221405172, + -0.3589112571594508, + 0.2739780970973445, + 6.00113681801525, + -0.9520046557887152, + 4.455509206119695, + -0.07994742403490834, + 6.912346070734264, + 1.7374666740318419, + -1.9482137044069845, + 5.243596178912719, + 3.718827522220301, + 1.1880164187237157, + 8.047695963346682, + 8.117318312531806, + -1.5085045180979209, + 8.80835524866147, + 2.9999212372772712, + 5.458046876567976, + 0.007803281932174618, + 1.3849554785538867, + 8.51477432156869, + 4.656161890950504, + -0.49453412636627153, + -4.9425646575433895, + 1.5754507134189302, + 4.071093984813563, + 1.4821345261234662, + 2.815670718021292, + 1.9423374489222467, + 5.64234093990883, + 1.711516119661729, + 2.5246316441116576, + 5.404768257953342, + -4.933144619659127, + -4.0862479358951616, + 2.414531419272294, + 9.27182973316845, + 2.2064135982664714, + 5.872298049989269, + 1.4429082064623937, + 3.99304417572891, + -1.7976779555774933, + 2.8060601507477996, + 9.544330963742757, + -0.2234861369430893, + 1.1926141918363296, + 2.8595940374636495, + 2.6911712476226657, + 2.458521202978513, + -2.694375476554727, + 3.9776603483448296, + 0.6718385790624146, + 2.37716045025641, + 4.041421509916807, + -1.6349543418763945, + 7.9212454675679185, + 0.7721147216829433, + 8.959113895222348, + 4.982386370940765, + 7.1514158587805365, + 4.073127252924829, + 3.973102469449036, + -1.4206098126378905, + -2.884552512926147, + 1.9047385965830614, + -3.169287508333552, + -1.7392144968569563, + 0.7721044520820948, + 1.927206272793925, + 7.104641765501545, + 3.4505110590561854, + 0.8733130174974519, + 9.266268836620407, + 0.08700274201436375, + 6.052240993895984, + 0.9007896354342442, + 6.173886287536836, + 5.907638810176699, + 1.9606259324926214, + -5.130693452152175, + 3.0132523997110416, + 2.4628682686429966, + 3.3604641188713784, + 5.456330102509671, + 3.1129611127328216, + 4.621919115236012, + 1.6051966558753044, + -2.395592286301594, + 9.415942637822253, + 4.5551016138441645, + 5.462041681472252, + 1.2553444252956512, + 9.125289294805025, + -5.19635425788795, + 0.6479316122636503, + 9.211129276541376, + 4.088576979982686, + 1.778078098119498, + 8.272830237615167, + 5.941247087817222, + 6.483218672754511, + 7.836505848839812, + 9.10777300419422, + 0.8697894759196668, + -1.8048153062968533, + 2.6932056631239343, + 6.270438904360643, + 3.793841292975147, + 5.93176798393608, + 0.5794319523472153, + -1.0634626786112789, + 1.5525130634435254, + 1.9069750675731074, + 5.450872922288456, + 3.352999491608821, + 2.6061036797014023, + 0.46996693176866594, + 3.089799989155103, + 2.1768196533032103, + 7.209632078897733, + 2.9382709469704604, + 5.81830042125279, + 3.0254259849920686, + 7.952336979799083, + 6.556182022826983, + -0.1976664554077333, + 5.064374587699065, + 2.5790684611219734, + 5.416054005071723, + 7.929922860554962, + -4.987485362225389, + -2.365232243588474, + 6.067477778583785, + 2.7165906101272084, + -5.0554658081074315, + 4.826017906934126, + 2.3051068664341603, + 3.2425676342002556, + 1.2996408495052318, + 0.703193985050713, + 2.7805627349610846, + 1.0491896153647293, + 6.821299225780244, + 0.7046460079936201, + -2.953473023647294, + -3.6590226625841997, + 0.4254855627307656, + 4.5834436824020655, + -0.1264574637622443, + 0.062170134272150034, + 1.0044135902927296, + 5.894066030179587, + -5.032302428711589, + 4.3565237912861, + 0.8204878684140501, + 8.542998995034315, + 0.19522163995677777, + -5.105510462957291, + 3.664400379320447, + 9.523795502928193, + 3.1412037401144466, + 2.2838679019892427, + 8.175845575985958, + 1.4812611753280103, + 9.229352687073469, + 7.315547660481734, + 5.826430520451797, + 0.7545773335194408, + -2.2423574079782207, + 6.472756705414401, + 1.37941517062476, + -2.1564940827513968, + 3.372013235866089, + 2.3650725020331045, + 6.568125635833751, + 2.445340791793013, + -0.9932390025071863, + 6.221281466545461, + 7.995363198747193, + 3.5240304820715087, + 3.7052514666884435, + 3.039674786023824, + 8.72316174744997, + 9.686001035805639, + 8.472376648409222, + 0.3369873707042546, + 0.4055299277861853, + 9.055907499616207, + -5.005667267446557, + 7.961191247169811, + 3.229307450854857, + 5.780672932325554, + 6.190242116673266, + 6.102394102049797, + 4.171372057552599, + 1.241947275086106, + -2.0859279953693677, + 9.473391633273852, + 5.49488016127507, + 9.122434389343034, + 1.0929961023746564, + 6.043910303777906, + 5.392428857667593, + 7.4885983371889955, + 8.349677984222893, + 9.566987967368911, + 0.36722542845369793 + ] +] diff --git a/client/__tests__/util/annoMatrix/whereCache.test.js b/client/__tests__/util/annoMatrix/whereCache.test.js new file mode 100644 index 00000000..f9f8e652 --- /dev/null +++ b/client/__tests__/util/annoMatrix/whereCache.test.js @@ -0,0 +1,184 @@ +import { + _whereCacheGet, + _whereCacheCreate, + _whereCacheMerge, +} from "../../../src/annoMatrix/whereCache"; + +const schema = {}; + +describe("whereCache", () => { + test("whereCacheGet - missing cache values", () => { + expect( + _whereCacheGet({}, schema, "X", { + field: "var", + column: "foo", + value: "bar", + }) + ).toEqual([undefined]); + expect( + _whereCacheGet({ X: {} }, schema, "X", { + field: "var", + column: "foo", + value: "bar", + }) + ).toEqual([undefined]); + expect( + _whereCacheGet({ X: { var: new Map() } }, schema, "X", { + field: "var", + column: "foo", + value: "bar", + }) + ).toEqual([undefined]); + expect( + _whereCacheGet( + { X: { var: new Map([["foo", new Map()]]) } }, + schema, + "X", + { + field: "var", + column: "foo", + value: "bar", + } + ) + ).toEqual([undefined]); + }); + + test("whereCacheGet - varied lookups", () => { + const whereCache = { + X: { + var: new Map([ + [ + "foo", + new Map([ + ["bar", [0]], + ["baz", [1, 2]], + ]), + ], + ]), + }, + }; + + expect( + _whereCacheGet(whereCache, schema, "X", { + field: "var", + column: "foo", + value: "bar", + }) + ).toEqual([0]); + expect( + _whereCacheGet(whereCache, schema, "X", { + field: "var", + column: "foo", + value: "baz", + }) + ).toEqual([1, 2]); + expect(_whereCacheGet(whereCache, schema, "Y", {})).toEqual([undefined]); + expect( + _whereCacheGet(whereCache, schema, "X", { + field: "whoknows", + column: "whatever", + value: "snork", + }) + ).toEqual([undefined]); + expect( + _whereCacheGet(whereCache, schema, "X", { + field: "var", + column: "whatever", + value: "snork", + }) + ).toEqual([undefined]); + expect( + _whereCacheGet(whereCache, schema, "X", { + field: "var", + column: "foo", + value: "snork", + }) + ).toEqual([undefined]); + }); + + test("whereCacheCreate", () => { + const query = { + field: "queryField", + column: "queryColumn", + value: "queryValue", + }; + const wc = _whereCacheCreate( + "field", + { field: "queryField", column: "queryColumn", value: "queryValue" }, + [0, 1, 2] + ); + expect(wc).toBeDefined(); + expect(wc).toEqual( + expect.objectContaining({ + field: { + queryField: expect.any(Map), + }, + }) + ); + expect(wc.field.queryField.has("queryColumn")).toEqual(true); + expect(wc.field.queryField.get("queryColumn")).toBeInstanceOf(Map); + expect(wc.field.queryField.get("queryColumn").has("queryValue")).toEqual( + true + ); + expect(_whereCacheGet(wc, schema, "field", query)).toEqual([0, 1, 2]); + }); + + test("whereCacheMerge", () => { + let wc; + + // remember, will mutate dst + const src = _whereCacheCreate( + "field", + { field: "queryField", column: "queryColumn", value: "foo" }, + ["foo"] + ); + + const dst1 = _whereCacheCreate( + "field", + { field: "queryField", column: "queryColumn", value: "bar" }, + ["dst1"] + ); + wc = _whereCacheMerge(dst1, src); + expect( + _whereCacheGet(wc, schema, "field", { + field: "queryField", + column: "queryColumn", + value: "foo", + }) + ).toEqual(["foo"]); + expect( + _whereCacheGet(wc, schema, "field", { + field: "queryField", + column: "queryColumn", + value: "bar", + }) + ).toEqual(["dst1"]); + + const dst2 = _whereCacheCreate( + "field", + { field: "queryField", column: "queryColumn", value: "bar" }, + ["dst2"] + ); + wc = _whereCacheMerge(dst2, dst1, src); + expect( + _whereCacheGet(wc, schema, "field", { + field: "queryField", + column: "queryColumn", + value: "foo", + }) + ).toEqual(["foo"]); + expect( + _whereCacheGet(wc, schema, "field", { + field: "queryField", + column: "queryColumn", + value: "bar", + }) + ).toEqual(["dst1"]); + + wc = _whereCacheMerge({}, src); + expect(wc).toEqual(src); + + wc = _whereCacheMerge({ field: { queryField: new Map() } }, src); + expect(wc).toEqual(src); + }); +}); diff --git a/client/__tests__/util/centroid.test.js b/client/__tests__/util/centroid.test.js index 6b7dcc6e..969bc3c9 100644 --- a/client/__tests__/util/centroid.test.js +++ b/client/__tests__/util/centroid.test.js @@ -1,40 +1,36 @@ import _ from "lodash"; import calcCentroid from "../../src/util/centroid"; - import quantile from "../../src/util/quantile"; -import * as Universe from "../../src/util/stateManager/universe"; import { matrixFBSToDataframe } from "../../src/util/stateManager/matrix"; -import * as World from "../../src/util/stateManager/world"; import * as REST from "./stateManager/sampleResponses"; +import { indexEntireSchema } from "../../src/util/stateManager/schemaHelpers"; +import { _normalizeCategoricalSchema } from "../../src/annoMatrix/schema"; describe("centroid", () => { - let world; + let schema; + let obsAnnotations; + let obsLayout; beforeAll(() => { - // Create world + universe - let universe = Universe.createUniverseFromResponse( - _.cloneDeep(REST.config), - _.cloneDeep(REST.schema) - ); + schema = indexEntireSchema(_.cloneDeep(REST.schema.schema)); + obsAnnotations = matrixFBSToDataframe(REST.annotationsObs); + obsLayout = matrixFBSToDataframe(REST.layoutObs); - universe = { - ...universe, - ...Universe.addObsAnnotations( - universe, - matrixFBSToDataframe(REST.annotationsObs) - ), - ...Universe.addVarAnnotations( - universe, - matrixFBSToDataframe(REST.annotationsVar) - ), - ...Universe.addObsLayout(universe, matrixFBSToDataframe(REST.layoutObs)), - }; - world = World.createWorldFromEntireUniverse(universe); + _normalizeCategoricalSchema( + schema.annotations.obsByName.field3, + obsAnnotations.col("field3") + ); }); test("field4 (categorical obsAnnotation)", () => { - const centroidResult = calcCentroid(world, "field4", ["umap_0", "umap_1"]); + const centroidResult = calcCentroid( + schema, + "field4", + obsAnnotations, + { current: "umap", currentDimNames: ["umap_0", "umap_1"] }, + obsLayout + ); // Check to see that a centroid has been calculated for every categorical value const keysAsArray = Array.from(centroidResult.keys()); @@ -44,8 +40,8 @@ describe("centroid", () => { // This expected result assumes that all cells belong in all categorical values inside of sample response const expectedResult = [ - quantile([0.5], world.obsLayout.col("umap_0").asArray())[0], - quantile([0.5], world.obsLayout.col("umap_1").asArray())[0], + quantile([0.5], obsLayout.col("umap_0").asArray())[0], + quantile([0.5], obsLayout.col("umap_1").asArray())[0], ]; centroidResult.forEach((coordinate) => { @@ -54,7 +50,13 @@ describe("centroid", () => { }); test("field3 (boolean obsAnnotation)", () => { - const centroidResult = calcCentroid(world, "field3", ["umap_0", "umap_1"]); + const centroidResult = calcCentroid( + schema, + "field3", + obsAnnotations, + { current: "umap", currentDimNames: ["umap_0", "umap_1"] }, + obsLayout + ); // Check to see that a centroid has been calculated for every categorical value const keysAsArray = Array.from(centroidResult.keys()); @@ -62,8 +64,8 @@ describe("centroid", () => { // This expected result assumes that all cells belong in all categorical values inside of sample response const expectedResult = [ - quantile([0.5], world.obsLayout.col("umap_0").asArray())[0], - quantile([0.5], world.obsLayout.col("umap_1").asArray())[0], + quantile([0.5], obsLayout.col("umap_0").asArray())[0], + quantile([0.5], obsLayout.col("umap_1").asArray())[0], ]; centroidResult.forEach((coordinate) => { diff --git a/client/__tests__/util/dataframe/dataframe.test.js b/client/__tests__/util/dataframe/dataframe.test.js index bb193a4d..727f0a7f 100644 --- a/client/__tests__/util/dataframe/dataframe.test.js +++ b/client/__tests__/util/dataframe/dataframe.test.js @@ -918,115 +918,342 @@ describe("dataframe col", () => { }); describe("label indexing", () => { - test("IdentityInt32Index", () => { + describe("isLabelIndex", () => { + expect( + Dataframe.isLabelIndex(new Dataframe.IdentityInt32Index(4)) + ).toBeTruthy(); + expect( + Dataframe.isLabelIndex(new Dataframe.DenseInt32Index([2, 4, 99])) + ).toBeTruthy(); + expect( + Dataframe.isLabelIndex(new Dataframe.KeyIndex(["a", 4, "toasty"])) + ).toBeTruthy(); + expect(Dataframe.isLabelIndex(false)).toBeFalsy(); + expect(Dataframe.isLabelIndex(undefined)).toBeFalsy(); + expect(Dataframe.isLabelIndex(null)).toBeFalsy(); + expect(Dataframe.isLabelIndex(true)).toBeFalsy(); + expect(Dataframe.isLabelIndex([])).toBeFalsy(); + expect(Dataframe.isLabelIndex({})).toBeFalsy(); + expect(Dataframe.isLabelIndex(Dataframe.IdentityInt32Index)).toBeFalsy(); + }); + + describe("IdentityInt32Index", () => { const idx = new Dataframe.IdentityInt32Index(12); // [0, 12) - expect(Dataframe.isLabelIndex(idx)).toBeTruthy(); + test("create", () => { + expect(Dataframe.isLabelIndex(idx)).toBeTruthy(); + }); - expect(idx.labels()).toEqual( - new Int32Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) - ); - expect(idx.getLabel(1)).toEqual(1); - expect(idx.getOffset(1)).toEqual(1); - expect(idx.getOffsets([1, 3])).toEqual([1, 3]); - expect(idx.getLabels([1, 3])).toEqual([1, 3]); - expect(idx.size()).toEqual(12); + test("labels", () => { + expect(idx.labels()).toEqual( + new Int32Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) + ); + expect(idx.getLabel(1)).toEqual(1); + expect(idx.getLabels([1, 3])).toEqual([1, 3]); + expect(idx.size()).toEqual(12); + }); - expect(idx.subset([2]).labels()).toEqual([2]); - expect(idx.subset([2, 3, 4]).labels()).toEqual(new Int32Array([2, 3, 4])); - expect(idx.subset([0, 1, 2, 3]).labels()).toEqual( - new Int32Array([0, 1, 2, 3]) - ); + test("offsets", () => { + expect(idx.getOffset(1)).toEqual(1); + expect(idx.getOffsets([1, 3])).toEqual([1, 3]); + }); - expect(idx.isubset([2]).labels()).toEqual([2]); - expect(idx.isubset([2, 3, 4]).labels()).toEqual(new Int32Array([2, 3, 4])); - expect(idx.isubset([0, 1, 2, 3]).labels()).toEqual( - new Int32Array([0, 1, 2, 3]) - ); + test("subset", () => { + expect(idx.subset([2]).labels()).toEqual([2]); + expect(idx.subset([2, 3, 4]).labels()).toEqual(new Int32Array([2, 3, 4])); + expect(idx.subset([0, 1, 2, 3]).labels()).toEqual( + new Int32Array([0, 1, 2, 3]) + ); + expect(idx.subset([0, 1, 2, 3, 4])).toBeInstanceOf( + Dataframe.IdentityInt32Index + ); + expect(idx.subset([2, 1, 0])).toBeInstanceOf( + Dataframe.IdentityInt32Index + ); + expect(idx.subset([1, 2, 3, 4])).toBeInstanceOf( + Dataframe.DenseInt32Index + ); + expect(idx.subset([0, 1, 3, 4])).toBeInstanceOf( + Dataframe.DenseInt32Index + ); + expect(idx.subset([0, 1, 2, 3, 10])).toBeInstanceOf( + Dataframe.DenseInt32Index + ); + expect(idx.subset([4, 3, 2, 1])).toBeInstanceOf( + Dataframe.DenseInt32Index + ); + expect(idx.subset([4])).toBeInstanceOf(Dataframe.KeyIndex); + }); - expect(idx.subset([0, 1, 2, 3, 4])).toBeInstanceOf( - Dataframe.IdentityInt32Index - ); - expect(idx.subset([2, 1, 0])).toBeInstanceOf(Dataframe.IdentityInt32Index); - expect(idx.subset([1, 2, 3, 4])).toBeInstanceOf(Dataframe.DenseInt32Index); - expect(idx.subset([0, 1, 3, 4])).toBeInstanceOf(Dataframe.DenseInt32Index); - expect(idx.subset([0, 1, 2, 3, 10])).toBeInstanceOf( - Dataframe.DenseInt32Index - ); - expect(idx.subset([4, 3, 2, 1])).toBeInstanceOf(Dataframe.DenseInt32Index); - expect(idx.subset([4])).toBeInstanceOf(Dataframe.KeyIndex); + test("isubset", () => { + expect(idx.isubset([2]).labels()).toEqual([2]); + expect(idx.isubset([2, 3, 4]).labels()).toEqual( + new Int32Array([2, 3, 4]) + ); + expect(idx.isubset([0, 1, 2, 3]).labels()).toEqual( + new Int32Array([0, 1, 2, 3]) + ); + expect(() => idx.isubset([-1001])).toThrow(RangeError); + expect(() => idx.isubset([1001])).toThrow(RangeError); + }); - expect(idx.withLabel(99).labels()).toEqual( - new Int32Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 99]) - ); - expect(idx.dropLabel(0).labels()).toEqual( - new Int32Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) - ); - expect(idx.dropLabel(11).labels()).toEqual( - new Int32Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) - ); - expect(idx.dropLabel(5).labels()).toEqual( - new Int32Array([0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11]) - ); + test("isubsetMask", () => { + expect( + idx + .isubsetMask([ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + ]) + .labels() + ).toEqual(new Int32Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])); + expect( + idx + .isubsetMask([ + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + ]) + .labels() + ).toEqual(new Int32Array([])); + expect( + idx + .isubsetMask([ + false, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + ]) + .labels() + ).toEqual(new Int32Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])); + expect( + idx + .isubsetMask([ + false, + true, + true, + true, + true, + true, + true, + true, + true, + true, + false, + true, + ]) + .labels() + ).toEqual(new Int32Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 11])); + expect( + idx + .isubsetMask([ + false, + true, + true, + false, + true, + true, + true, + true, + true, + true, + true, + false, + ]) + .labels() + ).toEqual(new Int32Array([1, 2, 4, 5, 6, 7, 8, 9, 10])); + expect(() => idx.isubsetMask([])).toThrow(RangeError); + }); + + test("withLabel", () => { + expect(idx.withLabel(99).labels()).toEqual( + new Int32Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 99]) + ); + expect(idx.withLabel(12).labels()).toEqual( + new Int32Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) + ); + expect(idx.withLabels([12, 13]).labels()).toEqual( + new Int32Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]) + ); + }); + + test("dropLabel", () => { + expect(idx.dropLabel(0).labels()).toEqual( + new Int32Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) + ); + expect(idx.dropLabel(11).labels()).toEqual( + new Int32Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) + ); + expect(idx.dropLabel(5).labels()).toEqual( + new Int32Array([0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11]) + ); + }); }); - test("DenseInt32Index", () => { + describe("DenseInt32Index", () => { const idx = new Dataframe.DenseInt32Index([99, 1002, 48, 0, 22]); - expect(Dataframe.isLabelIndex(idx)).toBeTruthy(); + test("create", () => { + expect(Dataframe.isLabelIndex(idx)).toBeTruthy(); + }); - expect(idx.labels()).toEqual(new Int32Array([99, 1002, 48, 0, 22])); - expect(idx.size()).toEqual(5); - expect(idx.getOffset(1002)).toEqual(1); - expect(idx.getOffset(0)).toEqual(3); - expect(idx.getLabel(0)).toEqual(99); - expect(idx.getLabels(new Int32Array([2, 4]))).toEqual( - new Int32Array([48, 22]) - ); - expect(idx.getLabels([2, 4])).toEqual([48, 22]); - expect(idx.getOffsets([0, 48])).toEqual([3, 2]); + test("labels", () => { + expect(idx.labels()).toEqual(new Int32Array([99, 1002, 48, 0, 22])); + expect(idx.size()).toEqual(5); + expect(idx.getLabel(0)).toEqual(99); + expect(idx.getLabels(new Int32Array([2, 4]))).toEqual( + new Int32Array([48, 22]) + ); + expect(idx.getLabels([2, 4])).toEqual([48, 22]); + }); - expect(idx.subset([1002, 0, 99]).labels()).toEqual( - new Int32Array([1002, 0, 99]) - ); - expect(idx.getOffsets(idx.subset([1002, 0, 99]).labels())).toEqual( - new Int32Array([1, 3, 0]) - ); - expect(idx.isubset([4, 1, 2]).labels()).toEqual( - new Int32Array([22, 1002, 48]) - ); + test("offsets", () => { + expect(idx.getOffset(1002)).toEqual(1); + expect(idx.getOffset(0)).toEqual(3); + expect(idx.getOffsets([0, 48])).toEqual([3, 2]); + }); - expect(idx.withLabel(88).labels()).toEqual( - new Int32Array([99, 1002, 48, 0, 22, 88]) - ); - expect(idx.withLabel(88).getOffset(88)).toEqual(5); - expect(idx.dropLabel(48).labels()).toEqual( - new Int32Array([99, 1002, 0, 22]) - ); + test("subset", () => { + expect(idx.subset([1002, 0, 99]).labels()).toEqual( + new Int32Array([1002, 0, 99]) + ); + expect(idx.getOffsets(idx.subset([1002, 0, 99]).labels())).toEqual( + new Int32Array([1, 3, 0]) + ); + expect(() => idx.subset([-1])).toThrow(RangeError); + }); + + test("isubset", () => { + expect(idx.isubset([4, 1, 2]).labels()).toEqual( + new Int32Array([22, 1002, 48]) + ); + expect(() => idx.isubset([-1001])).toThrow(RangeError); + expect(() => idx.isubset([1001])).toThrow(RangeError); + }); + + test("isubsetMask", () => { + expect(idx.isubsetMask([true, true, true, true, true]).labels()).toEqual( + new Int32Array([99, 1002, 48, 0, 22]) + ); + expect( + idx.isubsetMask([false, false, false, false, false]).labels() + ).toEqual(new Int32Array([])); + expect(idx.isubsetMask([true, true, false, true, true]).labels()).toEqual( + new Int32Array([99, 1002, 0, 22]) + ); + expect( + idx.isubsetMask([false, true, true, true, false]).labels() + ).toEqual(new Int32Array([1002, 48, 0])); + expect(() => idx.isubsetMask([])).toThrow(RangeError); + }); + + test("withLabel", () => { + expect(idx.withLabel(88).labels()).toEqual( + new Int32Array([99, 1002, 48, 0, 22, 88]) + ); + expect(idx.withLabel(88).getOffset(88)).toEqual(5); + expect(idx.withLabels([88, 99]).labels()).toEqual( + new Int32Array([99, 1002, 48, 0, 22, 88, 99]) + ); + }); + test("dropLabel", () => { + expect(idx.dropLabel(48).labels()).toEqual( + new Int32Array([99, 1002, 0, 22]) + ); + }); }); - test("KeyIndex", () => { + describe("KeyIndex", () => { const idx = new Dataframe.KeyIndex(["red", "green", "blue"]); - expect(Dataframe.isLabelIndex(idx)).toBeTruthy(); + test("create", () => { + expect(Dataframe.isLabelIndex(idx)).toBeTruthy(); + expect(() => new Dataframe.KeyIndex(["dup", "dup"])).toThrow(Error); + expect(new Dataframe.KeyIndex().size()).toEqual(0); + }); - expect(idx.labels()).toEqual(["red", "green", "blue"]); - expect(idx.size()).toEqual(3); - expect(idx.getOffset("blue")).toEqual(2); - expect(idx.getLabel(1)).toEqual("green"); + test("labels", () => { + expect(idx.labels()).toEqual(["red", "green", "blue"]); + expect(idx.size()).toEqual(3); + expect(idx.getLabel(1)).toEqual("green"); + expect(idx.getLabels([2, 0])).toEqual(["blue", "red"]); + }); - expect(idx.subset(["green"]).labels()).toEqual(["green"]); - expect(idx.subset(["green", "red"]).labels()).toEqual(["green", "red"]); - expect(idx.isubset([2, 1, 0]).labels()).toEqual(["blue", "green", "red"]); + test("offsets", () => { + expect(idx.getOffset("blue")).toEqual(2); + }); - expect(idx.withLabel("yo").labels()).toEqual([ - "red", - "green", - "blue", - "yo", - ]); - expect(idx.withLabel("yo").getOffset("yo")).toEqual(3); - expect(idx.dropLabel("blue").labels()).toEqual(["red", "green"]); + test("subset", () => { + expect(idx.subset(["green"]).labels()).toEqual(["green"]); + expect(idx.subset(["green", "red"]).labels()).toEqual(["green", "red"]); + }); + + test("isubset", () => { + expect(idx.isubset([2, 1, 0]).labels()).toEqual(["blue", "green", "red"]); + expect(() => idx.isubset([-1001])).toThrow(RangeError); + expect(() => idx.isubset([1001])).toThrow(RangeError); + }); + + test("isubsetMask", () => { + expect(idx.isubsetMask([true, true, true]).labels()).toEqual([ + "red", + "green", + "blue", + ]); + expect(idx.isubsetMask([false, false, false]).labels()).toEqual([]); + expect(idx.isubsetMask([true, false, true]).labels()).toEqual([ + "red", + "blue", + ]); + expect(() => idx.isubsetMask([])).toThrow(RangeError); + }); + + test("withLabel", () => { + expect(idx.withLabel("yo").labels()).toEqual([ + "red", + "green", + "blue", + "yo", + ]); + expect(idx.withLabel("yo").getOffset("yo")).toEqual(3); + expect(idx.withLabels(["hey", "there"]).labels()).toEqual([ + "red", + "green", + "blue", + "hey", + "there", + ]); + }); + + test("dropLabel", () => { + expect(idx.dropLabel("blue").labels()).toEqual(["red", "green"]); + }); }); }); diff --git a/client/__tests__/util/promiseLimit.test.js b/client/__tests__/util/promiseLimit.test.js index 58424601..b22ff5b1 100644 --- a/client/__tests__/util/promiseLimit.test.js +++ b/client/__tests__/util/promiseLimit.test.js @@ -71,4 +71,26 @@ describe("PromiseLimit", () => { ]); expect(result).toEqual(["OK", "not OK", "OK", "not OK"]); }); + + test("priority queue", async () => { + const plimit = new PromiseLimit(1); + + let finishOrder = 0; + const callback = () => async () => { + await delay(100); + const result = finishOrder; + finishOrder += 1; + return result; + }; + + const result = await Promise.all([ + plimit.add(callback()), + plimit.priorityAdd(4, callback()), + plimit.priorityAdd(0, callback()), + plimit.priorityAdd(1, callback()), + plimit.priorityAdd(-1, callback()), + ]); + + expect(result).toEqual([0, 4, 2, 3, 1]); + }); }); diff --git a/client/__tests__/util/stateManager/universe.test.js b/client/__tests__/util/stateManager/universe.test.js deleted file mode 100644 index bf89058c..00000000 --- a/client/__tests__/util/stateManager/universe.test.js +++ /dev/null @@ -1,90 +0,0 @@ -import * as Universe from "../../../src/util/stateManager/universe"; -import { matrixFBSToDataframe } from "../../../src/util/stateManager/matrix"; -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; - let universe = Universe.createUniverseFromResponse( - REST.config, - REST.schema - ); - 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), - }) - ); - - universe = { - ...universe, - ...Universe.addObsAnnotations( - universe, - matrixFBSToDataframe(REST.annotationsObs) - ), - ...Universe.addVarAnnotations( - universe, - matrixFBSToDataframe(REST.annotationsVar) - ), - ...Universe.addObsLayout(universe, matrixFBSToDataframe(REST.layoutObs)), - }; - - 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.labels()).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 ec6adc76..00000000 --- a/client/__tests__/util/stateManager/world.test.js +++ /dev/null @@ -1,202 +0,0 @@ -import _ from "lodash"; -import * as Universe from "../../../src/util/stateManager/universe"; -import { matrixFBSToDataframe } from "../../../src/util/stateManager/matrix"; -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 */ - let universe = Universe.createUniverseFromResponse( - _.cloneDeep(REST.config), - _.cloneDeep(REST.schema) - ); - - universe = { - ...universe, - ...Universe.addObsAnnotations( - universe, - matrixFBSToDataframe(REST.annotationsObs) - ), - ...Universe.addVarAnnotations( - universe, - matrixFBSToDataframe(REST.annotationsVar) - ), - ...Universe.addObsLayout(universe, matrixFBSToDataframe(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( - _.cloneDeep(REST.config), - _.cloneDeep(REST.schema), - matrixFBSToDataframe(_.cloneDeep(REST.annotationsObs)), - matrixFBSToDataframe(_.cloneDeep(REST.annotationsVar)), - matrixFBSToDataframe(_.cloneDeep(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.labels()).toEqual( - new Int32Array(matchingIndices) - ); - expect(world.obsAnnotations.colIndex.labels()).toEqual( - universe.obsAnnotations.colIndex.labels() - ); - expect(world.obsLayout.rowIndex.labels()).toEqual( - new Int32Array(matchingIndices) - ); - expect(world.obsLayout.colIndex.labels()).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/typedCrossfilter/crossfilter.test.js b/client/__tests__/util/typedCrossfilter/crossfilter.test.js index a65d2949..25ccd863 100644 --- a/client/__tests__/util/typedCrossfilter/crossfilter.test.js +++ b/client/__tests__/util/typedCrossfilter/crossfilter.test.js @@ -253,6 +253,11 @@ describe("ImmutableTypedCrossfilter", () => { p.select("quantity", { mode: "exact", values: v }).countSelected() ).toEqual(_.filter(someData, (d) => v.includes(d.quantity)).length) ); + test("single value exact", () => { + expect( + p.select("quantity", { mode: "exact", values: 2 }).countSelected() + ).toEqual(_.filter(someData, (d) => d.quantity === 2).length); + }); test.each([ [0, 1], [1, 2], @@ -295,6 +300,11 @@ describe("ImmutableTypedCrossfilter", () => { p.select("type", { mode: "exact", values: v }).countSelected() ).toEqual(_.filter(someData, (d) => v.includes(d.type)).length) ); + test("single value exact", () => { + expect( + p.select("type", { mode: "exact", values: "tab" }).countSelected() + ).toEqual(_.filter(someData, (d) => d.type === "tab").length); + }); test("range", () => { expect(() => p.select("type", { mode: "range", lo: 0, hi: 9 })).toThrow( Error diff --git a/client/package-lock.json b/client/package-lock.json index 34c22613..7fa0970c 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -8,7 +8,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", - "dev": true, "requires": { "@babel/highlight": "^7.8.3" } @@ -54,7 +53,6 @@ "version": "7.10.2", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.10.2.tgz", "integrity": "sha512-KQmV9yguEjQsXqyOUGKjS4+3K8/DlOCE2pZcq4augdQmtTy5iv5EHtmMSJ7V4c1BIPjuwtZYqYLCq9Ga+hGBRQ==", - "dev": true, "requires": { "@babel/code-frame": "^7.10.1", "@babel/generator": "^7.10.2", @@ -78,7 +76,6 @@ "version": "7.10.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.1.tgz", "integrity": "sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw==", - "dev": true, "requires": { "@babel/highlight": "^7.10.1" } @@ -87,7 +84,6 @@ "version": "7.10.2", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.10.2.tgz", "integrity": "sha512-AxfBNHNu99DTMvlUPlt1h2+Hn7knPpH5ayJ8OqDWSeLld+Fi2AYBTC/IejWDM9Edcii4UzZRCsbUt0WlSDsDsA==", - "dev": true, "requires": { "@babel/types": "^7.10.2", "jsesc": "^2.5.1", @@ -99,7 +95,6 @@ "version": "7.10.1", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.1.tgz", "integrity": "sha512-fcpumwhs3YyZ/ttd5Rz0xn0TpIwVkN7X0V38B9TWNfVF42KEkhkAAuPCQ3oXmtTRtiPJrmZ0TrfS0GKF0eMaRQ==", - "dev": true, "requires": { "@babel/helper-get-function-arity": "^7.10.1", "@babel/template": "^7.10.1", @@ -110,7 +105,6 @@ "version": "7.10.1", "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.1.tgz", "integrity": "sha512-F5qdXkYGOQUb0hpRaPoetF9AnsXknKjWMZ+wmsIRsp5ge5sFh4c3h1eH2pRTTuy9KKAA2+TTYomGXAtEL2fQEw==", - "dev": true, "requires": { "@babel/types": "^7.10.1" } @@ -119,7 +113,6 @@ "version": "7.10.1", "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.1.tgz", "integrity": "sha512-u7XLXeM2n50gb6PWJ9hoO5oO7JFPaZtrh35t8RqKLT1jFKj9IWeD1zrcrYp1q1qiZTdEarfDWfTIP8nGsu0h5g==", - "dev": true, "requires": { "@babel/types": "^7.10.1" } @@ -128,7 +121,6 @@ "version": "7.10.1", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.1.tgz", "integrity": "sha512-SFxgwYmZ3HZPyZwJRiVNLRHWuW2OgE5k2nrVs6D9Iv4PPnXVffuEHy83Sfx/l4SqF+5kyJXjAyUmrG7tNm+qVg==", - "dev": true, "requires": { "@babel/types": "^7.10.1" } @@ -137,7 +129,6 @@ "version": "7.10.1", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.10.1.tgz", "integrity": "sha512-RLHRCAzyJe7Q7sF4oy2cB+kRnU4wDZY/H2xJFGof+M+SJEGhZsb+GFj5j1AD8NiSaVBJ+Pf0/WObiXu/zxWpFg==", - "dev": true, "requires": { "@babel/helper-module-imports": "^7.10.1", "@babel/helper-replace-supers": "^7.10.1", @@ -152,7 +143,6 @@ "version": "7.10.1", "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.1.tgz", "integrity": "sha512-a0DjNS1prnBsoKx83dP2falChcs7p3i8VMzdrSbfLhuQra/2ENC4sbri34dz/rWmDADsmF1q5GbfaXydh0Jbjg==", - "dev": true, "requires": { "@babel/types": "^7.10.1" } @@ -161,7 +151,6 @@ "version": "7.10.1", "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.1.tgz", "integrity": "sha512-SOwJzEfpuQwInzzQJGjGaiG578UYmyi2Xw668klPWV5n07B73S0a9btjLk/52Mlcxa+5AdIYqws1KyXRfMoB7A==", - "dev": true, "requires": { "@babel/helper-member-expression-to-functions": "^7.10.1", "@babel/helper-optimise-call-expression": "^7.10.1", @@ -173,7 +162,6 @@ "version": "7.10.1", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.1.tgz", "integrity": "sha512-VSWpWzRzn9VtgMJBIWTZ+GP107kZdQ4YplJlCmIrjoLVSi/0upixezHCDG8kpPVTBJpKfxTH01wDhh+jS2zKbw==", - "dev": true, "requires": { "@babel/template": "^7.10.1", "@babel/types": "^7.10.1" @@ -183,7 +171,6 @@ "version": "7.10.1", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.1.tgz", "integrity": "sha512-UQ1LVBPrYdbchNhLwj6fetj46BcFwfS4NllJo/1aJsT+1dLTEnXJL0qHqtY7gPzF8S2fXBJamf1biAXV3X077g==", - "dev": true, "requires": { "@babel/types": "^7.10.1" } @@ -191,14 +178,12 @@ "@babel/helper-validator-identifier": { "version": "7.10.1", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz", - "integrity": "sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw==", - "dev": true + "integrity": "sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw==" }, "@babel/highlight": { "version": "7.10.1", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.1.tgz", "integrity": "sha512-8rMof+gVP8mxYZApLF/JgNDAkdKa+aJt3ZYxF8z6+j/hpeXL7iMsKCPHa2jNMHu/qqBwzQF4OHNoYi8dMA/rYg==", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.1", "chalk": "^2.0.0", @@ -208,14 +193,12 @@ "@babel/parser": { "version": "7.10.2", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.10.2.tgz", - "integrity": "sha512-PApSXlNMJyB4JiGVhCOlzKIif+TKFTvu0aQAhnTvfP/z3vVSN6ZypH5bfUNwFXXjRQtUEBNFd2PtmCmG2Py3qQ==", - "dev": true + "integrity": "sha512-PApSXlNMJyB4JiGVhCOlzKIif+TKFTvu0aQAhnTvfP/z3vVSN6ZypH5bfUNwFXXjRQtUEBNFd2PtmCmG2Py3qQ==" }, "@babel/template": { "version": "7.10.1", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.1.tgz", "integrity": "sha512-OQDg6SqvFSsc9A0ej6SKINWrpJiNonRIniYondK2ViKhB06i3c0s+76XUft71iqBEe9S1OKsHwPAjfHnuvnCig==", - "dev": true, "requires": { "@babel/code-frame": "^7.10.1", "@babel/parser": "^7.10.1", @@ -226,7 +209,6 @@ "version": "7.10.1", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.10.1.tgz", "integrity": "sha512-C/cTuXeKt85K+p08jN6vMDz8vSV0vZcI0wmQ36o6mjbuo++kPMdpOYw23W2XH04dbRt9/nMEfA4W3eR21CD+TQ==", - "dev": true, "requires": { "@babel/code-frame": "^7.10.1", "@babel/generator": "^7.10.1", @@ -243,7 +225,6 @@ "version": "7.10.2", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.2.tgz", "integrity": "sha512-AD3AwWBSz0AWF0AkCN9VPiWrvldXq+/e3cHa4J89vo4ymjz1XwrBFFVZmkJTsQIPNk+ZVomPSXUJqq8yyjZsng==", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.1", "lodash": "^4.17.13", @@ -254,7 +235,6 @@ "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", @@ -267,7 +247,6 @@ "version": "7.8.8", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.8.tgz", "integrity": "sha512-HKyUVu69cZoclptr8t8U5b6sx6zoWjh8jiUhnuj3MpZuKT2dJ8zPTuiy31luq32swhI0SpwItCIlU8XW7BZeJg==", - "dev": true, "requires": { "@babel/types": "^7.8.7", "jsesc": "^2.5.1", @@ -861,7 +840,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", - "dev": true, "requires": { "@babel/helper-get-function-arity": "^7.8.3", "@babel/template": "^7.8.3", @@ -872,7 +850,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", - "dev": true, "requires": { "@babel/types": "^7.8.3" } @@ -963,8 +940,7 @@ "@babel/helper-plugin-utils": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true + "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==" }, "@babel/helper-regex": { "version": "7.8.3", @@ -1014,7 +990,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", - "dev": true, "requires": { "@babel/types": "^7.8.3" } @@ -1041,7 +1016,6 @@ "version": "7.10.1", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.10.1.tgz", "integrity": "sha512-muQNHF+IdU6wGgkaJyhhEmI54MOZBKsFfsXFhboz1ybwJ1Kl7IHlbm2a++4jwrmY5UYsgitt5lfqo1wMFcHmyw==", - "dev": true, "requires": { "@babel/template": "^7.10.1", "@babel/traverse": "^7.10.1", @@ -1052,7 +1026,6 @@ "version": "7.10.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.1.tgz", "integrity": "sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw==", - "dev": true, "requires": { "@babel/highlight": "^7.10.1" } @@ -1061,7 +1034,6 @@ "version": "7.10.2", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.10.2.tgz", "integrity": "sha512-AxfBNHNu99DTMvlUPlt1h2+Hn7knPpH5ayJ8OqDWSeLld+Fi2AYBTC/IejWDM9Edcii4UzZRCsbUt0WlSDsDsA==", - "dev": true, "requires": { "@babel/types": "^7.10.2", "jsesc": "^2.5.1", @@ -1073,7 +1045,6 @@ "version": "7.10.1", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.1.tgz", "integrity": "sha512-fcpumwhs3YyZ/ttd5Rz0xn0TpIwVkN7X0V38B9TWNfVF42KEkhkAAuPCQ3oXmtTRtiPJrmZ0TrfS0GKF0eMaRQ==", - "dev": true, "requires": { "@babel/helper-get-function-arity": "^7.10.1", "@babel/template": "^7.10.1", @@ -1084,7 +1055,6 @@ "version": "7.10.1", "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.1.tgz", "integrity": "sha512-F5qdXkYGOQUb0hpRaPoetF9AnsXknKjWMZ+wmsIRsp5ge5sFh4c3h1eH2pRTTuy9KKAA2+TTYomGXAtEL2fQEw==", - "dev": true, "requires": { "@babel/types": "^7.10.1" } @@ -1093,7 +1063,6 @@ "version": "7.10.1", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.1.tgz", "integrity": "sha512-UQ1LVBPrYdbchNhLwj6fetj46BcFwfS4NllJo/1aJsT+1dLTEnXJL0qHqtY7gPzF8S2fXBJamf1biAXV3X077g==", - "dev": true, "requires": { "@babel/types": "^7.10.1" } @@ -1101,14 +1070,12 @@ "@babel/helper-validator-identifier": { "version": "7.10.1", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz", - "integrity": "sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw==", - "dev": true + "integrity": "sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw==" }, "@babel/highlight": { "version": "7.10.1", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.1.tgz", "integrity": "sha512-8rMof+gVP8mxYZApLF/JgNDAkdKa+aJt3ZYxF8z6+j/hpeXL7iMsKCPHa2jNMHu/qqBwzQF4OHNoYi8dMA/rYg==", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.1", "chalk": "^2.0.0", @@ -1118,14 +1085,12 @@ "@babel/parser": { "version": "7.10.2", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.10.2.tgz", - "integrity": "sha512-PApSXlNMJyB4JiGVhCOlzKIif+TKFTvu0aQAhnTvfP/z3vVSN6ZypH5bfUNwFXXjRQtUEBNFd2PtmCmG2Py3qQ==", - "dev": true + "integrity": "sha512-PApSXlNMJyB4JiGVhCOlzKIif+TKFTvu0aQAhnTvfP/z3vVSN6ZypH5bfUNwFXXjRQtUEBNFd2PtmCmG2Py3qQ==" }, "@babel/template": { "version": "7.10.1", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.1.tgz", "integrity": "sha512-OQDg6SqvFSsc9A0ej6SKINWrpJiNonRIniYondK2ViKhB06i3c0s+76XUft71iqBEe9S1OKsHwPAjfHnuvnCig==", - "dev": true, "requires": { "@babel/code-frame": "^7.10.1", "@babel/parser": "^7.10.1", @@ -1136,7 +1101,6 @@ "version": "7.10.1", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.10.1.tgz", "integrity": "sha512-C/cTuXeKt85K+p08jN6vMDz8vSV0vZcI0wmQ36o6mjbuo++kPMdpOYw23W2XH04dbRt9/nMEfA4W3eR21CD+TQ==", - "dev": true, "requires": { "@babel/code-frame": "^7.10.1", "@babel/generator": "^7.10.1", @@ -1153,7 +1117,6 @@ "version": "7.10.2", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.2.tgz", "integrity": "sha512-AD3AwWBSz0AWF0AkCN9VPiWrvldXq+/e3cHa4J89vo4ymjz1XwrBFFVZmkJTsQIPNk+ZVomPSXUJqq8yyjZsng==", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.1", "lodash": "^4.17.13", @@ -1164,7 +1127,6 @@ "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", @@ -1177,7 +1139,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", - "dev": true, "requires": { "chalk": "^2.0.0", "esutils": "^2.0.2", @@ -1188,7 +1149,6 @@ "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", @@ -1200,8 +1160,7 @@ "@babel/parser": { "version": "7.8.8", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.8.tgz", - "integrity": "sha512-mO5GWzBPsPf6865iIbzNE0AvkKF3NE+2S3eRUpE+FE07BOAkXh6G+GW/Pj01hhXjve1WScbaIO4UlY1JKeqCcA==", - "dev": true + "integrity": "sha512-mO5GWzBPsPf6865iIbzNE0AvkKF3NE+2S3eRUpE+FE07BOAkXh6G+GW/Pj01hhXjve1WScbaIO4UlY1JKeqCcA==" }, "@babel/plugin-proposal-async-generator-functions": { "version": "7.8.3", @@ -1421,7 +1380,6 @@ "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" } @@ -1430,7 +1388,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" } @@ -1439,7 +1396,6 @@ "version": "7.10.1", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.1.tgz", "integrity": "sha512-Gf2Yx/iRs1JREDtVZ56OrjjgFHCaldpTnuy9BHla10qyVT3YkIIGEtoDWhyop0ksu1GvNjHIoYRBqm3zoR1jyQ==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.1" }, @@ -1447,8 +1403,7 @@ "@babel/helper-plugin-utils": { "version": "7.10.1", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.1.tgz", - "integrity": "sha512-fvoGeXt0bJc7VMWZGCAEBEMo/HAjW2mP8apF5eXK0wSqwLAVHAISCWRoLMBMUs2kqeaG77jltVqu4Hn8Egl3nA==", - "dev": true + "integrity": "sha512-fvoGeXt0bJc7VMWZGCAEBEMo/HAjW2mP8apF5eXK0wSqwLAVHAISCWRoLMBMUs2kqeaG77jltVqu4Hn8Egl3nA==" } } }, @@ -1508,7 +1463,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" } @@ -1534,7 +1488,6 @@ "version": "7.10.1", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.1.tgz", "integrity": "sha512-XyHIFa9kdrgJS91CUH+ccPVTnJShr8nLGc5bG2IhGXv5p1Rd+8BleGE5yzIg2Nc1QZAdHDa0Qp4m6066OL96Iw==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.1" }, @@ -1542,8 +1495,7 @@ "@babel/helper-plugin-utils": { "version": "7.10.1", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.1.tgz", - "integrity": "sha512-fvoGeXt0bJc7VMWZGCAEBEMo/HAjW2mP8apF5eXK0wSqwLAVHAISCWRoLMBMUs2kqeaG77jltVqu4Hn8Egl3nA==", - "dev": true + "integrity": "sha512-fvoGeXt0bJc7VMWZGCAEBEMo/HAjW2mP8apF5eXK0wSqwLAVHAISCWRoLMBMUs2kqeaG77jltVqu4Hn8Egl3nA==" } } }, @@ -1551,7 +1503,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" } @@ -1560,7 +1511,6 @@ "version": "7.10.1", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.1.tgz", "integrity": "sha512-uTd0OsHrpe3tH5gRPTxG8Voh99/WCU78vIm5NMRYPAqC8lR4vajt6KkCAknCHrx24vkPdd/05yfdGSB4EIY2mg==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.1" }, @@ -1568,8 +1518,7 @@ "@babel/helper-plugin-utils": { "version": "7.10.1", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.1.tgz", - "integrity": "sha512-fvoGeXt0bJc7VMWZGCAEBEMo/HAjW2mP8apF5eXK0wSqwLAVHAISCWRoLMBMUs2kqeaG77jltVqu4Hn8Egl3nA==", - "dev": true + "integrity": "sha512-fvoGeXt0bJc7VMWZGCAEBEMo/HAjW2mP8apF5eXK0wSqwLAVHAISCWRoLMBMUs2kqeaG77jltVqu4Hn8Egl3nA==" } } }, @@ -1577,7 +1526,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" } @@ -1586,7 +1534,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" } @@ -1595,7 +1542,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" } @@ -4068,7 +4014,6 @@ "version": "7.8.6", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", - "dev": true, "requires": { "@babel/code-frame": "^7.8.3", "@babel/parser": "^7.8.6", @@ -4079,7 +4024,6 @@ "version": "7.8.6", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.6.tgz", "integrity": "sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==", - "dev": true, "requires": { "@babel/code-frame": "^7.8.3", "@babel/generator": "^7.8.6", @@ -4096,7 +4040,6 @@ "version": "7.8.7", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", - "dev": true, "requires": { "esutils": "^2.0.2", "lodash": "^4.17.13", @@ -4106,8 +4049,7 @@ "@bcoe/v8-coverage": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" }, "@blueprintjs/core": { "version": "3.28.1", @@ -4150,7 +4092,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", - "dev": true, "requires": { "exec-sh": "^0.3.2", "minimist": "^1.2.0" @@ -4199,7 +4140,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, "requires": { "camelcase": "^5.3.1", "find-up": "^4.1.0", @@ -4212,7 +4152,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, "requires": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -4222,7 +4161,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, "requires": { "p-locate": "^4.1.0" } @@ -4231,7 +4169,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, "requires": { "p-limit": "^2.2.0" } @@ -4239,22 +4176,19 @@ "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" } } }, "@istanbuljs/schema": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", - "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", - "dev": true + "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==" }, "@jest/console": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.0.1.tgz", "integrity": "sha512-9t1KUe/93coV1rBSxMmBAOIK3/HVpwxArCA1CxskKyRiv6o8J70V8C/V3OJminVCTa2M0hQI9AWRd5wxu2dAHw==", - "dev": true, "requires": { "@jest/types": "^26.0.1", "chalk": "^4.0.0", @@ -4267,7 +4201,6 @@ "version": "26.0.1", "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.0.1.tgz", "integrity": "sha512-Xq3eqYnxsG9SjDC+WLeIgf7/8KU6rddBxH+SCt18gEpOhAGYC/Mq+YbtlNcIdwjnnT+wDseXSbU0e5X84Y4jTQ==", - "dev": true, "requires": { "@jest/console": "^26.0.1", "@jest/reporters": "^26.0.1", @@ -4301,14 +4234,12 @@ "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" }, "strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, "requires": { "ansi-regex": "^5.0.0" } @@ -4319,7 +4250,6 @@ "version": "26.0.1", "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.0.1.tgz", "integrity": "sha512-xBDxPe8/nx251u0VJ2dFAFz2H23Y98qdIaNwnMK6dFQr05jc+Ne/2np73lOAx+5mSBO/yuQldRrQOf6hP1h92g==", - "dev": true, "requires": { "@jest/fake-timers": "^26.0.1", "@jest/types": "^26.0.1", @@ -4330,7 +4260,6 @@ "version": "26.0.1", "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.0.1.tgz", "integrity": "sha512-Oj/kCBnTKhm7CR+OJSjZty6N1bRDr9pgiYQr4wY221azLz5PHi08x/U+9+QpceAYOWheauLP8MhtSVFrqXQfhg==", - "dev": true, "requires": { "@jest/types": "^26.0.1", "@sinonjs/fake-timers": "^6.0.1", @@ -4343,7 +4272,6 @@ "version": "26.0.1", "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.0.1.tgz", "integrity": "sha512-iuucxOYB7BRCvT+TYBzUqUNuxFX1hqaR6G6IcGgEqkJ5x4htNKo1r7jk1ji9Zj8ZMiMw0oB5NaA7k5Tx6MVssA==", - "dev": true, "requires": { "@jest/environment": "^26.0.1", "@jest/types": "^26.0.1", @@ -4354,7 +4282,6 @@ "version": "26.0.1", "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.0.1.tgz", "integrity": "sha512-NWWy9KwRtE1iyG/m7huiFVF9YsYv/e+mbflKRV84WDoJfBqUrNRyDbL/vFxQcYLl8IRqI4P3MgPn386x76Gf2g==", - "dev": true, "requires": { "@bcoe/v8-coverage": "^0.2.3", "@jest/console": "^26.0.1", @@ -4386,14 +4313,12 @@ "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" }, "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 + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" } } }, @@ -4401,7 +4326,6 @@ "version": "26.0.0", "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.0.0.tgz", "integrity": "sha512-S2Z+Aj/7KOSU2TfW0dyzBze7xr95bkm5YXNUqqCek+HE0VbNNSNzrRwfIi5lf7wvzDTSS0/ib8XQ1krFNyYgbQ==", - "dev": true, "requires": { "callsites": "^3.0.0", "graceful-fs": "^4.2.4", @@ -4411,14 +4335,12 @@ "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" }, "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 + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" } } }, @@ -4426,7 +4348,6 @@ "version": "26.0.1", "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.0.1.tgz", "integrity": "sha512-oKwHvOI73ICSYRPe8WwyYPTtiuOAkLSbY8/MfWF3qDEd/sa8EDyZzin3BaXTqufir/O/Gzea4E8Zl14XU4Mlyg==", - "dev": true, "requires": { "@jest/console": "^26.0.1", "@jest/types": "^26.0.1", @@ -4438,7 +4359,6 @@ "version": "26.0.1", "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.0.1.tgz", "integrity": "sha512-ssga8XlwfP8YjbDcmVhwNlrmblddMfgUeAkWIXts1V22equp2GMIHxm7cyeD5Q/B0ZgKPK/tngt45sH99yLLGg==", - "dev": true, "requires": { "@jest/test-result": "^26.0.1", "graceful-fs": "^4.2.4", @@ -4450,8 +4370,7 @@ "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" } } }, @@ -4459,7 +4378,6 @@ "version": "26.0.1", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.0.1.tgz", "integrity": "sha512-pPRkVkAQ91drKGbzCfDOoHN838+FSbYaEAvBXvKuWeeRRUD8FjwXkqfUNUZL6Ke48aA/1cqq/Ni7kVMCoqagWA==", - "dev": true, "requires": { "@babel/core": "^7.1.0", "@jest/types": "^26.0.1", @@ -4481,14 +4399,12 @@ "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" }, "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 + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" } } }, @@ -4496,7 +4412,6 @@ "version": "26.0.1", "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", - "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^1.1.1", @@ -5190,7 +5105,6 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.0.tgz", "integrity": "sha512-wEj54PfsZ5jGSwMX68G8ZXFawcSglQSXqCftWX3ec8MDUzQdHgcKvw97awHbY0efQEL5iKUOAmmVtoYgmrSG4Q==", - "dev": true, "requires": { "type-detect": "4.0.8" } @@ -5199,7 +5113,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", - "dev": true, "requires": { "@sinonjs/commons": "^1.7.0" } @@ -5220,7 +5133,6 @@ "version": "7.1.8", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.8.tgz", "integrity": "sha512-KXBiQG2OXvaPWFPDS1rD8yV9vO0OuWIqAEqLsbfX0oU2REN5KuoMnZ1gClWcBhO5I3n6oTVAmrMufOvRqdmFTQ==", - "dev": true, "requires": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0", @@ -5233,7 +5145,6 @@ "version": "7.6.1", "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.1.tgz", "integrity": "sha512-bBKm+2VPJcMRVwNhxKu8W+5/zT7pwNEqeokFOmbvVSqGzFneNxYcEBro9Ac7/N9tlsaPYnZLK8J1LWKkMsLAew==", - "dev": true, "requires": { "@babel/types": "^7.0.0" } @@ -5242,7 +5153,6 @@ "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" @@ -5252,7 +5162,6 @@ "version": "7.0.12", "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.12.tgz", "integrity": "sha512-t4CoEokHTfcyfb4hUaF9oOHu9RmmNWnm1CP0YmMqOOfClKascOmvlEM736vlqeScuGvBDsHkf8R2INd4DWreQA==", - "dev": true, "requires": { "@babel/types": "^7.3.0" } @@ -5260,8 +5169,7 @@ "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", - "dev": true + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" }, "@types/dom4": { "version": "2.0.1", @@ -5298,7 +5206,6 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.3.tgz", "integrity": "sha512-AiHRaEB50LQg0pZmm659vNBb9f4SJ0qrAnteuzhSeAUcJKxoYgEnprg/83kppCnc2zvtCKbdZry1a5pVY3lOTQ==", - "dev": true, "requires": { "@types/node": "*" } @@ -5312,14 +5219,12 @@ "@types/istanbul-lib-coverage": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.2.tgz", - "integrity": "sha512-rsZg7eL+Xcxsxk2XlBt9KcG8nOp9iYdKCOikY9x2RFJCyOdNj4MKPQty0e8oZr29vVAzKXr1BmR+kZauti3o1w==", - "dev": true + "integrity": "sha512-rsZg7eL+Xcxsxk2XlBt9KcG8nOp9iYdKCOikY9x2RFJCyOdNj4MKPQty0e8oZr29vVAzKXr1BmR+kZauti3o1w==" }, "@types/istanbul-lib-report": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "dev": true, "requires": { "@types/istanbul-lib-coverage": "*" } @@ -5328,7 +5233,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", - "dev": true, "requires": { "@types/istanbul-lib-coverage": "*", "@types/istanbul-lib-report": "*" @@ -5355,26 +5259,22 @@ "@types/node": { "version": "13.11.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-13.11.0.tgz", - "integrity": "sha512-uM4mnmsIIPK/yeO+42F2RQhGUIs39K2RFmugcJANppXe6J1nvH87PvzPZYpza7Xhhs8Yn9yIAVdLZ84z61+0xQ==", - "dev": true + "integrity": "sha512-uM4mnmsIIPK/yeO+42F2RQhGUIs39K2RFmugcJANppXe6J1nvH87PvzPZYpza7Xhhs8Yn9yIAVdLZ84z61+0xQ==" }, "@types/normalize-package-data": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", - "dev": true + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==" }, "@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" }, "@types/prettier": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.0.1.tgz", - "integrity": "sha512-boy4xPNEtiw6N3abRhBi/e7hNvy3Tt8E9ZRAQrwAGzoCGZS/1wjo9KY7JHhnfnEsG5wSjDbymCozUM9a3ea7OQ==", - "dev": true + "integrity": "sha512-boy4xPNEtiw6N3abRhBi/e7hNvy3Tt8E9ZRAQrwAGzoCGZS/1wjo9KY7JHhnfnEsG5wSjDbymCozUM9a3ea7OQ==" }, "@types/q": { "version": "1.5.2", @@ -5391,8 +5291,7 @@ "@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 + "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==" }, "@types/tapable": { "version": "1.0.5", @@ -5462,7 +5361,6 @@ "version": "15.0.5", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz", "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==", - "dev": true, "requires": { "@types/yargs-parser": "*" } @@ -5470,8 +5368,7 @@ "@types/yargs-parser": { "version": "15.0.0", "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz", - "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", - "dev": true + "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==" }, "@types/yauzl": { "version": "2.9.1", @@ -5707,8 +5604,7 @@ "abab": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz", - "integrity": "sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg==", - "dev": true + "integrity": "sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg==" }, "accepts": { "version": "1.3.7", @@ -5723,14 +5619,12 @@ "acorn": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.2.0.tgz", - "integrity": "sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ==", - "dev": true + "integrity": "sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ==" }, "acorn-globals": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", - "dev": true, "requires": { "acorn": "^7.1.1", "acorn-walk": "^7.1.1" @@ -5745,8 +5639,7 @@ "acorn-walk": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.1.1.tgz", - "integrity": "sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==", - "dev": true + "integrity": "sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==" }, "agent-base": { "version": "6.0.0", @@ -5779,7 +5672,6 @@ "version": "6.12.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", - "dev": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -5857,7 +5749,6 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", - "dev": true, "requires": { "type-fest": "^0.11.0" }, @@ -5865,22 +5756,19 @@ "type-fest": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", - "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", - "dev": true + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==" } } }, "ansi-regex": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" }, "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" } @@ -5895,7 +5783,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, "requires": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -5921,7 +5808,6 @@ "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" } @@ -5945,20 +5831,17 @@ "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 + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" }, "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 + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" }, "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 + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" }, "array-find-index": { "version": "1.0.2", @@ -5987,7 +5870,6 @@ "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" } @@ -5995,14 +5877,12 @@ "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 + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" }, "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 + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" }, "array.prototype.flat": { "version": "1.2.3", @@ -6024,7 +5904,6 @@ "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" } @@ -6078,14 +5957,12 @@ "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, "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 + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" }, "ast-types-flow": { "version": "0.0.7", @@ -6109,14 +5986,12 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "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 + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" }, "author-regex": { "version": "1.0.0", @@ -6127,14 +6002,12 @@ "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 + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", - "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==", - "dev": true + "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==" }, "axobject-query": { "version": "2.1.2", @@ -6160,7 +6033,6 @@ "version": "26.0.1", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.0.1.tgz", "integrity": "sha512-Z4GGmSNQ8pX3WS1O+6v3fo41YItJJZsVxG5gIQ+HuB/iuAQBJxMTHTwz292vuYws1LnHfwSRgoqI+nxdy/pcvw==", - "dev": true, "requires": { "@jest/transform": "^26.0.1", "@jest/types": "^26.0.1", @@ -6175,8 +6047,7 @@ "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" } } }, @@ -6218,7 +6089,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", "@istanbuljs/load-nyc-config": "^1.0.0", @@ -6231,7 +6101,6 @@ "version": "26.0.0", "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.0.0.tgz", "integrity": "sha512-+AuoehOrjt9irZL7DOt2+4ZaTM6dlu1s5TTS46JBa0/qem4dy7VNW3tMb96qeEqcIh20LD73TVNtmVEeymTG7w==", - "dev": true, "requires": { "@babel/template": "^7.3.3", "@babel/types": "^7.3.3", @@ -6242,7 +6111,6 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.2.tgz", "integrity": "sha512-u/8cS+dEiK1SFILbOC8/rUI3ml9lboKuuMvZ/4aQnQmhecQAgPw5ew066C1ObnEAUmlx7dv/s2z52psWEtLNiw==", - "dev": true, "requires": { "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-bigint": "^7.8.3", @@ -6260,7 +6128,6 @@ "version": "26.0.0", "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.0.0.tgz", "integrity": "sha512-9ce+DatAa31DpR4Uir8g4Ahxs5K4W4L8refzt+qHWQANb6LhGcAEfIFgLUwk67oya2cCUd6t4eUMtO/z64ocNw==", - "dev": true, "requires": { "babel-plugin-jest-hoist": "^26.0.0", "babel-preset-current-node-syntax": "^0.1.2" @@ -6291,14 +6158,12 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "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", @@ -6313,7 +6178,6 @@ "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" } @@ -6322,7 +6186,6 @@ "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" } @@ -6331,7 +6194,6 @@ "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" } @@ -6340,7 +6202,6 @@ "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", @@ -6359,7 +6220,6 @@ "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" } @@ -6383,6 +6243,16 @@ "dev": true, "optional": true }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, "bl": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz", @@ -6545,7 +6415,6 @@ "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" @@ -6555,7 +6424,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, "requires": { "fill-range": "^7.0.1" } @@ -6569,8 +6437,7 @@ "browser-process-hrtime": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "dev": true + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" }, "browserify-aes": { "version": "1.2.0", @@ -6688,7 +6555,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, "requires": { "node-int64": "^0.4.0" } @@ -6740,8 +6606,7 @@ "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 + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" }, "buffer-json": { "version": "2.0.0", @@ -6805,7 +6670,6 @@ "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", @@ -6942,8 +6806,7 @@ "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" }, "camelcase": { "version": "5.3.1", @@ -6990,7 +6853,6 @@ "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" } @@ -7004,14 +6866,12 @@ "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, "chalk": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", - "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -7021,7 +6881,6 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, "requires": { "@types/color-name": "^1.1.1", "color-convert": "^2.0.1" @@ -7031,7 +6890,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "requires": { "color-name": "~1.1.4" } @@ -7039,20 +6897,17 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "supports-color": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -7062,8 +6917,7 @@ "char-regex": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==" }, "chardet": { "version": "0.7.0", @@ -7159,8 +7013,7 @@ "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 + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" }, "cipher-base": { "version": "1.0.4", @@ -7176,7 +7029,6 @@ "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", @@ -7188,7 +7040,6 @@ "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" } @@ -7389,7 +7240,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, "requires": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -7400,7 +7250,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, "requires": { "ansi-regex": "^5.0.0" } @@ -7463,8 +7312,7 @@ "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" }, "coa": { "version": "2.0.2", @@ -7512,14 +7360,12 @@ "collect-v8-coverage": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", - "dev": true + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==" }, "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" @@ -7539,7 +7385,6 @@ "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" } @@ -7547,8 +7392,7 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "color-string": { "version": "1.5.3", @@ -7570,7 +7414,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "requires": { "delayed-stream": "~1.0.0" } @@ -7595,14 +7438,12 @@ "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" }, "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 + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "concat-stream": { "version": "1.6.2", @@ -7713,7 +7554,6 @@ "version": "1.7.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, "requires": { "safe-buffer": "~5.1.1" } @@ -7758,8 +7598,7 @@ "copy-descriptor": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" }, "copy-webpack-plugin": { "version": "5.1.1", @@ -7814,8 +7653,7 @@ "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 + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "cosmiconfig": { "version": "5.2.1", @@ -7920,11 +7758,19 @@ "warning": "^4.0.3" } }, + "cross-fetch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.5.tgz", + "integrity": "sha512-FFLcLtraisj5eteosnX1gf01qYDCOc4fDy0+euOt8Kn9YBY2NtXL/pCoYPavw24NIQkQqm5ZOLsGD5Zzj0gyew==", + "dev": true, + "requires": { + "node-fetch": "2.6.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", @@ -8187,14 +8033,12 @@ "cssom": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", - "dev": true + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==" }, "cssstyle": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "dev": true, "requires": { "cssom": "~0.3.6" }, @@ -8202,8 +8046,7 @@ "cssom": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" } } }, @@ -8506,7 +8349,6 @@ "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" } @@ -8515,7 +8357,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", - "dev": true, "requires": { "abab": "^2.0.3", "whatwg-mimetype": "^2.3.0", @@ -8526,7 +8367,6 @@ "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" } @@ -8534,20 +8374,17 @@ "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" }, "decimal.js": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.0.tgz", - "integrity": "sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw==", - "dev": true + "integrity": "sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw==" }, "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=", - "dev": true + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" }, "decompress-response": { "version": "4.2.1", @@ -8586,14 +8423,12 @@ "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 + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" }, "deepmerge": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "dev": true + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" }, "define-properties": { "version": "1.1.3", @@ -8607,7 +8442,6 @@ "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" @@ -8617,7 +8451,6 @@ "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" } @@ -8626,7 +8459,6 @@ "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" } @@ -8635,7 +8467,6 @@ "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", @@ -8699,8 +8530,7 @@ "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 + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, "delegates": { "version": "1.0.0", @@ -8745,14 +8575,12 @@ "detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==" }, "diff-sequences": { "version": "26.0.0", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.0.0.tgz", - "integrity": "sha512-JC/eHYEC3aSS0vZGjuoc4vHA0yAQTzhQQldXMeMF+JlxLGJlCO38Gma82NV9gk1jGFz8mDzUMeaKXvjRRdJ2dg==", - "dev": true + "integrity": "sha512-JC/eHYEC3aSS0vZGjuoc4vHA0yAQTzhQQldXMeMF+JlxLGJlCO38Gma82NV9gk1jGFz8mDzUMeaKXvjRRdJ2dg==" }, "diffie-hellman": { "version": "5.0.3", @@ -8862,7 +8690,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "dev": true, "requires": { "webidl-conversions": "^5.0.0" }, @@ -8870,8 +8697,7 @@ "webidl-conversions": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==" } } }, @@ -8898,7 +8724,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.3.tgz", "integrity": "sha512-7hwEmg6RiSQfm/GwPL4AAWXKy3YNNZA3oFv2Pdiey0mwkRCPZ9x6SZbkLcn8Ma5PYeVokzoD4Twv2n7LKp5WeA==", - "dev": true, "requires": { "no-case": "^3.0.3", "tslib": "^1.10.0" @@ -8908,7 +8733,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.1.tgz", "integrity": "sha512-LiWgfDLLb1dwbFQZsSglpRj+1ctGnayXz3Uv0/WO8n558JycT5fg6zkNcnW0G68Nn0aEldTFeEfmjCfmqry/rQ==", - "dev": true, "requires": { "tslib": "^1.10.0" } @@ -8917,7 +8741,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.3.tgz", "integrity": "sha512-ehY/mVQCf9BL0gKfsJBvFJen+1V//U+0HQMPrWct40ixE4jnv0bfvxDbWtAHL9EcaPEOJHVVYKoQn1TlZUB8Tw==", - "dev": true, "requires": { "lower-case": "^2.0.1", "tslib": "^1.10.0" @@ -8956,7 +8779,6 @@ "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" @@ -9000,8 +8822,7 @@ "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "emojis-list": { "version": "3.0.0", @@ -9019,7 +8840,6 @@ "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, "requires": { "once": "^1.4.0" } @@ -9075,7 +8895,6 @@ "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" } @@ -9123,14 +8942,12 @@ "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 + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "escodegen": { "version": "1.14.2", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.2.tgz", "integrity": "sha512-InuOIiKk8wwuOFg6x9BQXbzjrQhtyXh46K9bqVTPzSo2FnyMBaYGBMC6PhQy7yxxil9vIedFBweQBMK74/7o8A==", - "dev": true, "requires": { "esprima": "^4.0.1", "estraverse": "^4.2.0", @@ -9143,7 +8960,6 @@ "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" @@ -9153,7 +8969,6 @@ "version": "0.8.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, "requires": { "deep-is": "~0.1.3", "fast-levenshtein": "~2.0.6", @@ -9166,21 +8981,18 @@ "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 + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" }, "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 }, "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" } @@ -9379,6 +9191,25 @@ "ms": "2.0.0" } }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "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=" + } + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -9704,8 +9535,7 @@ "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 + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, "esquery": { "version": "1.3.1", @@ -9736,14 +9566,12 @@ "estraverse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" }, "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" }, "etag": { "version": "1.8.1", @@ -9770,14 +9598,12 @@ "exec-sh": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", - "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==", - "dev": true + "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==" }, "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", @@ -9797,14 +9623,12 @@ "exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=" }, "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", @@ -9815,11 +9639,20 @@ "to-regex": "^3.0.1" }, "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.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" } @@ -9828,7 +9661,6 @@ "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" } @@ -9837,7 +9669,6 @@ "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" } @@ -9845,8 +9676,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } }, @@ -9869,7 +9699,6 @@ "version": "26.0.1", "resolved": "https://registry.npmjs.org/expect/-/expect-26.0.1.tgz", "integrity": "sha512-QcCy4nygHeqmbw564YxNbHTJlXh47dVID2BUP52cZFpLU9zHViMFK6h07cC1wf7GYCTIigTdAXhVua8Yl1FkKg==", - "dev": true, "requires": { "@jest/types": "^26.0.1", "ansi-styles": "^4.0.0", @@ -9883,7 +9712,6 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, "requires": { "@types/color-name": "^1.1.1", "color-convert": "^2.0.1" @@ -9893,7 +9721,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "requires": { "color-name": "~1.1.4" } @@ -9901,8 +9728,7 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" } } }, @@ -9970,14 +9796,12 @@ "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, "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" @@ -9987,7 +9811,6 @@ "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" } @@ -10009,7 +9832,6 @@ "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", @@ -10025,7 +9847,6 @@ "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" } @@ -10034,7 +9855,6 @@ "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" } @@ -10043,7 +9863,6 @@ "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" } @@ -10052,7 +9871,6 @@ "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" } @@ -10061,12 +9879,16 @@ "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" } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } }, @@ -10096,14 +9918,12 @@ "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, "fast-deep-equal": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", - "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", - "dev": true + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" }, "fast-diff": { "version": "1.2.0", @@ -10114,14 +9934,12 @@ "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, "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 + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" }, "favicons": { "version": "5.5.0", @@ -10285,7 +10103,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", - "dev": true, "requires": { "bser": "2.1.1" } @@ -10333,6 +10150,11 @@ "schema-utils": "^2.6.5" }, "dependencies": { + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "loader-utils": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", @@ -10362,11 +10184,17 @@ "integrity": "sha512-Qe/5NJrgIOlwijpq3B7BEpzPFcgzggOTagZmkXQY4LA6bsXKTUstK7Wp12lEJ/mLKTpvIZxmIuRcLYWT6ov9lw==", "dev": true }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, "requires": { "to-regex-range": "^5.0.1" } @@ -10702,8 +10530,7 @@ "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" }, "for-own": { "version": "0.1.5", @@ -10717,14 +10544,12 @@ "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" }, "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", @@ -10741,7 +10566,6 @@ "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" } @@ -10809,14 +10633,12 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "dev": true, "optional": true }, "function-bind": { @@ -10891,14 +10713,12 @@ "gensync": { "version": "1.0.0-beta.1", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", - "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", - "dev": true + "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==" }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, "get-own-enumerable-property-symbols": { "version": "3.0.2", @@ -10909,8 +10729,7 @@ "get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==" }, "get-stdin": { "version": "6.0.0", @@ -10922,7 +10741,6 @@ "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" } @@ -10930,14 +10748,12 @@ "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" }, "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" } @@ -10967,7 +10783,6 @@ "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -10995,6 +10810,11 @@ "requires": { "is-extglob": "^2.1.0" } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } }, @@ -11066,8 +10886,7 @@ "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" }, "globby": { "version": "7.1.1", @@ -11134,7 +10953,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", - "dev": true, "optional": true }, "gud": { @@ -11145,14 +10963,12 @@ "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" }, "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" @@ -11169,8 +10985,7 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, "has-symbols": { "version": "1.0.1", @@ -11187,7 +11002,6 @@ "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", @@ -11198,7 +11012,6 @@ "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" @@ -11208,7 +11021,6 @@ "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" }, @@ -11217,7 +11029,6 @@ "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" } @@ -11228,7 +11039,6 @@ "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" } @@ -11318,8 +11128,7 @@ "hosted-git-info": { "version": "2.8.8", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", - "dev": true + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==" }, "hsl-regex": { "version": "1.0.0", @@ -11343,7 +11152,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", - "dev": true, "requires": { "whatwg-encoding": "^1.0.5" } @@ -11351,8 +11159,7 @@ "html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==" }, "html-minifier-terser": { "version": "5.0.5", @@ -11502,7 +11309,6 @@ "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", @@ -11536,8 +11342,7 @@ "human-signals": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", - "dev": true + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==" }, "husky": { "version": "4.2.5", @@ -11690,17 +11495,24 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", - "dev": true, "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" }, "dependencies": { + "param-case": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.3.tgz", + "integrity": "sha512-VWBVyimc1+QrzappRs7waeN2YmoZFCGXWASRYX1/rGHtXqEcrGEIDm+jqIwFa2fRXNgQEwrxaYuIrX0WcAguTA==", + "requires": { + "dot-case": "^3.0.3", + "tslib": "^1.10.0" + } + }, "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 + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" } } }, @@ -11714,7 +11526,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", - "dev": true, "requires": { "pkg-dir": "^4.2.0", "resolve-cwd": "^3.0.0" @@ -11724,7 +11535,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, "requires": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -11734,7 +11544,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, "requires": { "p-locate": "^4.1.0" } @@ -11743,7 +11552,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, "requires": { "p-limit": "^2.2.0" } @@ -11751,14 +11559,12 @@ "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" }, "pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, "requires": { "find-up": "^4.0.0" } @@ -11768,8 +11574,7 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" }, "indent-string": { "version": "2.1.0", @@ -11796,7 +11601,6 @@ "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" @@ -11805,8 +11609,7 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "ini": { "version": "1.3.5", @@ -11870,12 +11673,54 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + } + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==" + }, "strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", @@ -11931,8 +11776,7 @@ "ip-regex": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", - "dev": true + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=" }, "ipaddr.js": { "version": "1.9.1", @@ -11950,7 +11794,6 @@ "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" }, @@ -11959,7 +11802,6 @@ "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" } @@ -11974,8 +11816,7 @@ "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" }, "is-binary-path": { "version": "2.1.0", @@ -11990,8 +11831,7 @@ "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 + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, "is-callable": { "version": "1.1.5", @@ -12002,7 +11842,6 @@ "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" } @@ -12025,7 +11864,6 @@ "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" }, @@ -12034,7 +11872,6 @@ "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" } @@ -12050,7 +11887,6 @@ "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", @@ -12060,8 +11896,7 @@ "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 + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" } } }, @@ -12075,14 +11910,12 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.0.0.tgz", "integrity": "sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ==", - "dev": true, "optional": 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 + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" }, "is-extglob": { "version": "2.1.1", @@ -12099,8 +11932,7 @@ "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, "is-function": { "version": "1.0.1", @@ -12111,8 +11943,7 @@ "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 + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==" }, "is-glob": { "version": "4.0.1", @@ -12184,7 +12015,6 @@ "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" } @@ -12192,8 +12022,7 @@ "is-potential-custom-element-name": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz", - "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=", - "dev": true + "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=" }, "is-redirect": { "version": "1.0.0", @@ -12230,8 +12059,7 @@ "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, "is-string": { "version": "1.0.5", @@ -12259,8 +12087,7 @@ "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, "is-utf8": { "version": "0.2.1", @@ -12271,14 +12098,12 @@ "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 + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" }, "is-wsl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, "optional": true, "requires": { "is-docker": "^2.0.0" @@ -12287,38 +12112,32 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" }, "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 + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, "istanbul-lib-coverage": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", - "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", - "dev": true + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==" }, "istanbul-lib-instrument": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dev": true, "requires": { "@babel/core": "^7.7.5", "@istanbuljs/schema": "^0.1.2", @@ -12329,8 +12148,7 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" } } }, @@ -12338,7 +12156,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, "requires": { "istanbul-lib-coverage": "^3.0.0", "make-dir": "^3.0.0", @@ -12348,14 +12165,12 @@ "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, "requires": { "semver": "^6.0.0" } @@ -12363,14 +12178,12 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" }, "supports-color": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -12381,7 +12194,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", - "dev": true, "requires": { "debug": "^4.1.1", "istanbul-lib-coverage": "^3.0.0", @@ -12391,8 +12203,7 @@ "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 + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" } } }, @@ -12400,7 +12211,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", - "dev": true, "requires": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" @@ -12450,7 +12260,6 @@ "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.0.1.tgz", "integrity": "sha512-q8LP9Sint17HaE2LjxQXL+oYWW/WeeXMPE2+Op9X3mY8IEGFVc14xRxFjUuXUbcPAlDLhtWdIEt59GdQbn76Hw==", - "dev": true, "requires": { "@jest/types": "^26.0.1", "execa": "^4.0.0", @@ -12461,7 +12270,6 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, "requires": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -12472,7 +12280,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.2.tgz", "integrity": "sha512-QI2zLa6CjGWdiQsmSkZoGtDx2N+cQIGb3yNolGTdjSQzydzLgYYf8LRuagp7S7fPimjcrzUDSUFd/MgzELMi4Q==", - "dev": true, "requires": { "cross-spawn": "^7.0.0", "get-stream": "^5.0.0", @@ -12489,7 +12296,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", - "dev": true, "requires": { "pump": "^3.0.0" } @@ -12497,14 +12303,12 @@ "is-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" }, "npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, "requires": { "path-key": "^3.0.0" } @@ -12512,14 +12316,12 @@ "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "requires": { "shebang-regex": "^3.0.0" } @@ -12527,14 +12329,12 @@ "shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "requires": { "isexe": "^2.0.0" } @@ -12571,7 +12371,6 @@ "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.0.1.tgz", "integrity": "sha512-9mWKx2L1LFgOXlDsC4YSeavnblN6A4CPfXFiobq+YYLaBMymA/SczN7xYTSmLaEYHZOcB98UdoN4m5uNt6tztg==", - "dev": true, "requires": { "@babel/core": "^7.1.0", "@jest/test-sequencer": "^26.0.1", @@ -12596,8 +12395,7 @@ "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" } } }, @@ -12651,12 +12449,36 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "jest-cli": { + "version": "26.0.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.0.1.tgz", + "integrity": "sha512-pFLfSOBcbG9iOZWaMK4Een+tTxi/Wcm34geqZEqrst9cZDkTQ1LZ2CnBrTlHWuYAiTMFr0EQeK52ScyFU8wK+w==", + "requires": { + "@jest/core": "^26.0.1", + "@jest/test-result": "^26.0.1", + "@jest/types": "^26.0.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "import-local": "^3.0.2", + "is-ci": "^2.0.0", + "jest-config": "^26.0.1", + "jest-util": "^26.0.1", + "jest-validate": "^26.0.1", + "prompts": "^2.0.1", + "yargs": "^15.3.1" + } + }, "supports-color": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", @@ -12672,7 +12494,6 @@ "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.0.1.tgz", "integrity": "sha512-odTcHyl5X+U+QsczJmOjWw5tPvww+y9Yim5xzqxVl/R1j4z71+fHW4g8qu1ugMmKdFdxw+AtQgs5mupPnzcIBQ==", - "dev": true, "requires": { "chalk": "^4.0.0", "diff-sequences": "^26.0.0", @@ -12684,7 +12505,6 @@ "version": "26.0.0", "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz", "integrity": "sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==", - "dev": true, "requires": { "detect-newline": "^3.0.0" } @@ -12693,7 +12513,6 @@ "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.0.1.tgz", "integrity": "sha512-OTgJlwXCAR8NIWaXFL5DBbeS4QIYPuNASkzSwMCJO+ywo9BEa6TqkaSWsfR7VdbMLdgYJqSfQcIyjJCNwl5n4Q==", - "dev": true, "requires": { "@jest/types": "^26.0.1", "chalk": "^4.0.0", @@ -12706,7 +12525,6 @@ "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.0.1.tgz", "integrity": "sha512-u88NJa3aptz2Xix2pFhihRBAatwZHWwSiRLBDBQE1cdJvDjPvv7ZGA0NQBxWwDDn7D0g1uHqxM8aGgfA9Bx49g==", - "dev": true, "requires": { "@jest/environment": "^26.0.1", "@jest/fake-timers": "^26.0.1", @@ -12720,7 +12538,6 @@ "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.0.1.tgz", "integrity": "sha512-4FRBWcSn5yVo0KtNav7+5NH5Z/tEgDLp7VRQVS5tCouWORxj+nI+1tOLutM07Zb2Qi7ja+HEDoOUkjBSWZg/IQ==", - "dev": true, "requires": { "@jest/environment": "^26.0.1", "@jest/fake-timers": "^26.0.1", @@ -12776,12 +12593,77 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "execa": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.2.tgz", + "integrity": "sha512-QI2zLa6CjGWdiQsmSkZoGtDx2N+cQIGb3yNolGTdjSQzydzLgYYf8LRuagp7S7fPimjcrzUDSUFd/MgzELMi4Q==", + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "requires": { + "pump": "^3.0.0" + } + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "requires": { + "path-key": "^3.0.0" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, "supports-color": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", @@ -12790,20 +12672,36 @@ "requires": { "has-flag": "^4.0.0" } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } } } }, + "jest-fetch-mock": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/jest-fetch-mock/-/jest-fetch-mock-3.0.3.tgz", + "integrity": "sha512-Ux1nWprtLrdrH4XwE7O7InRY6psIi3GOsqNESJgMJ+M5cv4A8Lh7SN9d2V2kKRZ8ebAfcd1LNyZguAOb6JiDqw==", + "dev": true, + "requires": { + "cross-fetch": "^3.0.4", + "promise-polyfill": "^8.1.3" + } + }, "jest-get-type": { "version": "26.0.0", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.0.0.tgz", - "integrity": "sha512-zRc1OAPnnws1EVfykXOj19zo2EMw5Hi6HLbFCSjpuJiXtOWAYIjNsHVSbpQ8bDX7L5BGYGI8m+HmKdjHYFF0kg==", - "dev": true + "integrity": "sha512-zRc1OAPnnws1EVfykXOj19zo2EMw5Hi6HLbFCSjpuJiXtOWAYIjNsHVSbpQ8bDX7L5BGYGI8m+HmKdjHYFF0kg==" }, "jest-haste-map": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.0.1.tgz", "integrity": "sha512-J9kBl/EdjmDsvyv7CiyKY5+DsTvVOScenprz/fGqfLg/pm1gdjbwwQ98nW0t+OIt+f+5nAVaElvn/6wP5KO7KA==", - "dev": true, "requires": { "@jest/types": "^26.0.1", "@types/graceful-fs": "^4.1.2", @@ -12823,14 +12721,12 @@ "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "requires": { "isexe": "^2.0.0" } @@ -12841,7 +12737,6 @@ "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.0.1.tgz", "integrity": "sha512-ILaRyiWxiXOJ+RWTKupzQWwnPaeXPIoLS5uW41h18varJzd9/7I0QJGqg69fhTT1ev9JpSSo9QtalriUN0oqOg==", - "dev": true, "requires": { "@babel/traverse": "^7.1.0", "@jest/environment": "^26.0.1", @@ -12866,7 +12761,6 @@ "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.0.1.tgz", "integrity": "sha512-93FR8tJhaYIWrWsbmVN1pQ9ZNlbgRpfvrnw5LmgLRX0ckOJ8ut/I35CL7awi2ecq6Ca4lL59bEK9hr7nqoHWPA==", - "dev": true, "requires": { "jest-get-type": "^26.0.0", "pretty-format": "^26.0.1" @@ -12876,7 +12770,6 @@ "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.0.1.tgz", "integrity": "sha512-PUMlsLth0Azen8Q2WFTwnSkGh2JZ8FYuwijC8NR47vXKpsrKmA1wWvgcj1CquuVfcYiDEdj985u5Wmg7COEARw==", - "dev": true, "requires": { "chalk": "^4.0.0", "jest-diff": "^26.0.1", @@ -12888,7 +12781,6 @@ "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.0.1.tgz", "integrity": "sha512-CbK8uQREZ8umUfo8+zgIfEt+W7HAHjQCoRaNs4WxKGhAYBGwEyvxuK81FXa7VeB9pwDEXeeKOB2qcsNVCAvB7Q==", - "dev": true, "requires": { "@babel/code-frame": "^7.0.0", "@jest/types": "^26.0.1", @@ -12903,8 +12795,7 @@ "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" } } }, @@ -12912,7 +12803,6 @@ "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.0.1.tgz", "integrity": "sha512-MpYTBqycuPYSY6xKJognV7Ja46/TeRbAZept987Zp+tuJvMN0YBWyyhG9mXyYQaU3SBI0TUlSaO5L3p49agw7Q==", - "dev": true, "requires": { "@jest/types": "^26.0.1" } @@ -12920,8 +12810,7 @@ "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 + "integrity": "sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ==" }, "jest-puppeteer": { "version": "4.4.0", @@ -12931,19 +12820,24 @@ "requires": { "expect-puppeteer": "^4.4.0", "jest-environment-puppeteer": "^4.4.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" + } } }, "jest-regex-util": { "version": "26.0.0", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", - "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", - "dev": true + "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==" }, "jest-resolve": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.0.1.tgz", "integrity": "sha512-6jWxk0IKZkPIVTvq6s72RH735P8f9eCJW3IM5CX/SJFeKq1p2cZx0U49wf/SdMlhaB/anann5J2nCJj6HrbezQ==", - "dev": true, "requires": { "@jest/types": "^26.0.1", "chalk": "^4.0.0", @@ -12959,7 +12853,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, "requires": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -12968,14 +12861,12 @@ "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, "requires": { "p-locate": "^4.1.0" } @@ -12984,7 +12875,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, "requires": { "p-limit": "^2.2.0" } @@ -12993,7 +12883,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", - "dev": true, "requires": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -13004,14 +12893,12 @@ "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" }, "read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, "requires": { "@types/normalize-package-data": "^2.4.0", "normalize-package-data": "^2.5.0", @@ -13022,8 +12909,7 @@ "type-fest": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==" } } }, @@ -13031,7 +12917,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, "requires": { "find-up": "^4.1.0", "read-pkg": "^5.2.0", @@ -13042,7 +12927,6 @@ "version": "1.17.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "dev": true, "requires": { "path-parse": "^1.0.6" } @@ -13053,7 +12937,6 @@ "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.0.1.tgz", "integrity": "sha512-9d5/RS/ft0vB/qy7jct/qAhzJsr6fRQJyGAFigK3XD4hf9kIbEH5gks4t4Z7kyMRhowU6HWm/o8ILqhaHdSqLw==", - "dev": true, "requires": { "@jest/types": "^26.0.1", "jest-regex-util": "^26.0.0", @@ -13064,7 +12947,6 @@ "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.0.1.tgz", "integrity": "sha512-CApm0g81b49Znm4cZekYQK67zY7kkB4umOlI2Dx5CwKAzdgw75EN+ozBHRvxBzwo1ZLYZ07TFxkaPm+1t4d8jA==", - "dev": true, "requires": { "@jest/console": "^26.0.1", "@jest/environment": "^26.0.1", @@ -13090,8 +12972,7 @@ "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" } } }, @@ -13099,7 +12980,6 @@ "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.0.1.tgz", "integrity": "sha512-Ci2QhYFmANg5qaXWf78T2Pfo6GtmIBn2rRaLnklRyEucmPccmCKvS9JPljcmtVamsdMmkyNkVFb9pBTD6si9Lw==", - "dev": true, "requires": { "@jest/console": "^26.0.1", "@jest/environment": "^26.0.1", @@ -13132,14 +13012,12 @@ "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" }, "strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==" } } }, @@ -13147,7 +13025,6 @@ "version": "26.0.0", "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.0.0.tgz", "integrity": "sha512-sQGXLdEGWFAE4wIJ2ZaIDb+ikETlUirEOBsLXdoBbeLhTHkZUJwgk3+M8eyFizhM6le43PDCCKPA1hzkSDo4cQ==", - "dev": true, "requires": { "graceful-fs": "^4.2.4" }, @@ -13155,8 +13032,7 @@ "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" } } }, @@ -13164,7 +13040,6 @@ "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.0.1.tgz", "integrity": "sha512-jxd+cF7+LL+a80qh6TAnTLUZHyQoWwEHSUFJjkw35u3Gx+BZUNuXhYvDqHXr62UQPnWo2P6fvQlLjsU93UKyxA==", - "dev": true, "requires": { "@babel/types": "^7.0.0", "@jest/types": "^26.0.1", @@ -13186,14 +13061,12 @@ "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, "requires": { "semver": "^6.0.0" }, @@ -13201,16 +13074,14 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" } } }, "semver": { "version": "7.3.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", - "dev": true + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" } } }, @@ -13218,7 +13089,6 @@ "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.0.1.tgz", "integrity": "sha512-byQ3n7ad1BO/WyFkYvlWQHTsomB6GIewBh8tlGtusiylAlaxQ1UpS0XYH0ngOyhZuHVLN79Qvl6/pMiDMSSG1g==", - "dev": true, "requires": { "@jest/types": "^26.0.1", "chalk": "^4.0.0", @@ -13230,14 +13100,12 @@ "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, "requires": { "semver": "^6.0.0" } @@ -13245,8 +13113,7 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" } } }, @@ -13254,7 +13121,6 @@ "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.0.1.tgz", "integrity": "sha512-u0xRc+rbmov/VqXnX3DlkxD74rHI/CfS5xaV2VpeaVySjbb1JioNVOyly5b56q2l9ZKe7bVG5qWmjfctkQb0bA==", - "dev": true, "requires": { "@jest/types": "^26.0.1", "camelcase": "^6.0.0", @@ -13267,8 +13133,7 @@ "camelcase": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.0.0.tgz", - "integrity": "sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w==", - "dev": true + "integrity": "sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w==" } } }, @@ -13276,7 +13141,6 @@ "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.0.1.tgz", "integrity": "sha512-pdZPydsS8475f89kGswaNsN3rhP6lnC3/QDCppP7bg1L9JQz7oU9Mb/5xPETk1RHDCWeqmVC47M4K5RR7ejxFw==", - "dev": true, "requires": { "@jest/test-result": "^26.0.1", "@jest/types": "^26.0.1", @@ -13290,7 +13154,6 @@ "version": "26.0.0", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.0.0.tgz", "integrity": "sha512-pPaYa2+JnwmiZjK9x7p9BoZht+47ecFCDFA/CJxspHzeDvQcfVBLWzCiWyo+EGrSiQMWZtCFo9iSvMZnAAo8vw==", - "dev": true, "requires": { "merge-stream": "^2.0.0", "supports-color": "^7.0.0" @@ -13299,14 +13162,12 @@ "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "supports-color": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -13350,7 +13211,6 @@ "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" @@ -13359,14 +13219,12 @@ "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" }, "jsdom": { "version": "16.2.2", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.2.2.tgz", "integrity": "sha512-pDFQbcYtKBHxRaP55zGXCJWgFHkDAYbKcsXEK/3Icu9nKYZkutUXfLBwbD+09XDutkYSHcgfQLZ0qvpAAm9mvg==", - "dev": true, "requires": { "abab": "^2.0.3", "acorn": "^7.1.1", @@ -13399,16 +13257,14 @@ "parse5": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", - "dev": true + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==" } } }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" }, "json-loader": { "version": "0.5.7", @@ -13419,20 +13275,17 @@ "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 + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" }, "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 + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -13443,14 +13296,12 @@ "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 + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, "json5": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", - "dev": true, "requires": { "minimist": "^1.2.5" } @@ -13474,7 +13325,6 @@ "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", @@ -13495,14 +13345,12 @@ "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" }, "kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" }, "last-call-webpack-plugin": { "version": "3.0.0", @@ -13541,8 +13389,7 @@ "leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==" }, "levenary": { "version": "1.1.1", @@ -13566,8 +13413,7 @@ "lines-and-columns": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", - "dev": true + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=" }, "lint-staged": { "version": "10.2.9", @@ -13921,8 +13767,7 @@ "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", - "dev": true + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=" }, "lodash.template": { "version": "4.5.0", @@ -14067,7 +13912,6 @@ "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" } @@ -14084,8 +13928,7 @@ "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" }, "map-obj": { "version": "1.0.1", @@ -14097,7 +13940,6 @@ "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" } @@ -14281,8 +14123,7 @@ "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" }, "methods": { "version": "1.1.2", @@ -14294,7 +14135,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, "requires": { "braces": "^3.0.1", "picomatch": "^2.0.5" @@ -14327,14 +14167,12 @@ "mime-db": { "version": "1.43.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", - "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==", - "dev": true + "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==" }, "mime-types": { "version": "2.1.26", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", - "dev": true, "requires": { "mime-db": "1.43.0" } @@ -14342,8 +14180,7 @@ "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 + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" }, "mimic-response": { "version": "2.1.0", @@ -14388,7 +14225,6 @@ "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" } @@ -14396,8 +14232,7 @@ "minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, "minipass": { "version": "3.1.1", @@ -14483,7 +14318,6 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, "requires": { "for-in": "^1.0.2", "is-extendable": "^1.0.1" @@ -14493,7 +14327,6 @@ "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" } @@ -14561,8 +14394,7 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "mute-stream": { "version": "0.0.8", @@ -14580,7 +14412,6 @@ "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", @@ -14604,8 +14435,7 @@ "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 + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" }, "negotiator": { "version": "0.6.2", @@ -14622,8 +14452,7 @@ "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, "node-abi": { "version": "2.15.0", @@ -14643,8 +14472,7 @@ "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", - "dev": true + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=" }, "node-libs-browser": { "version": "2.2.1", @@ -14699,14 +14527,12 @@ "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 + "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=" }, "node-notifier": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-7.0.1.tgz", "integrity": "sha512-VkzhierE7DBmQEElhTGJIoiZa1oqRijOtgOlsXg32KrJRXsPy0NXFBqWGW/wTswnJlDCs5viRYaqWguqzsKcmg==", - "dev": true, "optional": true, "requires": { "growly": "^1.3.0", @@ -14721,21 +14547,18 @@ "version": "7.3.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", - "dev": true, "optional": true }, "uuid": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==", - "dev": true, "optional": true }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "optional": true, "requires": { "isexe": "^2.0.0" @@ -14759,7 +14582,6 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, "requires": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", @@ -14770,8 +14592,7 @@ "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 + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" }, "normalize-url": { "version": "1.9.1", @@ -14794,7 +14615,6 @@ "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" } @@ -14829,14 +14649,12 @@ "nwsapi": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", - "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", - "dev": true + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==" }, "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 + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" }, "object-assign": { "version": "4.1.1", @@ -14847,7 +14665,6 @@ "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", @@ -14858,7 +14675,6 @@ "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" } @@ -14867,7 +14683,6 @@ "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" } @@ -14923,7 +14738,6 @@ "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" } @@ -14977,7 +14791,6 @@ "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" } @@ -15013,7 +14826,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -15022,7 +14834,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", - "dev": true, "requires": { "mimic-fn": "^2.1.0" } @@ -15095,14 +14906,12 @@ "p-each-series": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz", - "integrity": "sha512-ZuRs1miPT4HrjFa+9fRfOFXxGJfORgelKV9f9nNOWw2gl6gVsRaVDOQP0+MI0G0wGKns1Yacsu0GjOFbTK0JFQ==", - "dev": true + "integrity": "sha512-ZuRs1miPT4HrjFa+9fRfOFXxGJfORgelKV9f9nNOWw2gl6gVsRaVDOQP0+MI0G0wGKns1Yacsu0GjOFbTK0JFQ==" }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" }, "p-is-promise": { "version": "2.1.0", @@ -15114,7 +14923,6 @@ "version": "2.2.2", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", - "dev": true, "requires": { "p-try": "^2.0.0" } @@ -15137,8 +14945,7 @@ "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 + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" }, "package-json": { "version": "4.0.1", @@ -15173,7 +14980,6 @@ "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" } @@ -15299,8 +15105,7 @@ "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" }, "path-browserify": { "version": "0.0.1", @@ -15323,8 +15128,7 @@ "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 + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-is-inside": { "version": "1.0.2", @@ -15335,14 +15139,12 @@ "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" }, "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 + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" }, "path-to-regexp": { "version": "0.1.7", @@ -15389,8 +15191,7 @@ "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "phin": { "version": "2.9.3", @@ -15401,8 +15202,7 @@ "picomatch": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", - "dev": true + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==" }, "pify": { "version": "4.0.1", @@ -15413,14 +15213,12 @@ "pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" }, "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" } @@ -15429,7 +15227,6 @@ "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" } @@ -15529,8 +15326,7 @@ "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 + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" }, "postcss": { "version": "7.0.27", @@ -16209,7 +16005,6 @@ "version": "26.0.1", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.0.1.tgz", "integrity": "sha512-SWxz6MbupT3ZSlL0Po4WF/KujhQaVehijR2blyRDCzk9e45EaYMVhMBn49fnRuHxtkSpXTes1GxNpVmH86Bxfw==", - "dev": true, "requires": { "@jest/types": "^26.0.1", "ansi-regex": "^5.0.0", @@ -16221,7 +16016,6 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, "requires": { "@types/color-name": "^1.1.1", "color-convert": "^2.0.1" @@ -16231,7 +16025,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "requires": { "color-name": "~1.1.4" } @@ -16239,8 +16032,7 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" } } }, @@ -16274,11 +16066,16 @@ "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", "dev": true }, + "promise-polyfill": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.1.3.tgz", + "integrity": "sha512-MG5r82wBzh7pSKDRa9y+vllNHz3e3d4CNj1PQE4BQYxLme0gKYYBm9YENq+UkEikyZ0XbiGWxYlVw3Rl9O/U8g==", + "dev": true + }, "prompts": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.3.2.tgz", "integrity": "sha512-Q06uKs2CkNYVID0VqwfAl9mipo99zkBv/n2JtWY89Yxa3ZabWSrs0e2KTudKVa3peLUvYXMefDqIleLPVUBZMA==", - "dev": true, "requires": { "kleur": "^3.0.3", "sisteransi": "^1.0.4" @@ -16325,8 +16122,7 @@ "psl": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz", - "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==", - "dev": true + "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==" }, "public-encrypt": { "version": "4.0.3", @@ -16354,7 +16150,6 @@ "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" @@ -16386,8 +16181,7 @@ "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 + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, "puppeteer": { "version": "3.3.0", @@ -16525,6 +16319,11 @@ "prop-types": "^15.6.2" } }, + "react-async": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/react-async/-/react-async-10.0.1.tgz", + "integrity": "sha512-ORUz5ca0B57QgBIzEZM5SuhJ6xFjkvEEs0gylLNlWf06vuVcLZsjIw3wx58jJkZG38p+0nUAxRgFW2b7mnVZzA==" + }, "react-dom": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz", @@ -16795,7 +16594,6 @@ "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" @@ -16891,8 +16689,7 @@ "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 + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" }, "renderkid": { "version": "2.0.3", @@ -16927,14 +16724,12 @@ "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 + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==" }, "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 + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" }, "repeating": { "version": "2.0.1", @@ -16955,7 +16750,6 @@ "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "dev": true, "requires": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -16982,14 +16776,12 @@ "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 + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, "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" @@ -17001,7 +16793,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz", "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==", - "dev": true, "requires": { "lodash": "^4.17.15" } @@ -17010,7 +16801,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.8.tgz", "integrity": "sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==", - "dev": true, "requires": { "request-promise-core": "1.1.3", "stealthy-require": "^1.1.1", @@ -17021,7 +16811,6 @@ "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" @@ -17032,14 +16821,12 @@ "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 + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" }, "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 + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" }, "resize-img": { "version": "1.1.2", @@ -17153,7 +16940,6 @@ "version": "1.15.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", - "dev": true, "requires": { "path-parse": "^1.0.6" } @@ -17162,7 +16948,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, "requires": { "resolve-from": "^5.0.0" } @@ -17180,14 +16965,12 @@ "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" }, "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 + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" }, "restore-cursor": { "version": "3.1.0", @@ -17202,8 +16985,7 @@ "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 + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" }, "rgb-regex": { "version": "1.0.1", @@ -17221,7 +17003,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, "requires": { "glob": "^7.1.3" } @@ -17239,8 +17020,7 @@ "rsvp": { "version": "4.8.5", "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", - "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", - "dev": true + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==" }, "run-async": { "version": "2.4.1", @@ -17280,14 +17060,12 @@ "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 + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "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" } @@ -17301,7 +17079,6 @@ "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", @@ -17318,7 +17095,6 @@ "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" @@ -17328,7 +17104,6 @@ "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", @@ -17346,7 +17121,6 @@ "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" } @@ -17357,7 +17131,6 @@ "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", @@ -17369,7 +17142,6 @@ "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" } @@ -17380,7 +17152,6 @@ "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" }, @@ -17389,7 +17160,6 @@ "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" } @@ -17400,7 +17170,6 @@ "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", @@ -17421,7 +17190,6 @@ "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" } @@ -17430,7 +17198,6 @@ "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" @@ -17448,7 +17215,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", - "dev": true, "requires": { "xmlchars": "^2.2.0" } @@ -17476,8 +17242,7 @@ "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" }, "semver-compare": { "version": "1.0.0", @@ -17600,14 +17365,12 @@ "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 + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, "set-value": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dev": true, "requires": { "extend-shallow": "^2.0.1", "is-extendable": "^0.1.1", @@ -17619,7 +17382,6 @@ "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" } @@ -17711,7 +17473,6 @@ "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" } @@ -17719,14 +17480,12 @@ "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 + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" }, "shellwords": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", - "dev": true, "optional": true }, "side-channel": { @@ -17742,8 +17501,7 @@ "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, "simple-concat": { "version": "1.0.0", @@ -17782,14 +17540,12 @@ "sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" }, "slice-ansi": { "version": "2.1.0", @@ -17814,7 +17570,6 @@ "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", @@ -17830,7 +17585,6 @@ "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" } @@ -17839,7 +17593,6 @@ "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" } @@ -17848,7 +17601,6 @@ "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" } @@ -17856,8 +17608,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } }, @@ -17865,7 +17616,6 @@ "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", @@ -17876,7 +17626,6 @@ "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" } @@ -17885,7 +17634,6 @@ "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" } @@ -17894,7 +17642,6 @@ "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" } @@ -17903,7 +17650,6 @@ "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", @@ -17916,7 +17662,6 @@ "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" }, @@ -17925,7 +17670,6 @@ "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" } @@ -17950,14 +17694,12 @@ "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" }, "source-map-resolve": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "dev": true, "requires": { "atob": "^2.1.2", "decode-uri-component": "^0.2.0", @@ -17970,7 +17712,6 @@ "version": "0.5.16", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", - "dev": true, "requires": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -17979,16 +17720,14 @@ "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 + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" } } }, "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 + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" }, "spawnd": { "version": "4.4.0", @@ -18006,7 +17745,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", - "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" @@ -18015,14 +17753,12 @@ "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 + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" }, "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" @@ -18031,14 +17767,12 @@ "spdx-license-ids": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", - "dev": true + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==" }, "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" } @@ -18046,14 +17780,12 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, "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", @@ -18085,7 +17817,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.2.tgz", "integrity": "sha512-0H7QK2ECz3fyZMzQ8rH0j2ykpfbnd20BFtfg/SqVC2+sCTtcw0aDTGB7dk+de4U4uUeuz6nOtJcrkFFLG1B0Rg==", - "dev": true, "requires": { "escape-string-regexp": "^2.0.0" }, @@ -18093,8 +17824,7 @@ "escape-string-regexp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==" } } }, @@ -18102,7 +17832,6 @@ "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" @@ -18112,7 +17841,6 @@ "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" } @@ -18128,8 +17856,7 @@ "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 + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" }, "stream-browserify": { "version": "2.0.2", @@ -18210,7 +17937,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.1.tgz", "integrity": "sha512-PKyXUd0LK0ePjSOnWn34V2uD6acUWev9uy0Ft05k0E8xRW+SKcA0F7eMr7h5xlzfn+4O3N+55rduYyet3Jk+jw==", - "dev": true, "requires": { "char-regex": "^1.0.2", "strip-ansi": "^6.0.0" @@ -18220,7 +17946,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, "requires": { "ansi-regex": "^5.0.0" } @@ -18231,7 +17956,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -18242,7 +17966,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, "requires": { "ansi-regex": "^5.0.0" } @@ -18327,14 +18050,12 @@ "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 + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" }, "strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" }, "strip-indent": { "version": "1.0.1", @@ -18454,7 +18175,6 @@ "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" } @@ -18463,7 +18183,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz", "integrity": "sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA==", - "dev": true, "requires": { "has-flag": "^4.0.0", "supports-color": "^7.0.0" @@ -18472,14 +18191,12 @@ "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "supports-color": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -18637,8 +18354,7 @@ "symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" }, "table": { "version": "5.4.6", @@ -18819,7 +18535,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "dev": true, "requires": { "ansi-escapes": "^4.2.1", "supports-hyperlinks": "^2.0.0" @@ -19073,7 +18788,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, "requires": { "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", @@ -19089,8 +18803,7 @@ "throat": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", - "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", - "dev": true + "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==" }, "through": { "version": "2.3.8", @@ -19141,6 +18854,11 @@ "integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g=", "dev": true }, + "tinyqueue": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz", + "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==" + }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -19153,8 +18871,7 @@ "tmpl": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", - "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", - "dev": true + "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=" }, "to-arraybuffer": { "version": "1.0.1", @@ -19165,8 +18882,7 @@ "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 + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" }, "to-ico": { "version": "1.1.5", @@ -19193,7 +18909,6 @@ "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" }, @@ -19202,7 +18917,6 @@ "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" } @@ -19213,7 +18927,6 @@ "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", @@ -19225,7 +18938,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "requires": { "is-number": "^7.0.0" } @@ -19240,7 +18952,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", - "dev": true, "requires": { "ip-regex": "^2.1.0", "psl": "^1.1.28", @@ -19251,7 +18962,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.0.2.tgz", "integrity": "sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==", - "dev": true, "requires": { "punycode": "^2.1.1" } @@ -19315,7 +19025,6 @@ "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" } @@ -19323,8 +19032,7 @@ "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, "type-check": { "version": "0.4.0", @@ -19338,14 +19046,12 @@ "type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" }, "type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" }, "type-is": { "version": "1.6.18", @@ -19372,7 +19078,6 @@ "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, "requires": { "is-typedarray": "^1.0.0" } @@ -19419,7 +19124,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dev": true, "requires": { "arr-union": "^3.1.0", "get-value": "^2.0.6", @@ -19488,7 +19192,6 @@ "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" @@ -19498,7 +19201,6 @@ "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", @@ -19509,7 +19211,6 @@ "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" } @@ -19519,8 +19220,7 @@ "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 + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" } } }, @@ -19587,7 +19287,6 @@ "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" } @@ -19601,8 +19300,7 @@ "urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" }, "url": { "version": "0.11.0", @@ -19704,8 +19402,7 @@ "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 + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" }, "utif": { "version": "2.0.1", @@ -19764,8 +19461,7 @@ "uuid": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" }, "v8-compile-cache": { "version": "2.1.1", @@ -19777,7 +19473,6 @@ "version": "4.1.4", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-4.1.4.tgz", "integrity": "sha512-Rw6vJHj1mbdK8edjR7+zuJrpDtKIgNdAvTSAcpYfgMIw+u2dPDntD3dgN4XQFLU2/fvFQdzj+EeSGfd/jnY5fQ==", - "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.1", "convert-source-map": "^1.6.0", @@ -19787,8 +19482,7 @@ "source-map": { "version": "0.7.3", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" } } }, @@ -19796,7 +19490,6 @@ "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" @@ -19818,7 +19511,6 @@ "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", @@ -19849,7 +19541,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "dev": true, "requires": { "browser-process-hrtime": "^1.0.0" } @@ -19858,7 +19549,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", - "dev": true, "requires": { "xml-name-validator": "^3.0.0" } @@ -19910,7 +19600,6 @@ "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" } @@ -20059,6 +19748,7 @@ "dev": true, "optional": true, "requires": { + "bindings": "^1.5.0", "nan": "^2.12.1" } }, @@ -20144,8 +19834,7 @@ "webidl-conversions": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", - "dev": true + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==" }, "webpack": { "version": "4.43.0", @@ -20604,7 +20293,6 @@ "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" } @@ -20612,14 +20300,12 @@ "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 + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" }, "whatwg-url": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.1.0.tgz", "integrity": "sha512-vEIkwNi9Hqt4TV9RdnaBPNt+E2Sgmo3gePebCRgZ1R7g6d23+53zCTnuB0amKI4AXq6VM8jj2DUAa0S1vjJxkw==", - "dev": true, "requires": { "lodash.sortby": "^4.7.0", "tr46": "^2.0.2", @@ -20629,8 +20315,7 @@ "webidl-conversions": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==" } } }, @@ -20638,7 +20323,6 @@ "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" } @@ -20646,8 +20330,7 @@ "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 + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" }, "which-pm-runs": { "version": "1.0.0", @@ -20742,8 +20425,7 @@ "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" }, "worker-farm": { "version": "1.7.0", @@ -20758,7 +20440,6 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, "requires": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -20769,7 +20450,6 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, "requires": { "@types/color-name": "^1.1.1", "color-convert": "^2.0.1" @@ -20779,7 +20459,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "requires": { "color-name": "~1.1.4" } @@ -20787,14 +20466,12 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, "requires": { "ansi-regex": "^5.0.0" } @@ -20804,8 +20481,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write": { "version": "1.0.3", @@ -20820,7 +20496,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, "requires": { "imurmurhash": "^0.1.4", "is-typedarray": "^1.0.0", @@ -20831,8 +20506,7 @@ "ws": { "version": "7.3.0", "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.0.tgz", - "integrity": "sha512-iFtXzngZVXPGgpTlP1rBqsUK82p9tKqsWRPg5L56egiljujJT3vGAYnHANvFxBieXrTFavhzhxW52jnaWV+w2w==", - "dev": true + "integrity": "sha512-iFtXzngZVXPGgpTlP1rBqsUK82p9tKqsWRPg5L56egiljujJT3vGAYnHANvFxBieXrTFavhzhxW52jnaWV+w2w==" }, "xdg-basedir": { "version": "3.0.0", @@ -20855,8 +20529,7 @@ "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 + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" }, "xml-parse-from-string": { "version": "1.0.1", @@ -20883,8 +20556,7 @@ "xmlchars": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" }, "xmlhttprequest": { "version": "1.8.0", @@ -20909,8 +20581,7 @@ "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 + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" }, "yallist": { "version": "3.1.1", @@ -20921,14 +20592,12 @@ "yaml": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", - "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==", - "dev": true + "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==" }, "yargs": { "version": "15.3.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", - "dev": true, "requires": { "cliui": "^6.0.0", "decamelize": "^1.2.0", @@ -20947,7 +20616,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, "requires": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -20957,7 +20625,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, "requires": { "p-locate": "^4.1.0" } @@ -20966,7 +20633,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, "requires": { "p-limit": "^2.2.0" } @@ -20974,8 +20640,7 @@ "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" } } }, @@ -20983,7 +20648,6 @@ "version": "18.1.3", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, "requires": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" diff --git a/client/package.json b/client/package.json index f907f55a..db6e8454 100644 --- a/client/package.json +++ b/client/package.json @@ -44,6 +44,7 @@ "lodash": "^4.17.15", "memoize-one": "^5.1.1", "react": "^16.13.1", + "react-async": "^10.0.1", "react-dom": "^16.13.1", "react-flip-toolkit": "7.0.6", "react-helmet": "^5.2.1", @@ -51,7 +52,8 @@ "react-redux": "^7.2.0", "redux": "^4.0.5", "redux-thunk": "^2.3.0", - "regl": "^1.6.1" + "regl": "^1.6.1", + "tinyqueue": "^2.0.3" }, "devDependencies": { "@babel/core": "^7.10.2", @@ -102,6 +104,7 @@ "jest": "^26.0.1", "jest-circus": "^26.0.1", "jest-environment-puppeteer": "^4.4.0", + "jest-fetch-mock": "^3.0.3", "jest-puppeteer": "^4.4.0", "json-loader": "^0.5.7", "lint-staged": "^10.2.9", diff --git a/client/src/actions/annotation.js b/client/src/actions/annotation.js new file mode 100644 index 00000000..b07f2470 --- /dev/null +++ b/client/src/actions/annotation.js @@ -0,0 +1,374 @@ +/* +Action creators for user annotation +*/ +import _ from "lodash"; +import * as globals from "../globals"; +import { MatrixFBS, AnnotationsHelpers } from "../util/stateManager"; + +const { isUserAnnotation } = AnnotationsHelpers; + +export const annotationCreateCategoryAction = ( + newCategoryName, + categoryToDuplicate +) => async (dispatch, getState) => { + /* + Add a new user-created category to the obs annotations. + + Arguments: + newCategoryName - string name for the category. + categoryToDuplicate - obs category to use for initial values, or null. + */ + const { + annoMatrix: prevAnnoMatrix, + obsCrossfilter: prevObsCrossfilter, + } = getState(); + if (!prevAnnoMatrix || !prevObsCrossfilter) return; + const { schema } = prevAnnoMatrix; + + /* name must be a string, non-zero length */ + if (typeof newCategoryName !== "string" || newCategoryName.length === 0) + throw new Error("user annotations require string name"); + + /* ensure the name isn't already in use! */ + if (schema.annotations.obsByName[newCategoryName]) + throw new Error("name collision on annotation category create"); + + let initialValue; + let categories; + if (categoryToDuplicate) { + /* if we are duplicating a category, retrieve it */ + const catDupSchema = schema.annotations.obsByName[categoryToDuplicate]; + const catDupType = catDupSchema?.type; + if (catDupType !== "string" && catDupType !== "categorical") + throw new Error("categoryToDuplicate does not exist or has invalid type"); + + const catToDupDf = await prevAnnoMatrix + .base() + .fetch("obs", categoryToDuplicate); + const col = catToDupDf.col(categoryToDuplicate); + initialValue = col.asArray(); + ({ categories } = col.summarize()); + } else { + /* else assign to the standard default value */ + initialValue = globals.unassignedCategoryLabel; + categories = [globals.unassignedCategoryLabel]; + } + + const obsCrossfilter = prevObsCrossfilter.addObsColumn( + { + name: newCategoryName, + type: "categorical", + categories, + writable: true, + }, + Array, + initialValue + ); + + dispatch({ + type: "annotation: create category", + data: newCategoryName, + categoryToDuplicate, + annoMatrix: obsCrossfilter.annoMatrix, + obsCrossfilter, + }); +}; + +export const annotationRenameCategoryAction = ( + oldCategoryName, + newCategoryName +) => (dispatch, getState) => { + /* + Rename a user-created annotation category + */ + const { + annoMatrix: prevAnnoMatrix, + obsCrossfilter: prevObsCrossfilter, + } = getState(); + if (!prevAnnoMatrix || !prevObsCrossfilter) return; + if (!isUserAnnotation(prevAnnoMatrix, oldCategoryName)) + throw new Error("not a user annotation"); + + /* name must be a string, non-zero length */ + if (typeof newCategoryName !== "string" || newCategoryName.length === 0) + throw new Error("user annotations require string name"); + + if (oldCategoryName === newCategoryName) return; + + const obsCrossfilter = prevObsCrossfilter.renameObsColumn( + oldCategoryName, + newCategoryName + ); + + dispatch({ + type: "annotation: category edited", + annoMatrix: obsCrossfilter.annoMatrix, + obsCrossfilter, + metadataField: oldCategoryName, + newCategoryText: newCategoryName, + data: newCategoryName, + }); +}; + +export const annotationDeleteCategoryAction = (categoryName) => ( + dispatch, + getState +) => { + /* + Delete a user-created category + */ + const { + annoMatrix: prevAnnoMatrix, + obsCrossfilter: prevObsCrossfilter, + } = getState(); + if (!prevAnnoMatrix || !prevObsCrossfilter) return; + if (!isUserAnnotation(prevAnnoMatrix, categoryName)) + throw new Error("not a user annotation"); + + const obsCrossfilter = prevObsCrossfilter.dropObsColumn(categoryName); + dispatch({ + type: "annotation: delete category", + annoMatrix: obsCrossfilter.annoMatrix, + obsCrossfilter, + metadataField: categoryName, + }); +}; + +export const annotationCreateLabelInCategory = ( + categoryName, + labelName, + assignSelected +) => async (dispatch, getState) => { + /* + Add a new label to a user-defined category. If assignSelected is true, assign + the label to all currently selected cells. + */ + const { + annoMatrix: prevAnnoMatrix, + obsCrossfilter: prevObsCrossfilter, + } = getState(); + if (!prevAnnoMatrix || !prevObsCrossfilter) return; + if (!isUserAnnotation(prevAnnoMatrix, categoryName)) + throw new Error("not a user annotation"); + + let obsCrossfilter = prevObsCrossfilter.addObsAnnoCategory( + categoryName, + labelName + ); + if (assignSelected) { + obsCrossfilter = await obsCrossfilter.setObsColumnValues( + categoryName, + prevObsCrossfilter.allSelectedLabels(), + labelName + ); + } + + dispatch({ + type: "annotation: add new label to category", + annoMatrix: obsCrossfilter.annoMatrix, + obsCrossfilter, + metadataField: categoryName, + newLabelText: labelName, + assignSelectedCells: assignSelected, + }); +}; + +export const annotationDeleteLabelFromCategory = ( + categoryName, + labelName +) => async (dispatch, getState) => { + /* + delete a label from a user-defined category + */ + const { + annoMatrix: prevAnnoMatrix, + obsCrossfilter: prevObsCrossfilter, + } = getState(); + if (!prevAnnoMatrix || !prevObsCrossfilter) return; + if (!isUserAnnotation(prevAnnoMatrix, categoryName)) + throw new Error("not a user annotation"); + + const obsCrossfilter = await prevObsCrossfilter.removeObsAnnoCategory( + categoryName, + labelName, + globals.unassignedCategoryLabel + ); + + dispatch({ + type: "annotation: delete label", + metadataField: categoryName, + label: labelName, + annoMatrix: obsCrossfilter.annoMatrix, + obsCrossfilter, + }); +}; + +export const annotationRenameLabelInCategory = ( + categoryName, + oldLabelName, + newLabelName +) => async (dispatch, getState) => { + /* + label name change + */ + const { + annoMatrix: prevAnnoMatrix, + obsCrossfilter: prevObsCrossfilter, + } = getState(); + if (!prevAnnoMatrix || !prevObsCrossfilter) return; + if (!isUserAnnotation(prevAnnoMatrix, categoryName)) + throw new Error("not a user annotation"); + + let obsCrossfilter = await prevObsCrossfilter.resetObsColumnValues( + categoryName, + oldLabelName, + newLabelName + ); + obsCrossfilter = await obsCrossfilter.removeObsAnnoCategory( + categoryName, + oldLabelName, + globals.unassignedCategoryLabel + ); + + dispatch({ + type: "annotation: label edited", + editedLabel: newLabelName, + metadataField: categoryName, + label: oldLabelName, + annoMatrix: obsCrossfilter.annoMatrix, + obsCrossfilter, + }); +}; + +export const annotationLabelCurrentSelection = ( + categoryName, + labelName +) => async (dispatch, getState) => { + /* + set the label on all currently selected + */ + const { + annoMatrix: prevAnnoMatrix, + obsCrossfilter: prevObsCrossfilter, + } = getState(); + if (!prevAnnoMatrix || !prevObsCrossfilter) return; + if (!isUserAnnotation(prevAnnoMatrix, categoryName)) + throw new Error("not a user annotation"); + + const obsCrossfilter = await prevObsCrossfilter.setObsColumnValues( + categoryName, + prevObsCrossfilter.allSelectedLabels(), + labelName + ); + + dispatch({ + type: "annotation: label current cell selection", + metadataField: categoryName, + label: labelName, + obsCrossfilter, + annoMatrix: obsCrossfilter.annoMatrix, + }); +}; + +function writableAnnotations(annoMatrix) { + return annoMatrix.schema.annotations.obs.columns + .filter((s) => s.writable) + .map((s) => s.name); +} + +export const needToSaveObsAnnotations = (annoMatrix, lastSavedAnnoMatrix) => { + /* + Return true if there are LIKELY user-defined annotation modifications between the two + annoMatrices. Technically not an action creator, but intimately intertwined + with the save process. + + Two conditions will trigger a need to save: + * the collection of user-defined columns have changed + * the contents of the user-defined columns have change + */ + + annoMatrix = annoMatrix.base(); + + // if the annoMatrix hasn't changed, we are guaranteed no changes to the matrix schema or contents. + if (annoMatrix === lastSavedAnnoMatrix) return false; + + // if the schema has changed, we need to save + const currentWritable = writableAnnotations(annoMatrix); + if (_.difference(currentWritable, writableAnnotations(lastSavedAnnoMatrix))) { + return true; + } + + // no schema changes; check for change in contents + return currentWritable.some( + (col) => annoMatrix.col(col) !== lastSavedAnnoMatrix.col(col) + ); +}; + +export const saveObsAnnotationsAction = () => async (dispatch, getState) => { + /* + Save the user-created obs annotations IF any have changed. + */ + const state = getState(); + const { annotations, autosave } = state; + const { dataCollectionNameIsReadOnly, dataCollectionName } = annotations; + const { lastSavedAnnoMatrix, saveInProgress } = autosave; + + const annoMatrix = state.annoMatrix.base(); + + if (saveInProgress || annoMatrix === lastSavedAnnoMatrix) return; + if (!needToSaveObsAnnotations(annoMatrix, lastSavedAnnoMatrix)) { + dispatch({ + type: "writable obs annotations - save complete", + lastSavedAnnoMatrix: annoMatrix, + }); + return; + } + + /* + Else, we really do need to save + */ + + dispatch({ + type: "writable obs annotations - save started", + }); + + const df = await annoMatrix.fetch("obs", writableAnnotations(annoMatrix)); + const matrix = MatrixFBS.encodeMatrixFBS(df); + try { + const queryString = + !dataCollectionNameIsReadOnly && !!dataCollectionName + ? `?annotation-collection-name=${encodeURIComponent( + dataCollectionName + )}` + : ""; + const res = await fetch( + `${globals.API.prefix}${globals.API.version}annotations/obs${queryString}`, + { + method: "PUT", + body: matrix, + headers: new Headers({ + "Content-Type": "application/octet-stream", + }), + credentials: "include", + } + ); + if (res.ok) { + dispatch({ + type: "writable obs annotations - save complete", + lastSavedAnnoMatrix: annoMatrix, + }); + } else { + dispatch({ + type: "writable obs annotations - save error", + message: `HTTP error ${res.status} - ${res.statusText}`, + res, + }); + } + } catch (error) { + dispatch({ + type: "writable obs annotations - save error", + message: error.toString(), + error, + }); + } +}; diff --git a/client/src/actions/index.js b/client/src/actions/index.js index ada975d3..40ba928c 100644 --- a/client/src/actions/index.js +++ b/client/src/actions/index.js @@ -1,93 +1,21 @@ import * as globals from "../globals"; -import { Universe, MatrixFBS } from "../util/stateManager"; -import * as Dataframe from "../util/dataframe"; +import { + AnnoMatrixLoader, + AnnoMatrixObsCrossfilter, + clip, + isubsetMask, +} from "../annoMatrix"; import { catchErrorsWrap, doJsonRequest, - doBinaryRequest, dispatchNetworkErrorMessageToUser, } from "../util/actionHelpers"; -import PromiseLimit from "../util/promiseLimit"; -import { requestReembed, reembedResetWorldToUniverse } from "./reembed"; +import { + requestReembed /* , reembedResetWorldToUniverse -- disabled temporarily, TODO issue #1606 */, +} from "./reembed"; import { loadUserColorConfig } from "../util/stateManager/colorHelpers"; - -/* -return promise to fetch the OBS annotations we need to load. Omit anything -we don't need. -*/ -async function obsAnnotationFetchAndLoad(dispatch, schema) { - const obsAnnotations = schema?.schema?.annotations?.obs ?? {}; - const index = obsAnnotations.index ?? false; - const columns = (obsAnnotations.columns ?? []).filter( - (col) => col.name !== index - ); - - const plimit = new PromiseLimit(5); - return Promise.all( - columns.map((col) => - plimit.add(() => - fetchBinary( - `annotations/obs?annotation-name=${encodeURIComponent(col.name)}` - ) - .then((buffer) => MatrixFBS.matrixFBSToDataframe(buffer)) - .then((df) => - dispatch({ - type: "universe: column load success", - dim: "obsAnnotations", - dataframe: df, - }) - ) - ) - ) - ); -} - -/* -return promise fetching VAR annotations we need to load. Only index is currently used. -*/ -async function varAnnotationFetchAndLoad(dispatch, schema) { - const varAnnotations = schema?.schema?.annotations?.var ?? {}; - const index = varAnnotations.index ?? false; - const names = index ? [index] : []; - return Promise.all( - names.map((name) => - fetchBinary(`annotations/var?annotation-name=${encodeURIComponent(name)}`) - .then((buffer) => MatrixFBS.matrixFBSToDataframe(buffer)) - .then((df) => - dispatch({ - type: "universe: column load success", - dim: "varAnnotations", - dataframe: df, - }) - ) - ) - ); -} - -/* -return promise fetching layout we need -*/ -function layoutFetchAndLoad(dispatch, schema) { - const embeddings = schema?.schema?.layout?.obs ?? []; - const embNames = embeddings.map((e) => e.name); - - const plimit = new PromiseLimit(5); - return Promise.all( - embNames.map((e) => - plimit.add(() => - fetchBinary( - `layout/obs?layout-name=${encodeURIComponent(e)}` - ).then((buffer) => MatrixFBS.matrixFBSToDataframe(buffer)) - ) - ) - ).then((dfs) => - dispatch({ - type: "universe: column load success", - dim: "obsLayout", - dataframe: Dataframe.Dataframe.empty().withColsFromAll(dfs), - }) - ); -} +import * as selnActions from "./selection"; +import * as annoActions from "./annotation"; /* return promise fetching user-configured colors @@ -101,178 +29,62 @@ async function userColorsFetchAndLoad(dispatch) { ); } +async function schemaFetch() { + return fetchJson("schema"); +} + +async function configFetch(dispatch) { + return fetchJson("config").then((response) => { + const config = { ...globals.configDefaults, ...response.config }; + dispatch({ + type: "configuration load complete", + config, + }); + return config; + }); +} + /* -Bootstrap application with the initial data loading. - * /config - application configuration - * /schema - schema of dataframe - * /annotations - all metadata annotation - * /layout - all default layout +Application bootstrap */ const doInitialDataLoad = () => catchErrorsWrap(async (dispatch) => { dispatch({ type: "initial data load start" }); try { - /* - Step 1 - config & schema, all JSON - */ - const requestJson = ["config", "schema"].map(fetchJson); - const [responseConfig, schema] = await Promise.all(requestJson); - /* set config defaults */ - const config = { ...globals.configDefaults, ...responseConfig.config }; - const universe = Universe.createUniverseFromResponse(config, schema); - dispatch({ - type: "universe exists, but loading is still in progress", - universe, - }); - dispatch({ - type: "configuration load complete", - config, - }); - - /* - Step 2 - load the minimum stuff required to display. - */ - await Promise.all([ + const [, schema] = await Promise.all([ + configFetch(dispatch), + schemaFetch(dispatch), userColorsFetchAndLoad(dispatch), - layoutFetchAndLoad(dispatch, schema), - varAnnotationFetchAndLoad(dispatch, schema), ]); - /* - Step 3 - load everything else - */ - await obsAnnotationFetchAndLoad(dispatch, schema); - + const baseDataUrl = `${globals.API.prefix}${globals.API.version}`; + const annoMatrix = new AnnoMatrixLoader(baseDataUrl, schema.schema); + const obsCrossfilter = new AnnoMatrixObsCrossfilter(annoMatrix); dispatch({ - type: "initial data load complete (universe exists)", - universe, + type: "annoMatrix: init complete", + annoMatrix, + obsCrossfilter, }); + dispatch({ type: "initial data load complete" }); } 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 setWorldToSelection = () => (dispatch, getState) => { - const { universe, world, crossfilter } = getState(); - dispatch({ - type: "set World to current selection", - universe, - world, - crossfilter, - }); -}; - -/* double URI encode - needed for query-param filters */ -function dubEncURIComponent(s) { - return encodeURIComponent(encodeURIComponent(s)); -} - -/* -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 query = geneNames - .map( - (g) => - `var:${dubEncURIComponent(varIndexName)}=${dubEncURIComponent(g)}` - ) - .join("&"); - // TODO: why convert to an Object and not a Dataframe? - return fetchBinary(`data/var?${query}`).then((buffer) => - Universe.convertDataFBStoObject(universe, buffer) - ); - }; - - /* preload data already in cache */ - let expressionData = genes.reduce((acc, g) => { - const data = universe.varData.col(g); - if (data) { - acc[g] = data.asArray(); - } - return acc; - }, {}); // --> { gene: data } - - /* make a list of genes for which we do not have data */ - const genesToFetch = genes.filter((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, - }); - } + return { + type: "color by expression", + gene, }; } -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 requestUserDefinedGene = (gene) => ({ + type: "request user defined gene success", + data: { + genes: [gene], + }, +}); const dispatchDiffExpErrors = (dispatch, response) => { switch (response.status) { @@ -308,9 +120,8 @@ const requestDifferentialExpression = (set1, set2, num_genes = 10) => async ( 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; + const { annoMatrix } = getState(); + const varIndexName = annoMatrix.schema.annotations.var.index; // Legal values are null, Array or TypedArray. Null is initial state. if (!set1) set1 = []; @@ -345,22 +156,12 @@ const requestDifferentialExpression = (set1, set2, num_genes = 10) => async ( return dispatchDiffExpErrors(dispatch, res); } - const data = await res.json(); - // result is [ [varIdx, ...], ... ] - const topNGenes = data.map((r) => - universe.varAnnotations.at(r[0], varIndexName) - ); - - /* - Kick off secondary action to fetch all of the expression data for the - topN expressed genes. - */ - const plimit = new PromiseLimit(5); - await Promise.all( - topNGenes.map((gene) => - plimit.add(() => _doRequestExpressionData(dispatch, getState, [gene])) - ) - ); + const response = await res.json(); + const varIndex = await annoMatrix.fetch("var", varIndexName); + const data = response.map((v) => [ + varIndex.at(v[0], varIndexName), + ...v.slice(1), + ]); /* then send the success case action through */ return dispatch({ @@ -375,66 +176,73 @@ const requestDifferentialExpression = (set1, set2, num_genes = 10) => async ( } }; -const resetWorldToUniverse = () => (dispatch, getState) => { - const { universe } = getState(); - reembedResetWorldToUniverse(dispatch, getState); +const clipAction = (min, max) => (dispatch, getState) => { + /* + apply a clip to the current annoMatrix. By convention, the clip + view is ALWAYS the top view. + */ + const { annoMatrix: prevAnnoMatrix } = getState(); + const annoMatrix = prevAnnoMatrix.isClipped + ? clip(prevAnnoMatrix.viewOf, min, max) + : clip(prevAnnoMatrix, min, max); + const obsCrossfilter = new AnnoMatrixObsCrossfilter(annoMatrix); dispatch({ - type: "reset World to eq Universe", - universe, + type: "set clip quantiles", + clipQuantiles: { min, max }, + annoMatrix, + obsCrossfilter, }); }; -const saveObsAnnotations = () => async (dispatch, getState) => { - const { universe, annotations } = getState(); - const { obsAnnotations, schema } = universe; - const { dataCollectionNameIsReadOnly, dataCollectionName } = annotations; +const subsetAction = () => (dispatch, getState) => { + /* + Subset the annoMatrix to the current crossfilter selection + */ + const { + annoMatrix: prevAnnoMatrix, + obsCrossfilter: prevObsCrossfilter, + } = getState(); + const annoMatrix = isubsetMask( + prevAnnoMatrix, + prevObsCrossfilter.allSelectedMask() + ); + const obsCrossfilter = new AnnoMatrixObsCrossfilter(annoMatrix); dispatch({ - type: "writable obs annotations - save started", + type: "subset to selection", + annoMatrix, + obsCrossfilter, }); +}; - const writableAnnotations = schema.annotations.obs.columns - .filter((s) => s.writable) - .map((s) => s.name); - const df = obsAnnotations.subset(null, writableAnnotations); - const matrix = MatrixFBS.encodeMatrixFBS(df); - try { - const queryString = - !dataCollectionNameIsReadOnly && !!dataCollectionName - ? `?annotation-collection-name=${encodeURIComponent( - dataCollectionName - )}` - : ""; - const res = await fetch( - `${globals.API.prefix}${globals.API.version}annotations/obs${queryString}`, - { - method: "PUT", - body: matrix, - headers: new Headers({ - "Content-Type": "application/octet-stream", - }), - credentials: "include", - } - ); - if (res.ok) { - dispatch({ - type: "writable obs annotations - save complete", - obsAnnotations, - }); - } else { - dispatch({ - type: "writable obs annotations - save error", - message: `HTTP error ${res.status} - ${res.statusText}`, - res, - }); - } - } catch (error) { - dispatch({ - type: "writable obs annotations - save error", - message: error.toString(), - error, - }); +const resetSubsetAction = () => (dispatch, getState) => { + /* + Reset the annoMatrix to all data. Because we may have multiple views + stacked, we pop them all. By convention, any clip transformation will + be the top of the stack, and must be preserved. + */ + + const { annoMatrix: prevAnnoMatrix } = getState(); + + const clipRange = prevAnnoMatrix.isClipped ? prevAnnoMatrix.clipRange : null; + + /* pop all views */ + let annoMatrix = prevAnnoMatrix; + while (annoMatrix.isView) { + annoMatrix = annoMatrix.viewOf; } + + /* re-apply the clip, if any */ + if (clipRange !== null) { + annoMatrix = clip(annoMatrix, ...clipRange); + } + + const obsCrossfilter = new AnnoMatrixObsCrossfilter(annoMatrix); + dispatch({ + type: "reset subset", + annoMatrix, + obsCrossfilter, + }); }; function fetchJson(pathAndQuery) { @@ -443,19 +251,37 @@ function fetchJson(pathAndQuery) { ); } -function fetchBinary(pathAndQuery) { - return doBinaryRequest( - `${globals.API.prefix}${globals.API.version}${pathAndQuery}` - ); -} - export default { doInitialDataLoad, requestDifferentialExpression, requestSingleGeneExpressionCountsForColoringPOST, requestUserDefinedGene, requestReembed, - resetWorldToUniverse, - saveObsAnnotations, - setWorldToSelection, + selectContinuousMetadataAction: selnActions.selectContinuousMetadataAction, + selectCategoricalMetadataAction: selnActions.selectCategoricalMetadataAction, + selectCategoricalAllMetadataAction: + selnActions.selectCategoricalAllMetadataAction, + graphBrushStartAction: selnActions.graphBrushStartAction, + graphBrushChangeAction: selnActions.graphBrushChangeAction, + graphBrushDeselectAction: selnActions.graphBrushDeselectAction, + graphBrushCancelAction: selnActions.graphBrushCancelAction, + graphBrushEndAction: selnActions.graphBrushEndAction, + graphLassoStartAction: selnActions.graphLassoStartAction, + graphLassoEndAction: selnActions.graphLassoEndAction, + graphLassoCancelAction: selnActions.graphLassoCancelAction, + graphLassoDeselectAction: selnActions.graphLassoDeselectAction, + clipAction, + subsetAction, + resetSubsetAction, + annotationCreateCategoryAction: annoActions.annotationCreateCategoryAction, + annotationRenameCategoryAction: annoActions.annotationRenameCategoryAction, + annotationDeleteCategoryAction: annoActions.annotationDeleteCategoryAction, + annotationCreateLabelInCategory: annoActions.annotationCreateLabelInCategory, + annotationDeleteLabelFromCategory: + annoActions.annotationDeleteLabelFromCategory, + annotationRenameLabelInCategory: annoActions.annotationRenameLabelInCategory, + annotationLabelCurrentSelection: annoActions.annotationLabelCurrentSelection, + saveObsAnnotationsAction: annoActions.saveObsAnnotationsAction, + needToSaveObsAnnotations: annoActions.needToSaveObsAnnotations, + layoutChoiceAction: selnActions.layoutChoiceAction, }; diff --git a/client/src/actions/reembed.js b/client/src/actions/reembed.js index 7d73337e..43b38e1f 100644 --- a/client/src/actions/reembed.js +++ b/client/src/actions/reembed.js @@ -104,6 +104,7 @@ export function requestReembed() { }; } +/* disabled until reimplementation occurs export function reembedResetWorldToUniverse(dispatch, getState) { const { reembedController } = getState(); if (reembedController.pendingFetch) reembedController.pendingFetch.abort(); @@ -111,3 +112,4 @@ export function reembedResetWorldToUniverse(dispatch, getState) { type: "reembed: clear all reembeddings", }); } +*/ diff --git a/client/src/actions/selection.js b/client/src/actions/selection.js new file mode 100644 index 00000000..1ab8e42b --- /dev/null +++ b/client/src/actions/selection.js @@ -0,0 +1,211 @@ +/* +Action creators for selection +*/ +export const selectContinuousMetadataAction = ( + type, + query, + range, + oldProps = {} +) => async (dispatch, getState) => { + const { obsCrossfilter: prevObsCrossfilter } = getState(); + + const selection = range + ? { + mode: "range", + lo: range[0], + hi: range[1], + inclusive: true, // [lo, hi] incluisve selection + } + : { mode: "all" }; + + const obsCrossfilter = await prevObsCrossfilter.select(...query, selection); + + dispatch({ + type, + obsCrossfilter, + range, + ...oldProps, + }); +}; + +export const selectCategoricalMetadataAction = ( + type, // action type + metadataField, // annotation category name + labels, + label, // the label being selected/deselected + isSelected, // bool + oldProps = {} +) => async (dispatch, getState) => { + const { + obsCrossfilter: prevObsCrossfilter, + categoricalSelection, + } = getState(); + + const labelSelectionState = new Map(categoricalSelection[metadataField]); + labels.forEach( + (l) => labelSelectionState.has(l) || labelSelectionState.set(l, true) + ); + labelSelectionState.set(label, isSelected); + + const values = Array.from(labelSelectionState.keys()).filter((k) => + labelSelectionState.get(k) + ); + const selection = { + mode: "exact", + values, + }; + const obsCrossfilter = await prevObsCrossfilter.select( + "obs", + metadataField, + selection + ); + + dispatch({ + type, + obsCrossfilter, + metadataField, + labelSelectionState, + ...oldProps, + }); +}; + +export const selectCategoricalAllMetadataAction = ( + type, // action type + metadataField, // annotation category name + labels, + isSelected, // bool, select all or none + oldProps = {} +) => async (dispatch, getState) => { + const { + obsCrossfilter: prevObsCrossfilter, + categoricalSelection, + } = getState(); + + const labelSelectionState = new Map(categoricalSelection[metadataField]); + labels.forEach((label) => labelSelectionState.set(label, isSelected)); + + const selection = { mode: isSelected ? "all" : "none" }; + const obsCrossfilter = await prevObsCrossfilter.select( + "obs", + metadataField, + selection + ); + + dispatch({ + type, + obsCrossfilter, + metadataField, + labelSelectionState, + ...oldProps, + }); +}; + +/** + ** Graph selection-related actions + **/ + +export const graphBrushStartAction = () => + /* no change to crossfilter until a change fires */ + ({ type: "graph brush start" }); + +const _graphBrushWithinRectAction = (type, embName, brushCoords) => async ( + dispatch, + getState +) => { + const { obsCrossfilter: prevObsCrossfilter } = getState(); + + const selection = { mode: "within-rect", ...brushCoords }; + const obsCrossfilter = await prevObsCrossfilter.select( + "emb", + embName, + selection + ); + + dispatch({ + type, + obsCrossfilter, + brushCoords, + }); +}; + +const _graphAllAction = (type, embName) => async (dispatch, getState) => { + const { obsCrossfilter: prevObsCrossfilter } = getState(); + + const obsCrossfilter = await prevObsCrossfilter.select("emb", embName, { + mode: "all", + }); + + dispatch({ + type, + obsCrossfilter, + }); +}; + +export const graphBrushChangeAction = (embName, brushCoords) => + _graphBrushWithinRectAction("graph brush change", embName, brushCoords); + +export const graphBrushEndAction = (embName, brushCoords) => + _graphBrushWithinRectAction("graph brush end", embName, brushCoords); + +export const graphBrushCancelAction = (embName) => + _graphAllAction("graph brush cancel", embName); +export const graphBrushDeselectAction = (embName) => + _graphAllAction("graph brush deselect", embName); + +export const graphLassoStartAction = () => + /* no change to crossfilter until a change fires */ + ({ type: "graph lasso start" }); + +export const graphLassoCancelAction = (embName) => + _graphAllAction("graph lasso cancel", embName); + +export const graphLassoDeselectAction = (embName) => + _graphAllAction("graph lasso cancel", embName); + +export const graphLassoEndAction = (embName, polygon) => async ( + dispatch, + getState +) => { + const { obsCrossfilter: prevObsCrossfilter } = getState(); + + const selection = { + mode: "within-polygon", + polygon, + }; + const obsCrossfilter = await prevObsCrossfilter.select( + "emb", + embName, + selection + ); + + dispatch({ + type: "graph lasso end", + obsCrossfilter, + polygon, + }); +}; + +export const layoutChoiceAction = (newLayoutChoice) => async ( + dispatch, + getState +) => { + /* + On layout choice, make sure we have selected all on the previous layout, AND the new + layout. + */ + const { obsCrossfilter: prevObsCrossfilter, layoutChoice } = getState(); + + let obsCrossfilter = await prevObsCrossfilter.select( + "emb", + layoutChoice.current, + { mode: "all" } + ); + obsCrossfilter = await obsCrossfilter.select("emb", newLayoutChoice, { + mode: "all", + }); + dispatch({ + type: "set layout choice", + layoutChoice: newLayoutChoice, + obsCrossfilter, + }); +}; diff --git a/client/src/annoMatrix/annoMatrix.js b/client/src/annoMatrix/annoMatrix.js new file mode 100644 index 00000000..0cb802a8 --- /dev/null +++ b/client/src/annoMatrix/annoMatrix.js @@ -0,0 +1,634 @@ +import { Dataframe, IdentityInt32Index } from "../util/dataframe"; +import { + _getColumnDimensionNames, + _getColumnSchema, + _schemaColumns, + _getWritableColumns, +} from "./schema"; +import { indexEntireSchema } from "../util/stateManager/schemaHelpers"; +import { _whereCacheGet, _whereCacheMerge } from "./whereCache"; +import _shallowClone from "./clone"; + +export default class AnnoMatrix { + /* + Abstract base class for all AnnoMatrix objects. This class provides a proxy + to the annotated matrix data authoritatively served by the server/back-end. + + AnnoMatrix instances are immutable, meaning that their schema and dimensionality + will not change, and simple object equality can be used to detect structural + changes. The actual data is cached, and not guaranteed to be present -- any + request to access data must be resolved by a fetch() call, which is async, and + may involve a server round-trip. + + Guarantees made by the immutabilty, ie, any of these can be detected by + simple annoMatrix compare: + * schema is the same, including all fields and columns + * dimensionality is the same (nObs, nVar) + * data mapping/transformation, such as clipping, are the same + + AnnoMatrixes also "stack" like filters, allowing for the construction of + views which transform the data in some manner. + + The bootstrap class is AnnoMatrixLoader, which is the caching server proxy, and + is bootstrapped with a API URL: + new AnnoMatirx(url, schema) -> annoMatrix + + There are various "views", such as AnnoMatrixRowSubsetView, which provide + the same interface but with a transformed view of the server data. Utilities in + viewCreators.js can be used to create these views: + clip(annoMatrix, min, max) -> annoMatrix + subset(annoMatrix, rowLabels) -> annoMatrix + etc. + */ + static fields() { + /* + return the fields present in the AnnoMatrix instance. + */ + return ["obs", "var", "emb", "X"]; + } + + constructor(schema, nObs, nVar, rowIndex = null) { + /* + Private constructor - this is an abstract base class. Do not use. + */ + + /* + Public instance fields: + * schema - the matrix schema. IMPORTANT: always the entire schema, for the + base (unfiltered, unclipped, unsubset) annotated matrix, as the server + presents it. + * nObs, nVar - size of each dimension. These will accurately reflect the + size of the current annoMatrix view. For example, if you subset the view, + the nObs will be smaller. + * rowIndex - a rowIndex shared by all data on this view (ie, the list of cells). + The row index labels are as defined by the base dataset from the server. + * isView - true if this is a view, false if not. + * viewOf - pointer to parent annomatrix if a view, undefined/null if not a view. + */ + this.schema = indexEntireSchema(schema); + this.nObs = nObs; + this.nVar = nVar; + this.rowIndex = rowIndex || new IdentityInt32Index(nObs); + this.isView = false; + this.viewOf = undefined; + + /* + Private instance variables. + + These are caches - lazily loaded. The only guarantee is that if they + are loaded, they will conform to the schema & dimensionality constraints. + + Do NOT use directly - instead, use the fetch() and preload() API. + */ + this._cache = { + obs: Dataframe.empty(this.rowIndex), + var: Dataframe.empty(this.rowIndex), + emb: Dataframe.empty(this.rowIndex), + X: Dataframe.empty(this.rowIndex), + }; + this._pendingLoad = { + obs: {}, + var: {}, + emb: {}, + X: {}, + }; + this._whereCache = {}; + this._gcInfo = new Map(); + } + + /** + ** Schema helper/accessors + **/ + getMatrixColumns(field) { + /* + Return array of column names in the field. ONLY supported on the + obs, var and emb fields. X currently unimplemented and will throw. + + For exmaple: + + annoMatrix.getMatrixColumns("obs") -> ["louvain", "n_genes"] + */ + return _schemaColumns(this.schema, field); + } + + // eslint-disable-next-line class-methods-use-this -- need to be able to call this on instances + getMatrixFields() { + /* + Return array of fields in this annoMatrix. Currently hard-wired to + return: ["X", "obs", "var", "emb"]. + + These are the fields from data may be requested. + */ + return AnnoMatrix.fields(); + } + + getColumnSchema(field, col) { + /* + Return the schema for the field & column ,eg, + + anonMatrix.getColumnSchema("obs", "n_genes") -> { type: "int32", name: "n_genes" } + + This is identical to the information in the annoMatrix.schema + instance variable. + */ + return _getColumnSchema(this.schema, field, col); + } + + getColumnDimensions(field, col) { + /* + Return the dimensions on this field / column. For most fields, which are 1D, + this just return the column name. Multi-dimensional columns, such as embeddings, + will return >1 name. + + Examples: + + getColumnDimensions("obs", "louvain") -> ["louvain"] + getColumnDimensions("emb", "umap") -> ["umap_0", "umap_1"] + + */ + return _getColumnDimensionNames(this.schema, field, col); + } + + /** + ** General utility methods + **/ + base() { + /* + return the base of view, or `this` if not a view. + */ + let annoMatrix = this; + while (annoMatrix.isView) annoMatrix = annoMatrix.viewOf; + return annoMatrix; + } + + /** + ** Load / read interfaces + **/ + fetch(field, q) { + /* + Return the given query on a single matrix field as a single dataframe. + Currently supports ONLY full column query. + + Returns a Promise for the query result, which will resolve to a dataframe. + + Field must be one of the matrix fields: 'obs', 'var', 'X', 'emb'. Value + represents the underlying object upon which the query is occuring. + + Query is one of: + * a string, representing a single column name from the field, eg, + "n_genes" + * an object, containing an "value" query (see below). + * an array, containing one or more of the above. + + Columns may have more than one dimension, and all will be fetched + and returned together. This is most commonly seen in an embedding, + which usually has two dimensions. + + A value query allows for fetching based upon the value in another + field/column, similar to a join. Currently only supported on the var + dimension, allowing query of X columns by var value (eg, gene name) + + The query filter is a single value filter: + { "field name": [ + {name: "column name", values: [ list of values ]} + ]} + One and only one value filter is allowed in a value query. + + Examples: + + 1. Fetch the "n_genes" column the "obs": + + const df = await fetch("obs", "n_genes") + console.log("Largest number of genes is: ", df.summarize().max); + + 2. Fetch two separate columns from obs. Returns a single dataframe containing + the columns: + + const df = await fetch("obs", ["n_genes", "louvain"]) + console.log("Cell 0 has category: ", df.at(0, "louvain")); + + 3. Fetch an entire X (expression counts) column that has a var annotation + value "TYMP" in the var index. + + fetch("X", { + where: {field: "var", column: this.schema.annotations.var.index, value: "TYMP"} + }) + + In AnnData & Pandas DataFrame API, this is equivalent to: + adata.X[:, adata.var.index.get_loc("SUMO3")] + + The value query is a recodification and subset of the server REST API + value filter JSON. Range queries and multiple filters are not currently + supported. + + */ + return this._fetch(field, q); + } + + prefetch(field, q) { + /* + Start a data fetch & cache fill. Identical to fetch() except it does + not return a value. + + Primary use is to being a cache load as early as is possible, reducing + overall component rendering latency. + */ + this._fetch(field, q); + return undefined; + } + + /** + ** Save / mutate interfaces - manipulation of "writable" OBS annotations. + ** + ** These are all present to support client-side creation of OBS annotations, aka + ** "user annotations". + ** + ** They implement common manipulations to the AnnoMatrix, maintaining the + ** norma guarantees around correctness of public API, eg, + ** - schema will be correct, including the "writable" attribute + ** - fetch() will return the latest data, even from views + ** - immutability guranteeds + ** + ** As most of these interfaces mutate the annoMatrix, they return a new + ** annoMatrix + ** + ** The actual implementation is in the sub-classes, which MUST override these. + **/ + + // eslint-disable-next-line class-methods-use-this, no-unused-vars -- make sure subclass implements + addObsAnnoCategory(col, category) { + /* + Add a new category value (aka "label") to a writable obs column, and return the new AnnoMatrix. + Typical use is to add a new user-created label to a user-created obs categorical + annotation. + + Will throw column does not exist or is not writable. + + Example: + + addObsAnnoCategory("my cell type", "left toenail") -> AnnoMatrix + + */ + _subclassResponsibility(); + } + + // eslint-disable-next-line class-methods-use-this, no-unused-vars -- make sure subclass implements + async removeObsAnnoCategory(col, category, unassignedCategory) { + /* + Remove a category value from an obs column, reassign any obs having that value + to the 'unassignedCategory' value, and return a promise for a new AnnoMatrix. + Typical use is to remove a user-created label from a user-created obs categorical + annotation. + + Will throw column does not exist or is not writable. + + An `unassignedCategory` value must be provided, for assignment to any obs/cells + that had the now-delete category label as their value. + + Example: + await removeObsAnnoCategory("my-tissue-type", "right earlobe", "unassigned") -> AnnoMatrix + + NOTE: method is async as it may need to fetch data to provide the reassignment. + */ + _subclassResponsibility(); + } + + // eslint-disable-next-line class-methods-use-this, no-unused-vars -- make sure subclass implements + dropObsColumn(col) { + /* + Drop an entire writable column, eg a user-created obs annotation. Typical use + is to provide the "Delete Category" implementation. Returns the new AnnoMatrix. + Will throw if not a writable annotation. + + Will throw column does not exist or is not writable. + + Example: + + dropObsColumn("old annotations") -> AnnoMatrix + */ + _subclassResponsibility(); + } + + // eslint-disable-next-line class-methods-use-this, no-unused-vars -- make sure subclass implements + addObsColumn(colSchema, Ctor, value) { + /* + Add a new writable OBS annotation column, with the caller-specified schema, initial value + type and value. + + Value may be any one of: + * an array of values + * a primitive type, including null or undefined. + If an array, length must be the same as 'this.nObs', and constructor must equal 'Ctor'. + If a primitive, 'Ctor' will be used to create the initial value, which will be filled + with 'value'. + + Throws if the name specified in 'colSchema' duplicates an existing obs column. + + Returns a new AnnoMatrix. + + Examples: + + addObsColumn( + { name: "foo", type: "categorical", categories: "unassigned" }, + Array, + "unassigned" + ) -> AnnoMatrix + + */ + _subclassResponsibility(); + } + + // eslint-disable-next-line class-methods-use-this, no-unused-vars -- make sure subclass implements + renameObsColumn(oldCol, newCol) { + /* + Rename the obs column 'oldCol' to have name 'newCol' and returns new AnnoMatrix. + + Will throw column does not exist or is not writable, or if 'newCol' is not unique. + + Example: + + renameObsColumn('cell type', 'old cell type') -> AnnoMatrix. + + */ + _subclassResponsibility(); + } + + // eslint-disable-next-line class-methods-use-this, no-unused-vars -- make sure subclass implements + async setObsColumnValues(col, obsLabels, value) { + /* + Set all obs with label in array 'obsLabels' to have 'value'. Typical use would be + to set a group of cells to have a label on a user-created categorical anntoation + (eg set all selected cells to have a label). + + NOTE: async method, as it may need to fetch. + + Will throw column does not exist or is not writable. + + Example: + await setObsColmnValues("flavor", [383, 400], "tasty") -> AnnoMtarix + + */ + _subclassResponsibility(); + } + + // eslint-disable-next-line class-methods-use-this, no-unused-vars -- make sure subclass implements + async resetObsColumnValues(col, oldValue, newValue) { + /* + Set by value - all elements in the column with value 'oldValue' are set to 'newValue'. + Async method - returns a promise for a new AnnoMatrix. + + Typical use would be to set all labels of one value to another. + + Will throw column does not exist or is not writable. + + Example: + await resetObsColumnValues("my notes", "good", "not-good") -> AnnoMatrix + + */ + _subclassResponsibility(); + } + + /** + ** Private interfaces below. + **/ + _resolveCachedQueries(field, queries) { + return queries + .map((query) => + _whereCacheGet(this._whereCache, this.schema, field, query).filter( + (cacheKey) => + cacheKey !== undefined && this._cache[field].hasCol(cacheKey) + ) + ) + .flat(); + } + + async _fetch(field, q) { + if (!AnnoMatrix.fields().includes(field)) return undefined; + const queries = Array.isArray(q) ? q : [q]; + + /* find cached columns we need, and GC the rest */ + const cachedColumns = this._resolveCachedQueries(field, queries); + this._gcFetchCleanup(field, cachedColumns); + + /* find any query not already cached */ + const uncachedQueries = queries.filter((query) => + _whereCacheGet(this._whereCache, this.schema, field, query).some( + (cacheKey) => + cacheKey === undefined || !this._cache[field].hasCol(cacheKey) + ) + ); + + /* load uncached queries */ + if (uncachedQueries.length > 0) { + await Promise.all( + uncachedQueries.map((query) => + this._getPendingLoad(field, query, async (_field, _query) => { + /* fetch, then index. _doLoad is subclass interface */ + const [whereCacheUpdate, df] = await this._doLoad(_field, _query); + this._cache[_field] = this._cache[_field].withColsFrom(df); + this._whereCache = _whereCacheMerge( + this._whereCache, + whereCacheUpdate + ); + }) + ) + ); + } + + /* everything we need is in the cache, so just cherry-pick requested columns */ + const requestedCacheKeys = this._resolveCachedQueries(field, queries); + const response = this._cache[field].subset(null, requestedCacheKeys); + this._gcUpdateStats(field, response); + return response; + } + + async _getPendingLoad(field, query, fetchFn) { + /* + Given a query on a field, ensure that we only have a single outstanding + fetch at any given time. If multiple requests occur while a fetch is + outstanding, just wait for the original. + + This is implemented by returning a promise that will await the singular + fetch promise. + */ + const key = _queryCacheKey(field, query); + if (!this._pendingLoad[field][key]) { + this._pendingLoad[field][key] = fetchFn(field, query); + try { + await this._pendingLoad[field][key]; + } finally { + delete this._pendingLoad[field][key]; + } + } + return this._pendingLoad[field][key]; + } + + // eslint-disable-next-line class-methods-use-this -- make sure subclass implements + async _doLoad() { + _subclassResponsibility(); + } + + /** + ** Garbage collection of annomatrix cache to manage memory use. + **/ + + /* + These callbacks implement a GC policy for the cache. Background: + + * For the Loader (base) annomatrix, re-filling the cache is expensive as + it requires an HTTP fetch. + * user-defined / writable columns must not be GC'ed as they may be + still pending a save/commit. + * For views, cost is less and (roughly) proportional with nObs + * obs, var and emb do not grow without bounds, and are needed constantly + for rendering. + a) There is no upside to GC'ing these in the base (loader) + b) The undo/redo cache can hold a large number in views, which is worht GC'ing + * X is often much larger than memory, and the UI allows add/del from + this. Most of the GC potential is here in both the base and views. + + Current policy: + * if in active use ("hot") do not GC obs, var or emb. + * never, ever GC writable obs columns + * For base/loader set a numeric limit on maximum X column count + * For views, apply a fixed limit to the number of columns cached in any field. + Limit will be lower if not hot. + + To be effective, the GC callback needs to be invoked from the undo/redo code, + as much of the cache is pinned by that data structure. + */ + _gcField(field, isHot, pinnedColumns) { + const maxColumns = isHot ? 256 : 10; // maybe to aggessive? + + const cache = this._cache[field]; + if (cache.colIndex.size() < maxColumns) return; // trivial rejection + + const candidates = cache.colIndex + .labels() + .filter((col) => !pinnedColumns.includes(col)); + + const excessCount = candidates.length + pinnedColumns.length - maxColumns; + if (excessCount > 0) { + const { _gcInfo } = this; + candidates.sort((a, b) => { + let atime = _gcInfo.get(_columnCacheKey(field, a)); + if (atime === undefined) atime = 0; + + let btime = _gcInfo.get(_columnCacheKey(field, b)); + if (btime === undefined) btime = 0; + + return atime - btime; + }); + + const toDrop = candidates.slice(0, excessCount); + // helpful debugging - please leave in place. + // console.log( + // `GC: dropping from ${field} hot:${isHot}, columns [${toDrop.join( + // ", " + // )}]` + // ); + this._cache[field] = toDrop.reduce( + (df, col) => df.dropCol(col), + this._cache[field] + ); + toDrop.forEach((col) => _gcInfo.delete(_columnCacheKey(field, col))); + } + } + + _gcFetchCleanup(field, pinnedColumns) { + /* + Called during data load/fetch. By definition, this is 'hot', so we + only want to gc X. + */ + if (field === "X") { + this._gcField( + field, + true, + pinnedColumns.concat(_getWritableColumns(this.schema, field)) + ); + } + } + + _gc(hints) { + /* + Called from middleware, or elsewhere. isHot is true if we are in the active store, + or false if we are in some other context (eg, history state). + */ + const { isHot } = hints; + const candidateFields = isHot ? ["X"] : ["X", "emb", "var", "obs"]; + candidateFields.forEach((field) => + this._gcField(field, isHot, _getWritableColumns(this.schema, field)) + ); + } + + _gcUpdateStats(field, dataframe) { + /* + called each time a query is performed, allowing the gc to update any bookkeeping + information. Currently, this is just a simple last-fetched timestamp, stored + in a Map. + + Map objects preserve order of insertion. This is leveraged as a cheap way to + do LRU, by removing and re-inserting keys. IMPORTANT: the cleanup code assumes + the map insertion order is least-recently-used first. + */ + const cols = dataframe.colIndex.labels(); + const { _gcInfo } = this; + const now = Date.now(); + cols.forEach((c) => { + // gcInfo.delete(c); + _gcInfo.set(_columnCacheKey(field, c), now); + }); + } + + /** + Cloning sublcass protocol - we rely in cloning to preserve immutable + symantics while not causing races or other side effects in internal + cache management. + + Subclasses must override _cloneDeeper() if they have state which requires + something other than a shallow copy. Overrides MUST call super()._cloneDeepr(), + and return its result (after any required modification). _cloneDeeper() + will be called on the OLD object, with the NEW object as an argument. + + Do not override _clone(); + **/ + _cloneDeeper(clone) { + clone._cache = _shallowClone(this._cache); + clone._gcInfo = new Map(); + clone._pendingLoad = { + obs: {}, + var: {}, + emb: {}, + X: {}, + }; + return clone; + } + + _clone() { + const clone = _shallowClone(this); + this._cloneDeeper(clone); + Object.seal(clone); + return clone; + } +} + +/* +private utility functions below +*/ + +function _queryCacheKey(field, query) { + if (typeof query === "object") { + const { field: queryField, column: queryColumn, value: queryValue } = query; + return `${field}/${queryField}/${queryColumn}/${queryValue}`; + } + return `${field}/${query}`; +} + +function _columnCacheKey(field, column) { + return `${field}/${column}`; +} + +function _subclassResponsibility() { + /* protect against bugs in subclass */ + throw new Error("subclass failed to implement required method"); +} diff --git a/client/src/annoMatrix/clone.js b/client/src/annoMatrix/clone.js new file mode 100644 index 00000000..97cad4b5 --- /dev/null +++ b/client/src/annoMatrix/clone.js @@ -0,0 +1,6 @@ +/* +Shallow clone an object, correctly handling prototype +*/ +export default function _shallowClone(orig) { + return Object.assign(Object.create(Object.getPrototypeOf(orig)), orig); +} diff --git a/client/src/annoMatrix/crossfilter.js b/client/src/annoMatrix/crossfilter.js new file mode 100644 index 00000000..b3972978 --- /dev/null +++ b/client/src/annoMatrix/crossfilter.js @@ -0,0 +1,263 @@ +/* +Row crossfilter proxy for an AnnoMatrix. This wraps Crossfilter, +providing a number of services, and ensuring that the crossfilter and +AnnoMatrix stay in sync: + - on-demand index creation as data is loaded + - transparently mapping between queries and crossfilter index names. + - for mutation of the matrix by user annotations, maintain synchronization + between Crossfilter and AnnoMatrix. +*/ +import Crossfilter from "../util/typedCrossfilter"; +import { _getColumnSchema } from "./schema"; + +function _dimensionNameFromDf(field, df) { + const colNames = df.colIndex.labels(); + return _dimensionName(field, colNames); +} + +function _dimensionName(field, colNames) { + if (!Array.isArray(colNames)) return `${field}/${colNames}`; + return `${field}/${colNames.join(":")}`; +} + +export default class AnnoMatrixObsCrossfilter { + constructor(annoMatrix, _obsCrossfilter = null) { + this.annoMatrix = annoMatrix; + this.obsCrossfilter = + _obsCrossfilter || new Crossfilter(annoMatrix._cache.obs); + this.obsCrossfilter = this.obsCrossfilter.setData(annoMatrix._cache.obs); + } + + size() { + return this.obsCrossfilter.size(); + } + + /** + Managing the associated annoMatrix. These wrappers are necessary to + make coordinated changes to BOTH the crossfilter and annoMatrix, and + ensure that all state stays synchronized. + + See API documentation in annoMatrix.js. + **/ + addObsColumn(colSchema, Ctor, value) { + const annoMatrix = this.annoMatrix.addObsColumn(colSchema, Ctor, value); + const obsCrossfilter = this.obsCrossfilter.setData(annoMatrix._cache.obs); + return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter); + } + + dropObsColumn(col) { + const annoMatrix = this.annoMatrix.dropObsColumn(col); + let { obsCrossfilter } = this; + const dimName = _dimensionName("obs", col); + if (obsCrossfilter.hasDimension(dimName)) { + obsCrossfilter = obsCrossfilter.delDimension(dimName); + } + return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter); + } + + renameObsColumn(oldCol, newCol) { + const annoMatrix = this.annoMatrix.renameObsColumn(oldCol, newCol); + const oldDimName = _dimensionName("obs", oldCol); + const newDimName = _dimensionName("obs", newCol); + let { obsCrossfilter } = this; + if (obsCrossfilter.hasDimension(oldDimName)) { + obsCrossfilter = obsCrossfilter.renameDimension(oldDimName, newDimName); + } + return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter); + } + + addObsAnnoCategory(col, category) { + const annoMatrix = this.annoMatrix.addObsAnnoCategory(col, category); + const dimName = _dimensionName("obs", col); + let { obsCrossfilter } = this; + if (obsCrossfilter.hasDimension(dimName)) { + obsCrossfilter = obsCrossfilter.delDimension(dimName); + } + return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter); + } + + async removeObsAnnoCategory(col, category, unassignedCategory) { + const annoMatrix = await this.annoMatrix.removeObsAnnoCategory( + col, + category, + unassignedCategory + ); + const dimName = _dimensionName("obs", col); + let { obsCrossfilter } = this; + if (obsCrossfilter.hasDimension(dimName)) { + obsCrossfilter = obsCrossfilter.delDimension(dimName); + } + return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter); + } + + async setObsColumnValues(col, rowLabels, value) { + const annoMatrix = await this.annoMatrix.setObsColumnValues( + col, + rowLabels, + value + ); + const dimName = _dimensionName("obs", col); + let { obsCrossfilter } = this; + if (obsCrossfilter.hasDimension(dimName)) { + obsCrossfilter = obsCrossfilter.delDimension(dimName); + } + return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter); + } + + async resetObsColumnValues(col, oldValue, newValue) { + const annoMatrix = await this.annoMatrix.resetObsColumnValues( + col, + oldValue, + newValue + ); + const dimName = _dimensionName("obs", col); + let { obsCrossfilter } = this; + if (obsCrossfilter.hasDimension(dimName)) { + obsCrossfilter = obsCrossfilter.delDimension(dimName); + } + return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter); + } + + /** + Selection state - API is identical to ImmutableTypedCrossfilter, as these + are just wrappers to lazy create indices. + **/ + + async select(field, query, spec) { + const { annoMatrix } = this; + let { obsCrossfilter } = this; + + if (!annoMatrix?._cache?.[field]) { + throw new Error("Unknown field name"); + } + if (field === "var") { + throw new Error("unable to obsSelect upon the var dimension"); + } + + // grab the data, so we can grab the index. + const df = await annoMatrix.fetch(field, query); + + const dimName = _dimensionNameFromDf(field, df); + if (!obsCrossfilter.hasDimension(dimName)) { + // lazy index generation - add dimension when first used + obsCrossfilter = this._addObsCrossfilterDimension( + annoMatrix, + obsCrossfilter, + field, + df + ); + } + + // select + obsCrossfilter = obsCrossfilter.select(dimName, spec); + return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter); + } + + selectAll() { + /* + Select all on any dimension in this field. + */ + const { annoMatrix } = this; + const currentDims = this.obsCrossfilter.dimensionNames(); + const obsCrossfilter = currentDims.reduce((xfltr, dim) => { + return xfltr.select(dim, { mode: "all" }); + }, this.obsCrossfilter); + return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter); + } + + countSelected() { + /* if no data yet indexed in the crossfilter, just say everything is selected */ + if (this.obsCrossfilter.size() === 0) return this.annoMatrix.nObs; + return this.obsCrossfilter.countSelected(); + } + + allSelectedMask() { + /* if no data yet indexed in the crossfilter, just say everything is selected */ + if ( + this.obsCrossfilter.size() === 0 || + this.obsCrossfilter.dimensionNames().length === 0 + ) { + /* fake the mask */ + return new Uint8Array(this.annoMatrix.nObs).fill(1); + } + return this.obsCrossfilter.allSelectedMask(); + } + + allSelectedLabels() { + /* if no data yet indexed in the crossfilter, just say everything is selected */ + if ( + this.obsCrossfilter.size() === 0 || + this.obsCrossfilter.dimensionNames().length === 0 + ) { + return this.annoMatrix.rowIndex.labels(); + } + + const mask = this.obsCrossfilter.allSelectedMask(); + const index = this.annoMatrix.rowIndex.isubsetMask(mask); + return index.labels(); + } + + fillByIsSelected(array, selectedValue, deselectedValue) { + /* if no data yet indexed in the crossfilter, just say everything is selected */ + if ( + this.obsCrossfilter.size() === 0 || + this.obsCrossfilter.dimensionNames().length === 0 + ) { + return array.fill(selectedValue); + } + return this.obsCrossfilter.fillByIsSelected( + array, + selectedValue, + deselectedValue + ); + } + + /** + ** Private below + **/ + + _addObsCrossfilterDimension(annoMatrix, obsCrossfilter, field, df) { + if (field === "var") return obsCrossfilter; + const dimName = _dimensionNameFromDf(field, df); + const dimParams = this._getObsDimensionParams(field, df); + obsCrossfilter = obsCrossfilter.setData(annoMatrix._cache.obs); + obsCrossfilter = obsCrossfilter.addDimension(dimName, ...dimParams); + return obsCrossfilter; + } + + _getColumnBaseType(field, col) { + /* Look up the primitive type for this field/col */ + const colSchema = _getColumnSchema(this.annoMatrix.schema, field, col); + return colSchema.type; + } + + _getObsDimensionParams(field, df) { + /* return the crossfilter dimensiontype type and params for this field/dataframe */ + + if (field === "emb") { + /* assumed to be 2D */ + return ["spatial", df.icol(0).asArray(), df.icol(1).asArray()]; + } + + /* assumed to be 1D */ + const col = df.icol(0); + const colName = df.colIndex.getLabel(0); + const type = this._getColumnBaseType(field, colName); + if (type === "string" || type === "categorical" || type === "boolean") { + return ["enum", col.asArray()]; + } + if (type === "int32") { + return ["scalar", col.asArray(), Int32Array]; + } + if (type === "float32") { + return ["scalar", col.asArray(), Float32Array]; + } + // Currently not supporting boolean and categorical types. + console.error( + `Warning - unknown metadata schema (${type}) for field ${field} ${colName}.` + ); + // skip it - we don't know what to do with this type + + return undefined; + } +} diff --git a/client/src/annoMatrix/fetchHelpers.js b/client/src/annoMatrix/fetchHelpers.js new file mode 100644 index 00000000..abc67d09 --- /dev/null +++ b/client/src/annoMatrix/fetchHelpers.js @@ -0,0 +1,27 @@ +export { doBinaryRequest } from "../util/actionHelpers"; + +/* double URI encode - needed for query-param filters */ +export function _dubEncURIComp(s) { + return encodeURIComponent(encodeURIComponent(s)); +} + +/* currently unused, consider deleting */ +export function _fetchResult(promise) { + let _status = "pending"; + const res = promise.then( + (r) => { + _status = "success"; + return r; + }, + (e) => { + _status = "error"; + throw e; + } + ); + + res.status = () => { + return _status; + }; + + return res; +} diff --git a/client/src/annoMatrix/index.js b/client/src/annoMatrix/index.js new file mode 100644 index 00000000..9e23aef6 --- /dev/null +++ b/client/src/annoMatrix/index.js @@ -0,0 +1,15 @@ +/* +AnnoMatrix -- Annotated Matrix exported interface + +Public API is defined in: + + annoMatrix.js + viewCreators.js + crossfilter.js + +*/ + +export { default as AnnoMatrixLoader } from "./loader"; +export * from "./viewCreators"; +export { default as AnnoMatrixObsCrossfilter } from "./crossfilter"; +export { default as gcMiddleware } from "./middleware"; diff --git a/client/src/annoMatrix/loader.js b/client/src/annoMatrix/loader.js new file mode 100644 index 00000000..7ee27553 --- /dev/null +++ b/client/src/annoMatrix/loader.js @@ -0,0 +1,287 @@ +import { doBinaryRequest, _dubEncURIComp } from "./fetchHelpers"; +import { matrixFBSToDataframe } from "../util/stateManager/matrix"; +import { _getColumnSchema, _normalizeCategoricalSchema } from "./schema"; +import { + addObsAnnoColumn, + removeObsAnnoColumn, + addObsAnnoCategory, + removeObsAnnoCategory, +} from "../util/stateManager/schemaHelpers"; +import { isArrayOrTypedArray } from "../util/typeHelpers"; +import { _whereCacheCreate } from "./whereCache"; +import AnnoMatrix from "./annoMatrix"; +import PromiseLimit from "../util/promiseLimit"; + +const promiseThrottle = new PromiseLimit(5); + +export default class AnnoMatrixLoader extends AnnoMatrix { + /* + AnnoMatrix implementation which proxies to HTTP server using the CXG REST API. + Used as the base (non-view) instance. + + Public API is same as AnnoMatrix class (refer there for API description), + with the addition of the constructor which bootstraps: + + new AnnoMatrixLoader(serverBaseURL, schema) -> instance + + */ + constructor(baseURL, schema) { + const { nObs, nVar } = schema.dataframe; + super(schema, nObs, nVar); + + if (baseURL[baseURL.length - 1] !== "/") { + // must have trailing slash + baseURL += "/"; + } + this.baseURL = baseURL; + Object.seal(this); + } + + /** + ** Public. API described in base class. + **/ + addObsAnnoCategory(col, category) { + /* + Add a new category (aka label) to the schema for an obs column. + */ + const colSchema = _getColumnSchema(this.schema, "obs", col); + _writableCategoryTypeCheck(colSchema); // throws on error + + const o = this._clone(); + o.schema = addObsAnnoCategory(this.schema, col, category); + return o; + } + + async removeObsAnnoCategory(col, category, unassignedCategory) { + /* + Remove a single "category" (aka "label") from the data & schema of an obs column. + */ + const colSchema = _getColumnSchema(this.schema, "obs", col); + _writableCategoryTypeCheck(colSchema); // throws on error + + const o = await this.resetObsColumnValues( + col, + category, + unassignedCategory + ); + o.schema = removeObsAnnoCategory(o.schema, col, category); + return o; + } + + dropObsColumn(col) { + /* + drop column from field + */ + const colSchema = _getColumnSchema(this.schema, "obs", col); + _writableCheck(colSchema); // throws on error + + const o = this._clone(); + o._cache.obs = this._cache.obs.dropCol(col); + o.schema = removeObsAnnoColumn(this.schema, col); + return o; + } + + addObsColumn(colSchema, Ctor, value) { + /* + add a column to field, initializing with value. Value may + be one of: + * an array of values + * a primitive type, including null or undefined. + If an array, it must be of same size as nObs and same type as Ctor + */ + colSchema.writable = true; + const col = colSchema.name; + if ( + _getColumnSchema(this.schema, "obs", col) || + this._cache.obs.hasCol(col) + ) { + throw new Error("column already exists"); + } + + const o = this._clone(); + let data; + if (isArrayOrTypedArray(value)) { + if (value.constructor !== Ctor) + throw new Error("Mismatched value array type"); + if (value.length !== this.nObs) + throw new Error("Value array has incorrect length"); + data = value.slice(); + } else { + data = new Ctor(this.nObs).fill(value); + } + o._cache.obs = this._cache.obs.withCol(col, data); + o.schema = addObsAnnoColumn(this.schema, col, { + ...colSchema, + writable: true, + }); + return o; + } + + renameObsColumn(oldCol, newCol) { + /* + Rename the obs oldColName to newColName. oldCol must be writable. + */ + const oldColSchema = _getColumnSchema(this.schema, "obs", oldCol); + _writableCheck(oldColSchema); // throws on error + + const value = this._cache.obs.hasCol(oldCol) + ? this._cache.obs.col(oldCol).asArray() + : undefined; + return this.dropObsColumn(oldCol).addObsColumn( + { + ...oldColSchema, + name: newCol, + }, + value.constructor, + value + ); + } + + async setObsColumnValues(col, rowLabels, value) { + /* + Set all rows identified by rowLabels to value. + */ + const colSchema = _getColumnSchema(this.schema, "obs", col); + _writableCategoryTypeCheck(colSchema); // throws on error + + // ensure that we have the data in cache before we manipulate it + await this.fetch("obs", col); + if (!this._cache.obs.hasCol(col)) + throw new Error("Internal error - user annotation data missing"); + + const rowIndices = this.rowIndex.getOffsets(rowLabels); + const data = this._cache.obs.col(col).asArray().slice(); + for (let i = 0, len = rowIndices.length; i < len; i += 1) { + const idx = rowIndices[i]; + if (idx === undefined) throw new Error("Unknown row label"); + data[idx] = value; + } + + const o = this._clone(); + o._cache.obs = this._cache.obs.replaceColData(col, data); + const { categories } = colSchema; + if (!categories?.includes(value)) { + o.schema = addObsAnnoCategory(this.schema, col, value); + } + return o; + } + + async resetObsColumnValues(col, oldValue, newValue) { + /* + Set all rows with value 'oldValue' to 'newValue'. + */ + const colSchema = _getColumnSchema(this.schema, "obs", col); + _writableCategoryTypeCheck(colSchema); // throws on error + + if (!colSchema.categories.includes(oldValue)) { + throw new Error("unknown category"); + } + + // ensure that we have the data in cache before we manipulate it + await this.fetch("obs", col); + if (!this._cache.obs.hasCol(col)) + throw new Error("Internal error - user annotation data missing"); + + const data = this._cache.obs.col(col).asArray().slice(); + for (let i = 0, l = data.length; i < l; i += 1) { + if (data[i] === oldValue) data[i] = newValue; + } + + const o = this._clone(); + o._cache.obs = this._cache.obs.replaceColData(col, data); + const { categories } = colSchema; + if (!categories?.includes(newValue)) { + o.schema = addObsAnnoCategory(this.schema, col, newValue); + } + return o; + } + + /** + ** Private below + **/ + async _doLoad(field, query) { + /* + _doLoad - evaluates the query against the field. Returns: + * whereCache update: column query map mapping the query to the column labels + * Dataframe containing the new colums (one per dimension) + */ + let urlQuery; + let urlBase; + let priority = 10; // default fetch priority + + switch (field) { + case "obs": + case "var": { + urlBase = `${this.baseURL}annotations/${field}`; + urlQuery = _encodeQuery("annotation-name", query); + break; + } + case "X": { + urlBase = `${this.baseURL}data/var`; + urlQuery = _encodeQuery(undefined, query); + break; + } + case "emb": { + urlBase = `${this.baseURL}layout/obs`; + urlQuery = _encodeQuery("layout-name", query); + priority = 0; // high prio load for embeddings + break; + } + default: + throw new Error("Unknown field name"); + } + + const url = `${urlBase}?${urlQuery}`; + const buffer = await promiseThrottle.priorityAdd( + priority, + doBinaryRequest, + url + ); + const result = matrixFBSToDataframe(buffer); + if (!result || result.isEmpty()) throw Error("Unknown field/col"); + + const whereCacheUpdate = _whereCacheCreate( + field, + query, + result.colIndex.labels() + ); + + if (field === "obs") { + /* cough, cough - see comment on method */ + _normalizeCategoricalSchema( + this.schema.annotations.obsByName[query], + result.col(query) + ); + } + + return [whereCacheUpdate, result]; + } +} + +/* +Utility functions below +*/ + +function _encodeQuery(colKey, q) { + if (typeof q === "object") { + const { field: queryField, column: queryColumn, value: queryValue } = q; + return `${_dubEncURIComp(queryField)}:${_dubEncURIComp( + queryColumn + )}=${_dubEncURIComp(queryValue)}`; + } + if (!colKey) throw new Error("Unsupported query by name"); + return `${colKey}=${encodeURIComponent(q)}`; +} + +function _writableCheck(colSchema) { + if (!colSchema?.writable) { + throw new Error("Unknown or readonly obs column"); + } +} + +function _writableCategoryTypeCheck(colSchema) { + _writableCheck(colSchema); + if (colSchema.type !== "categorical") { + throw new Error("column must be categorical"); + } +} diff --git a/client/src/annoMatrix/middleware.js b/client/src/annoMatrix/middleware.js new file mode 100644 index 00000000..d332b79a --- /dev/null +++ b/client/src/annoMatrix/middleware.js @@ -0,0 +1,66 @@ +/* +Garbage collection / cache management support + +Middleware that knows how to pull annoMatrix from the undoable state, +and pass it along to the AnnoMatrix class for possible cache GC. + +Private interface. + +Future work item: this middleware knows internal details of both the +Undoable metareducer and the AnnoMatrix private API. It would be helpful +to make the Undoable interface better factored. +*/ + +const annoMatrixGC = (store) => (next) => (action) => { + if (_itIsTimeForGC()) { + _doGC(store); + } + return next(action); +}; + +let lastGCTime = 0; +const InterGCDelayMS = 30 * 1000; // 30 seconds +function _itIsTimeForGC() { + /* + we don't want to run GC on every dispatch, so throttle it a bit. + + Runs every InterGCDelay period + */ + const now = Date.now(); + if (now - lastGCTime > InterGCDelayMS) { + lastGCTime = now; + return true; + } + return false; +} + +function _doGC(store) { + const state = store.getState(); + + // these should probably be a function imported from undoable.js, etc, as + // they have overly intimiate knowledge of our reducers. + const undoablePast = state["@@undoable/past"]; + const undoableFuture = state["@@undoable/future"]; + const undoableStack = undoablePast + .concat(undoableFuture) + .flatMap((snapshot) => + snapshot.filter((v) => v[0] === "annoMatrix").map((v) => v[1]) + ); + const currentAnnoMatrix = state.annoMatrix; + + /* + We want to identify those matrixes currently "hot", ie, linked from the current annoMatrix, + as our current gc algo is more aggressive with those not hot. + */ + const allAnnoMatrices = new Map( + undoableStack.map((m) => [m, { isHot: false }]) + ); + let am = currentAnnoMatrix; + while (am) { + allAnnoMatrices.set(am, { isHot: true }); + am = am.viewOf; + } + allAnnoMatrices.forEach((hints, annoMatrix) => annoMatrix._gc(hints)); +} + +export default annoMatrixGC; diff --git a/client/src/annoMatrix/schema.js b/client/src/annoMatrix/schema.js new file mode 100644 index 00000000..9ce03765 --- /dev/null +++ b/client/src/annoMatrix/schema.js @@ -0,0 +1,82 @@ +/* +Private helper functions related to schema +*/ +import catLabelSort from "../util/catLabelSort"; +import { unassignedCategoryLabel } from "../globals"; + +export function _getColumnSchema(schema, field, col) { + /* look up the column definition */ + switch (field) { + case "obs": + if (typeof col === "object") + throw new Error("unable to get column schema by query"); + return schema.annotations.obsByName[col]; + case "var": + if (typeof col === "object") + throw new Error("unable to get column schema by query"); + return schema.annotations.varByName[col]; + case "emb": + if (typeof col === "object") + throw new Error("unable to get column schema by query"); + return schema.layout.obsByName[col]; + case "X": + return schema.dataframe; + default: + throw new Error(`unknown field name: ${field}`); + } +} + +export function _getColumnDimensionNames(schema, field, col) { + /* + field/col may be an alias for multiple columns. Currently used to map ND + values to 1D dataframe columns for embeddings/layout. Signfied by the presence + of the "dims" value in the schema. + */ + const colSchema = _getColumnSchema(schema, field, col); + if (!colSchema) { + return undefined; + } + return colSchema.dims || [col]; +} + +export function _schemaColumns(schema, field) { + switch (field) { + case "obs": + return Object.keys(schema.annotations.obsByName); + case "var": + return Object.keys(schema.annotations.varByName); + case "emb": + return Object.keys(schema.layout.obsByName); + default: + throw new Error(`unknown field name: ${field}`); + } +} + +export function _getWritableColumns(schema, field) { + if (field !== "obs") return []; + return schema.annotations.obs.columns + .filter((v) => v.writable) + .map((v) => v.name); +} + +export function _isContinuousType(schema) { + const { type } = schema; + return !(type === "string" || type === "boolean" || type === "categorical"); +} + +export function _normalizeCategoricalSchema(colSchema, col) { + const { type, writable } = colSchema; + if (type === "string" || type === "boolean" || type === "categorical") { + const categorySet = new Set( + col.summarize().categories.concat(colSchema.categories ?? []) + ); + if (writable && !categorySet.has(unassignedCategoryLabel)) { + categorySet.add(unassignedCategoryLabel); + } + colSchema.categories = Array.from(categorySet); + } + + if (colSchema.categories) { + colSchema.categories = catLabelSort(writable, colSchema.categories); + } +} diff --git a/client/src/annoMatrix/viewCreators.js b/client/src/annoMatrix/viewCreators.js new file mode 100644 index 00000000..d3488699 --- /dev/null +++ b/client/src/annoMatrix/viewCreators.js @@ -0,0 +1,63 @@ +/* +View creators. These are helper functions which create new views from existing +instances of AnnoMatrix, implementing common UI functions. +*/ + +import { AnnoMatrixRowSubsetView, AnnoMatrixClipView } from "./views"; + +export function isubsetMask(annoMatrix, obsMask) { + /* + Subset annomatrix to contain the rows which have truish value in the mask. + Maks length must equal annoMatrix.nObs (row count). + */ + return isubset(annoMatrix, _maskToList(obsMask)); +} + +export function isubset(annoMatrix, obsOffsets) { + /* + Subset annomatrix to contain the positions contained in the obsOffsets array + + Example: + + isubset(annoMatrix, [0, 1]) -> annoMatrix with only the first two rows + */ + const obsIndex = annoMatrix.rowIndex.isubset(obsOffsets); + return new AnnoMatrixRowSubsetView(annoMatrix, obsIndex); +} + +export function subset(annoMatrix, obsLabels) { + /* + subset based on labels + */ + const obsIndex = annoMatrix.rowIndex.subset(obsLabels); + return new AnnoMatrixRowSubsetView(annoMatrix, obsIndex); +} + +export function clip(annoMatrix, qmin, qmax) { + /* + Create a view that clips all continuous data to the [min, max] range. + The matrix shape does not change, but the continuous values outside the + specified range will become a NaN. + */ + return new AnnoMatrixClipView(annoMatrix, qmin, qmax); +} + +/* +Private utility functions below +*/ + +function _maskToList(mask) { + /* convert masks to lists - method wastes space, but is fast */ + if (!mask) { + return null; + } + const list = new Int32Array(mask.length); + 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); +} diff --git a/client/src/annoMatrix/views.js b/client/src/annoMatrix/views.js new file mode 100644 index 00000000..f97e05a0 --- /dev/null +++ b/client/src/annoMatrix/views.js @@ -0,0 +1,161 @@ +/* eslint-disable max-classes-per-file -- Classes are interrelated*/ + +/* +Views on the annomatrix. all API here is defined in viewCreators.js and annoMatrix.js. +*/ +import clip from "../util/clip"; +import AnnoMatrix from "./annoMatrix"; +import { _whereCacheCreate } from "./whereCache"; +import { _isContinuousType, _getColumnSchema } from "./schema"; + +class AnnoMatrixView extends AnnoMatrix { + constructor(viewOf, rowIndex = null) { + const nObs = rowIndex ? rowIndex.size() : viewOf.nObs; + super(viewOf.schema, nObs, viewOf.nVar, rowIndex || viewOf.rowIndex); + this.viewOf = viewOf; + this.isView = true; + } + + addObsAnnoCategory(col, category) { + const o = this._clone(); + o.viewOf = this.viewOf.addObsAnnoCategory(col, category); + o.schema = o.viewOf.schema; + return o; + } + + async removeObsAnnoCategory(col, category, unassignedCategory) { + const o = this._clone(); + o.viewOf = await this.viewOf.removeObsAnnoCategory( + col, + category, + unassignedCategory + ); + o.schema = o.viewOf.schema; + return o; + } + + dropObsColumn(col) { + const o = this._clone(); + o.viewOf = this.viewOf.dropObsColumn(col); + o._cache.obs = this._cache.obs.dropCol(col); + o.schema = o.viewOf.schema; + return o; + } + + addObsColumn(colSchema, Ctor, value) { + const o = this._clone(); + o.viewOf = this.viewOf.addObsColumn(colSchema, Ctor, value); + o.schema = o.viewOf.schema; + return o; + } + + renameObsColumn(oldCol, newCol) { + const o = this._clone(); + o.viewOf = this.viewOf.renameObsColumn(oldCol, newCol); + o.schema = o.viewOf.schema; + return o; + } + + async setObsColumnValues(col, rowLabels, value) { + const o = this._clone(); + o.viewOf = await this.viewOf.setObsColumnValues(col, rowLabels, value); + o._cache.obs = this._cache.obs.dropCol(col); + o.schema = o.viewOf.schema; + return o; + } + + async resetObsColumnValues(col, oldValue, newValue) { + const o = this._clone(); + o.viewOf = await this.viewOf.resetObsColumnValues(col, oldValue, newValue); + o._cache.obs = this._cache.obs.dropCol(col); + o.schema = o.viewOf.schema; + return o; + } +} + +class AnnoMatrixMapView extends AnnoMatrixView { + /* + A view which knows how to transform its data. + */ + constructor(viewOf, mapFn) { + super(viewOf); + this.mapFn = mapFn; + } + + async _doLoad(field, query) { + const df = await this.viewOf._fetch(field, query); + const dfMapped = df.mapColumns((colData, colIdx) => { + const colLabel = df.colIndex.getLabel(colIdx); + const colSchema = _getColumnSchema(this.schema, field, colLabel); + return this.mapFn(field, colLabel, colSchema, colData, df); + }); + const whereCacheUpdate = _whereCacheCreate( + field, + query, + dfMapped.colIndex.labels() + ); + return [whereCacheUpdate, dfMapped]; + } +} + +export class AnnoMatrixClipView extends AnnoMatrixMapView { + /* + A view which is a clipped transformation of its parent + */ + constructor(viewOf, qmin, qmax) { + super(viewOf, (field, colLabel, colSchema, colData, df) => + _clipAnnoMatrix(field, colLabel, colSchema, colData, df, qmin, qmax) + ); + this.isClipped = true; + this.clipRange = [qmin, qmax]; + Object.seal(this); + } +} + +export class AnnoMatrixRowSubsetView extends AnnoMatrixView { + /* + A view which is a subset of total rows. + */ + constructor(viewOf, rowIndex) { + super(viewOf, rowIndex); + Object.seal(this); + } + + async _doLoad(field, query) { + const df = await this.viewOf._fetch(field, query); + + // don't try to row-subset the var dimension. + if (field === "var") { + return [null, df]; + } + + const dfSubset = df.subset(null, null, this.rowIndex); + const whereCacheUpdate = _whereCacheCreate( + field, + query, + dfSubset.colIndex.labels() + ); + return [whereCacheUpdate, dfSubset]; + } +} + +/* +Utility functions below +*/ + +function _clipAnnoMatrix(field, colLabel, colSchema, colData, df, qmin, qmax) { + /* only clip obs and var scalar columns */ + if (field !== "obs" && field !== "X") return colData; + if (!_isContinuousType(colSchema)) return colData; + if (qmin < 0) qmin = 0; + if (qmax > 1) qmax = 1; + if (qmin === 0 && qmax === 1) return colData; + + const quantiles = df.col(colLabel).summarize().percentiles; + const lower = quantiles[100 * qmin]; + const upper = quantiles[100 * qmax]; + const clippedData = clip(colData.slice(), lower, upper, Number.NaN); + return clippedData; +} + +/* eslint-enable max-classes-per-file -- enable*/ diff --git a/client/src/annoMatrix/whereCache.js b/client/src/annoMatrix/whereCache.js new file mode 100644 index 00000000..d3c7a004 --- /dev/null +++ b/client/src/annoMatrix/whereCache.js @@ -0,0 +1,92 @@ +/* +Private support functions. + +Support for a "where" query, eg, + + { where: { field: "var", column: "gene", value: "FOXP2" }} + +These evaluate to a given column label. + +The "where cache" is a map that saves evaluated queries and points +to the column label they resolve to. + +Data structure, using X as the example field being queried, and var as +the index. + +{ + X: { + var: Map( + column_label_in_var => Map(value_in_var_column => [column_label_in_X, ...]) + ) + } +} +*/ +import { _getColumnDimensionNames } from "./schema"; + +export function _whereCacheGet(whereCache, schema, field, query) { + /* + query will either be an where query (object) or a column name (string). + + Return array of column labels or undefined. + */ + + if (typeof query === "object") { + const { field: queryField, column: queryColumn, value: queryValue } = query; + + const columnMap = whereCache?.[field]?.[queryField]; + if (columnMap === undefined) return [undefined]; + + const valueMap = columnMap.get(queryColumn); + if (valueMap === undefined) return [undefined]; + + const columnLabels = valueMap.get(queryValue); + return columnLabels === undefined ? [undefined] : columnLabels; + } + + const colDims = _getColumnDimensionNames(schema, field, query); + return colDims === undefined ? [undefined] : colDims; +} + +export function _whereCacheCreate(field, query, columnLabels) { + /* + Create a new whereCache + */ + if (typeof query !== "object") return null; + + const { field: queryField, column: queryColumn, value: queryValue } = query; + const whereCache = { + [field]: { + [queryField]: new Map([ + [queryColumn, new Map([[queryValue, columnLabels]])], + ]), + }, + }; + return whereCache; +} + +function __whereCacheMerge(dst, src) { + /* + merge src into dst (modifies dst) + */ + if (!dst) dst = {}; + if (!src || typeof src !== "object") return dst; + Object.entries(src).forEach(([field, query]) => { + if (!Object.prototype.hasOwnProperty.call(dst, field)) dst[field] = {}; + Object.entries(query).forEach(([queryField, columnMap]) => { + if (!Object.prototype.hasOwnProperty.call(dst[field], queryField)) + dst[field][queryField] = new Map(); + columnMap.forEach((valueMap, queryColumn) => { + if (!dst[field][queryField].has(queryColumn)) + dst[field][queryField].set(queryColumn, new Map()); + valueMap.forEach((columnLabels, queryValue) => { + dst[field][queryField].get(queryColumn).set(queryValue, columnLabels); + }); + }); + }); + }); + return dst; +} + +export function _whereCacheMerge(...caches) { + return caches.reduce((dst, src) => __whereCacheMerge(dst, src), {}); +} diff --git a/client/src/components/app.js b/client/src/components/app.js index 14ea5dc2..fe1de5e7 100644 --- a/client/src/components/app.js +++ b/client/src/components/app.js @@ -64,10 +64,10 @@ class App extends React.Component { left: window.innerWidth / 2 - 50, }} > - error loading + error loading cellxgene ) : null} - {loading ? null : ( + {loading || error ? null : ( {(viewportRef) => ( diff --git a/client/src/components/autosave/filenameDialog.js b/client/src/components/autosave/filenameDialog.js index 715a978c..6ede84b5 100644 --- a/client/src/components/autosave/filenameDialog.js +++ b/client/src/components/autosave/filenameDialog.js @@ -11,13 +11,8 @@ import { } from "@blueprintjs/core"; @connect((state) => ({ - universe: state.universe, idhash: state.config?.parameters?.["annotations-user-data-idhash"] ?? null, annotations: state.annotations, - obsAnnotations: state.universe.obsAnnotations, - saveInProgress: state.autosave?.saveInProgress ?? false, - lastSavedObsAnnotations: state.autosave?.lastSavedObsAnnotations, - error: state.autosave?.error, writableCategoriesEnabled: state.config?.parameters?.annotations ?? false, })) class FilenameDialog extends React.Component { diff --git a/client/src/components/autosave/index.js b/client/src/components/autosave/index.js index d77dcc65..c03cdfd5 100644 --- a/client/src/components/autosave/index.js +++ b/client/src/components/autosave/index.js @@ -4,14 +4,12 @@ import actions from "../../actions"; import FilenameDialog from "./filenameDialog"; @connect((state) => ({ - universe: state.universe, annotations: state.annotations, - obsAnnotations: state.universe.obsAnnotations, saveInProgress: state.autosave?.saveInProgress ?? false, - lastSavedObsAnnotations: state.autosave?.lastSavedObsAnnotations, error: state.autosave?.error, writableCategoriesEnabled: state.config?.parameters?.annotations ?? false, - initialDataLoadComplete: state.autosave?.initialDataLoadComplete, + annoMatrix: state.annoMatrix, + lastSavedAnnoMatrix: state.autosave?.lastSavedAnnoMatrix, })) class Autosave extends React.Component { constructor(props) { @@ -42,16 +40,14 @@ class Autosave extends React.Component { tick = () => { const { dispatch, saveInProgress } = this.props; if (this.needToSave() && !saveInProgress) { - dispatch(actions.saveObsAnnotations()); + dispatch(actions.saveObsAnnotationsAction()); } }; needToSave = () => { /* return true if we need to save, false if we don't */ - const { obsAnnotations, lastSavedObsAnnotations } = this.props; - return ( - lastSavedObsAnnotations && obsAnnotations !== lastSavedObsAnnotations - ); + const { annoMatrix, lastSavedAnnoMatrix } = this.props; + return actions.needToSaveObsAnnotations(annoMatrix, lastSavedAnnoMatrix); }; statusMessage() { @@ -66,9 +62,13 @@ class Autosave extends React.Component { const { writableCategoriesEnabled, saveInProgress, - initialDataLoadComplete, + lastSavedAnnoMatrix, } = this.props; - return writableCategoriesEnabled ? ( + const initialDataLoadComplete = lastSavedAnnoMatrix; + + if (!writableCategoriesEnabled) return null; + + return (
- ) : null; + ); } } diff --git a/client/src/components/brushableHistogram/index.js b/client/src/components/brushableHistogram/index.js index 0be15c7c..71175b2d 100644 --- a/client/src/components/brushableHistogram/index.js +++ b/client/src/components/brushableHistogram/index.js @@ -4,13 +4,13 @@ https://bl.ocks.org/mbostock/34f08d5e11952a80609169b7917d4172 https://bl.ocks.org/SpaceActuary/2f004899ea1b2bd78d6f1dbb2febf771 https://bl.ocks.org/mbostock/3019563 */ -// jshint esversion: 6 -import React from "react"; +import React, { useEffect, useRef, useState, useCallback } 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 { interpolateCool } from "d3-scale-chromatic"; +import Async from "react-async"; +import memoize from "memoize-one"; import * as globals from "../../globals"; import actions from "../../actions"; import { histogramContinuous } from "../../util/dataframe/histogram"; @@ -21,6 +21,425 @@ function clamp(val, rng) { return Math.max(Math.min(val, rng[1]), rng[0]); } +function maybeScientific(x) { + let format = ","; + const _ticks = x.ticks(4); + + if (x.domain().some((n) => Math.abs(n) >= 10000)) { + /* + heuristic: if the last tick d3 wants to render has one significant + digit ie., 2000, render 2e+3, but if it's anything else ie., 42000000 render + 4.20e+n + */ + format = significantDigits(_ticks[_ticks.length - 1]) === 1 ? ".0e" : ".2e"; + } + + return format; +} + +const StillLoading = ({ zebra, displayName }) => { + /* + Render a loading indicator for the field. + */ + return ( +
+
+
+
+ {displayName} +
+
+
+
+
+ ); +}; + +const ErrorLoading = ({ displayName, error, zebra }) => { + console.log(error); // log to console as this is unexpected + return ( +
+ {`Failure loading ${displayName}`} +
+ ); +}; + +const HistogramFooter = React.memo( + ({ + displayName, + hideRanges, + rangeMin, + rangeMax, + rangeColorMin, + rangeColorMax, + logFoldChange, + pvalAdj, + }) => { + /* + Footer of each histogram. Will render range, title, and optionally + differential expression info. + + Required props: + * displayName - the displayName, aka "n_genes", "FOXP2", etc. + * hideRanges - true/false, enables/disable rendering of ranges + * range - length two array, [min, max], containing the range values to display + * rangeColor - length two array, [mincolor, maxcolor], each a CSS color + * logFoldChange - lfc to display, optional. + * pValue - pValue to display, optional. + */ + return ( +
+
+ + min {rangeMin.toPrecision(4)} + + + {displayName} + +
+ : {rangeMin} +
+ + max {rangeMax.toPrecision(4)} + +
+ + {logFoldChange && pvalAdj ? ( +
+ + log fold change: + {` ${logFoldChange.toPrecision(4)}`} + + + p-value (adj): + {pvalAdj < 0.0001 ? " < 0.0001" : ` ${pvalAdj.toFixed(4)}`} + +
+ ) : null} +
+ ); + } +); + +const HistogramHeader = React.memo( + ({ + fieldId, + isColorBy, + onColorByClick, + onRemoveClick, + isScatterPlotX, + isScatterPlotY, + onScatterPlotXClick, + onScatterPlotYClick, + isObs, + }) => { + /* + Render the toolbar for the histogram. Props: + * fieldId - field identifier, used for various IDs + * isColorBy - true/false, is this the current color-by + * onColorByClick - color-by click handler + * onRemoveClick - optional handler for remove. Button will not render if not defined. + * isScatterPlotX - optional, true/false if currently the X scatterplot field + * isScatterPlotY - optional, true/false if currently the Y scatterplot field + * onScatterPlotXClick - optional, handler for scatterPlot X button. + * onScatterPlotYClick - optional, handler for scatterPlot X button. + + Scatterplot controls will not render if either handler unspecified. + */ + + const memoizedColorByCallback = useCallback( + () => onColorByClick(fieldId, isObs), + [fieldId, isObs] + ); + + return ( +
+ {onScatterPlotXClick && onScatterPlotYClick ? ( + + + + + + + + ) : null} + {onRemoveClick ? ( + + ) : null} + +
+ ); + } +); + +const Histogram = ({ + field, + fieldForId, + display, + histogram, + width, + height, + onBrush, + onBrushEnd, + margin, + isColorBy, + selectionRange, +}) => { + const svgRef = useRef(null); + const [brush, setBrush] = useState(null); + + useEffect(() => { + /* + Create the d3 histogram + */ + const { marginLeft, marginRight, marginBottom, marginTop } = margin; + const { x, y, bins, binStart, binEnd, binWidth } = histogram; + const svg = d3.select(svgRef.current); + + /* Remove everything */ + svg.selectAll("*").remove(); + + /* Set margins within the SVG */ + const container = svg + .attr("width", width + marginLeft + marginRight) + .attr("height", height + marginTop + marginBottom) + .append("g") + .attr("class", "histogram-container") + .attr("transform", `translate(${marginLeft},${marginTop})`); + + const colorScale = d3 + .scaleSequential(interpolateCool) + .domain([0, bins.length]); + + const histogramScale = d3 + .scaleLinear() + .domain(x.domain()) + .range([ + colorScale.domain()[1], + colorScale.domain()[0], + ]); /* we flip this to make colors dark if high in the color scale */ + + if (binWidth > 0) { + /* BINS */ + container + .insert("g", "*") + .selectAll("rect") + .data(bins) + .enter() + .append("rect") + .attr("x", (d, i) => x(binStart(i)) + 1) + .attr("y", (d) => y(d)) + .attr("width", (d, i) => x(binEnd(i)) - x(binStart(i)) - 1) + .attr("height", (d) => y(0) - y(d)) + .style( + "fill", + isColorBy ? (d, i) => colorScale(histogramScale(binStart(i))) : "#bbb" + ); + } + + // BRUSH + // Note the brushable area is bounded by the data on three sides, but goes down to cover the x-axis + const brushX = d3 + .brushX() + .extent([ + [x.range()[0], y.range()[1]], + [x.range()[1], marginTop + height + marginBottom], + ]) + /* + emit start so that the Undoable history can save an undo point + upon drag start, and ignore the subsequent intermediate drag events. + */ + .on("start", onBrush(field, x.invert, "start")) + .on("brush", onBrush(field, x.invert, "brush")) + .on("end", onBrushEnd(field, x.invert)); + + const brushXselection = container + .insert("g") + .attr("class", "brush") + .attr("data-testid", `${svgRef.current.dataset.testid}-brushable-area`) + .call(brushX); + + /* X AXIS */ + container + .insert("g") + .attr("class", "axis axis--x") + .attr("transform", `translate(0,${marginTop + height})`) + .call( + d3 + .axisBottom(x) + .ticks(4) + .tickFormat(d3.format(maybeScientific(x))) + ); + + /* Y AXIS */ + container + .insert("g") + .attr("class", "axis axis--y") + .attr("transform", `translate(${marginLeft + width},0)`) + .call( + d3 + .axisRight(y) + .ticks(3) + .tickFormat( + d3.format( + y.domain().some((n) => Math.abs(n) >= 10000) ? ".0e" : "," + ) + ) + ); + + /* 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)"); + + setBrush({ brushX, brushXselection }); + }, [histogram, isColorBy]); + + useEffect(() => { + /* + paint/update selection brush + */ + if (!brush) return; + const { brushX, brushXselection } = brush; + const selection = d3.brushSelection(brushXselection.node()); + if (!selectionRange && selection) { + /* no active selection - clear brush */ + brushXselection.call(brushX.move, null); + } else if (selectionRange) { + const { x, domain } = histogram; + const [min, max] = domain; + const x0 = x(clamp(selectionRange[0], [min, max])); + const x1 = x(clamp(selectionRange[1], [min, max])); + if (!selection) { + /* there is an active selection, but no brush - set the brush */ + brushXselection.call(brushX.move, [x0, x1]); + } else { + /* there is an active selection and a brush - make sure they match */ + const moveDeltaThreshold = 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]); + } + } + } + }, [brush, selectionRange]); + + return ( + + ); +}; + @connect((state, ownProps) => { const { isObs, isUserDefined, isDiffExp, field } = ownProps; const myName = makeContinuousDimensionName( @@ -28,7 +447,7 @@ function clamp(val, rng) { field ); return { - world: state.world, + annoMatrix: state.annoMatrix, isScatterplotXXaccessor: state.controls.scatterplotXXaccessor === field, isScatterplotYYaccessor: state.controls.scatterplotYYaccessor === field, continuousSelectionRange: state.continuousSelection[myName], @@ -36,139 +455,36 @@ function clamp(val, rng) { }; }) class HistogramBrush extends React.PureComponent { - 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); + /* memoized closure to prevent HistogramHeader unecessary repaint */ + handleColorAction = memoize((dispatch) => (field, isObs) => { + if (isObs) { + dispatch({ + type: "color by continuous metadata", + colorAccessor: field, + }); + } else { + dispatch(actions.requestSingleGeneExpressionCountsForColoringPOST(field)); } - return varData.col(field); - } - - calcHistogramCache = memoize((col) => { - /* - recalculate expensive stuff, notably bins, summaries, etc. - */ - const histogramCache = {}; - const summary = col.summarize(); - const { min: domainMin, max: domainMax } = summary; - const numBins = 40; - - histogramCache.x = d3 - .scaleLinear() - .domain([domainMin, domainMax]) - .range([this.marginLeft, this.marginLeft + this.width]); - - histogramCache.bins = histogramContinuous(col, numBins, [ - domainMin, - domainMax, - ]); - histogramCache.binWidth = (domainMax - domainMin) / numBins; - - histogramCache.binStart = (i) => domainMin + i * histogramCache.binWidth; - histogramCache.binEnd = (i) => - domainMin + (i + 1) * histogramCache.binWidth; - - const yMax = histogramCache.bins.reduce((l, r) => (l > r ? l : r)); - - histogramCache.y = d3 - .scaleLinear() - .domain([0, yMax]) - .range([this.marginTop + this.height, this.marginTop]); - - return histogramCache; }); constructor(props) { super(props); - this.marginLeft = 10; // Space for 0 tick label on X axis - this.marginRight = 54; // space for Y axis & labels - this.marginBottom = 25; // space for X axis & labels - this.marginTop = 3; - - this.width = 340 - this.marginLeft - this.marginRight; - this.height = 135 - this.marginTop - this.marginBottom; + const marginLeft = 10; // Space for 0 tick label on X axis + const marginRight = 54; // space for Y axis & labels + const marginBottom = 25; // space for X axis & labels + const marginTop = 3; + this.margin = { + marginLeft, + marginRight, + marginBottom, + marginTop, + }; + this.width = 340 - marginLeft - marginRight; + this.height = 135 - marginTop - marginBottom; } - componentDidMount() { - const { field, isColorAccessor } = this.props; - - this.renderHistogram(this._histogram, field, isColorAccessor); - } - - componentDidUpdate(prevProps) { - const { - field, - world, - continuousSelectionRange: range, - isColorAccessor, - } = this.props; - const { x } = this._histogram; - let { brushX, brushXselection } = this.state; - - const dfColumn = HistogramBrush.getColumn(world, field); - const oldDfColumn = HistogramBrush.getColumn( - prevProps.world, - prevProps.field - ); - - const rangeChanged = range !== prevProps.continuousSelectionRange; - const dfChanged = dfColumn !== oldDfColumn; - const colorSelectionChanged = prevProps.isColorAccessor !== isColorAccessor; - - if (dfChanged || colorSelectionChanged) { - ({ brushX, brushXselection } = this.renderHistogram( - this._histogram, - field, - isColorAccessor - )); - } - - /* - if the selection has changed, ensure that the brush correctly reflects - the underlying selection. - */ - if ( - (dfChanged || rangeChanged || colorSelectionChanged) && - brushXselection - ) { - const selection = d3.brushSelection(brushXselection.node()); - if (!range && selection) { - /* no active selection - clear brush */ - brushXselection.call(brushX.move, null); - } else if (range) { - const { min, max } = dfColumn.summarize(); - const x0 = x(clamp(range[0], [min, max])); - const x1 = x(clamp(range[1], [min, max])); - if (!selection) { - /* there is an active selection, but no brush - set the brush */ - brushXselection.call(brushX.move, [x0, x1]); - } else { - /* there is an active selection and a brush - make sure they match */ - const moveDeltaThreshold = 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) { + onBrush = (selection, x, eventType) => { const type = `continuous metadata histogram ${eventType}`; return () => { const { dispatch, field, isObs, isUserDefined, isDiffExp } = this.props; @@ -178,33 +494,25 @@ class HistogramBrush extends React.PureComponent { // 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, - }); - } + const query = this.createQuery(); + const range = d3.event.selection + ? [x(d3.event.selection[0]), x(d3.event.selection[1])] + : null; + const otherProps = { + selection: field, + continuousNamespace: { + isObs, + isUserDefined, + isDiffExp, + }, + }; + dispatch( + actions.selectContinuousMetadataAction(type, query, range, otherProps) + ); }; - } + }; - onBrushEnd(selection, x) { + onBrushEnd = (selection, x) => { return () => { const { dispatch, field, isObs, isUserDefined, isDiffExp } = this.props; const minAllowedBrushSize = 10; @@ -215,14 +523,15 @@ class HistogramBrush extends React.PureComponent { // ignore cascading events, which are programmatically generated if (d3.event.sourceEvent.sourceEvent) return; + let type; + let range = null; if (d3.event.selection) { - let _range; - + type = "continuous metadata histogram end"; if ( d3.event.selection[1] - d3.event.selection[0] > minAllowedBrushSize ) { - _range = [x(d3.event.selection[0]), x(d3.event.selection[1])]; + 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 */ @@ -232,32 +541,26 @@ class HistogramBrush extends React.PureComponent { minAllowedBrushSize + smallAmountToAvoidInfiniteLoop; // - _range = [x(d3.event.selection[0]), x(procedurallyResizedBrushWidth)]; + 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, - }, - }); + type = "continuous metadata histogram cancel"; } + + const query = this.createQuery(); + const otherProps = { + selection: field, + continuousNamespace: { + isObs, + isUserDefined, + isDiffExp, + }, + }; + dispatch( + actions.selectContinuousMetadataAction(type, query, range, otherProps) + ); }; - } + }; handleSetGeneAsScatterplotX = () => { const { dispatch, field } = this.props; @@ -275,37 +578,6 @@ class HistogramBrush extends React.PureComponent { }); }; - 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)); - } - }; - - maybeScientific = (x) => { - let format = ","; - const _ticks = x.ticks(4); - - if (x.domain().some((n) => Math.abs(n) >= 10000)) { - /* - heuristic: if the last tick d3 wants to render has one significant - digit ie., 2000, render 2e+3, but if it's anything else ie., 42000000 render - 4.20e+n - */ - format = - significantDigits(_ticks[_ticks.length - 1]) === 1 ? ".0e" : ".2e"; - } - - return format; - }; - removeHistogram = () => { const { dispatch, @@ -337,126 +609,122 @@ class HistogramBrush extends React.PureComponent { } }; - drawHistogram(svgRef) { - const { field, world } = this.props; - const col = HistogramBrush.getColumn(world, field); - this._histogram = { - ...this.calcHistogramCache(col), - svgRef, - }; - } + fetchAsyncProps = async () => { + const { annoMatrix } = this.props; + const { isClipped } = annoMatrix; - renderHistogram(histogram, field, isColorAccessor) { - const { x, y, bins, svgRef, binStart, binEnd, binWidth } = histogram; - const svg = d3.select(svgRef); + const query = this.createQuery(); + const df = await annoMatrix.fetch(...query); + const column = df.icol(0); - /* Remove everything */ - svg.selectAll("*").remove(); + // if we are clipped, fetch both our value and our unclipped value, + // as we need the absolute min/max range, not just the clipped min/max. + const summary = column.summarize(); + const range = [summary.min, summary.max]; - /* Set margins within the SVG */ - const container = svg - .attr("width", this.width + this.marginLeft + this.marginRight) - .attr("height", this.height + this.marginTop + this.marginBottom) - .append("g") - .attr("class", "histogram-container") - .attr("transform", `translate(${this.marginLeft},${this.marginTop})`); - - const colorScale = d3 - .scaleSequential(interpolateCool) - .domain([0, bins.length]); - - const histogramScale = d3 - .scaleLinear() - .domain(x.domain()) - .range([ - colorScale.domain()[1], - colorScale.domain()[0], - ]); /* we flip this to make colors dark if high in the color scale */ - - if (binWidth > 0) { - /* BINS */ - container - .insert("g", "*") - .selectAll("rect") - .data(bins) - .enter() - .append("rect") - .attr("x", (d, i) => x(binStart(i)) + 1) - .attr("y", (d) => y(d)) - .attr("width", (d, i) => x(binEnd(i)) - x(binStart(i)) - 1) - .attr("height", (d) => y(0) - y(d)) - .style( - "fill", - isColorAccessor - ? (d, i) => colorScale(histogramScale(binStart(i))) - : "#bbb" - ); + let unclippedRange = [...range]; + if (isClipped) { + const parent = await annoMatrix.viewOf.fetch(...query); + const { min, max } = parent.icol(0).summarize(); + unclippedRange = [min, max]; } - // BRUSH - // Note the brushable area is bounded by the data on three sides, but goes down to cover the x-axis - const brushX = d3 - .brushX() - .extent([ - [x.range()[0], y.range()[1]], - [x.range()[1], this.marginTop + this.height + this.marginBottom], - ]) - /* - 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 unclippedRangeColor = [ + !annoMatrix.isClipped || annoMatrix.clipRange[0] === 0 + ? "#bbb" + : globals.blue, + !annoMatrix.isClipped || annoMatrix.clipRange[1] === 1 + ? "#bbb" + : globals.blue, + ]; - const brushXselection = container - .insert("g") - .attr("class", "brush") - .attr("data-testid", `${svgRef.dataset.testid}-brushable-area`) - .call(brushX); + const histogram = this.calcHistogramCache( + column, + this.margin, + this.width, + this.height + ); - /* X AXIS */ - container - .insert("g") - .attr("class", "axis axis--x") - .attr("transform", `translate(0,${this.marginTop + this.height})`) - .call( - d3 - .axisBottom(x) - .ticks(4) - .tickFormat(d3.format(this.maybeScientific(x))) - ); + const isSingleValue = summary.min === summary.max; + const nonFiniteExtent = + summary.min === undefined || + summary.max === undefined || + Number.isNaN(summary.min) || + Number.isNaN(summary.max); - /* Y AXIS */ - container - .insert("g") - .attr("class", "axis axis--y") - .attr("transform", `translate(${this.marginLeft + this.width},0)`) - .call( - d3 - .axisRight(y) - .ticks(3) - .tickFormat( - d3.format( - y.domain().some((n) => Math.abs(n) >= 10000) ? ".0e" : "," - ) - ) - ); + const OK2Render = !summary.categorical && !nonFiniteExtent; - /* 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)"); + return { + histogram, + range, + unclippedRange, + unclippedRangeColor, + isSingleValue, + OK2Render, + }; + }; - const newState = { brushX, brushXselection }; - this.setState(newState); - return newState; + // eslint-disable-next-line class-methods-use-this -- instance method allows for memoization per annotation + calcHistogramCache(col, margin, width, height) { + /* + recalculate expensive stuff, notably bins, summaries, etc. + */ + const histogramCache = {}; + const summary = col.summarize(); + const { min: domainMin, max: domainMax } = summary; + const numBins = 40; + const { marginTop, marginLeft } = margin; + + histogramCache.domain = [domainMin, domainMax]; + + histogramCache.x = d3 + .scaleLinear() + .domain([domainMin, domainMax]) + .range([marginLeft, marginLeft + width]); + + histogramCache.bins = histogramContinuous(col, numBins, [ + domainMin, + domainMax, + ]); + histogramCache.binWidth = (domainMax - domainMin) / numBins; + + histogramCache.binStart = (i) => domainMin + i * histogramCache.binWidth; + histogramCache.binEnd = (i) => + domainMin + (i + 1) * histogramCache.binWidth; + + const yMax = histogramCache.bins.reduce((l, r) => (l > r ? l : r)); + + histogramCache.y = d3 + .scaleLinear() + .domain([0, yMax]) + .range([marginTop + height, marginTop]); + + return histogramCache; + } + + createQuery() { + const { isObs, field, annoMatrix } = this.props; + const { schema } = annoMatrix; + if (isObs) { + return ["obs", field]; + } + const varIndex = schema?.annotations?.var?.index; + if (!varIndex) return null; + return [ + "X", + { + field: "var", + column: varIndex, + value: field, + }, + ]; } render() { const { + dispatch, + annoMatrix, field, - world, isColorAccessor, isUserDefined, isDiffExp, @@ -465,163 +733,83 @@ class HistogramBrush extends React.PureComponent { isScatterplotXXaccessor, isScatterplotYYaccessor, zebra, - ranges, + continuousSelectionRange, + isObs, } = 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; - - const isSingleValue = ranges?.min === ranges?.max; + const showScatterPlot = isDiffExp || isUserDefined; return ( -
-
- {isDiffExp || isUserDefined ? ( - - - - - - - - ) : null} - {isUserDefined ? ( - - ) : null} - -
- this.drawHistogram(svgRef)} - /> -
- - min {unclippedRangeMin.toPrecision(4)} - - - {field} - -
- : {unclippedRangeMin} -
- - max {unclippedRangeMax.toPrecision(4)} - -
- - {isDiffExp ? ( -
- - log fold change: - {` ${logFoldChange.toPrecision(4)}`} - - - p-value (adj): - {pvalAdj < 0.0001 ? " < 0.0001" : ` ${pvalAdj.toFixed(4)}`} - -
- ) : null} -
+ + + + + + {(error) => ( + + )} + + + {(asyncProps) => + asyncProps.OK2Render ? ( +
+ + + +
+ ) : null + } +
+
); } } diff --git a/client/src/components/categorical/category/annoDialogAddLabel.js b/client/src/components/categorical/category/annoDialogAddLabel.js index 254fca0c..7de242d0 100644 --- a/client/src/components/categorical/category/annoDialogAddLabel.js +++ b/client/src/components/categorical/category/annoDialogAddLabel.js @@ -3,12 +3,13 @@ import { connect } from "react-redux"; import AnnoDialog from "../annoDialog"; import LabelInput from "../labelInput"; import { labelPrompt, isLabelErroneous } from "../labelUtil"; +import actions from "../../../actions"; @connect((state) => ({ annotations: state.annotations, - universe: state.universe, + schema: state.annoMatrix?.schema, ontology: state.ontology, - crossfilter: state.crossfilter, + obsCrossfilter: state.obsCrossfilter, })) class Category extends React.PureComponent { constructor(props) { @@ -34,12 +35,13 @@ class Category extends React.PureComponent { const { newLabelText } = this.state; this.disableAddNewLabelMode(); - dispatch({ - type: "annotation: add new label to category", - metadataField, - newLabelText, - assignSelectedCells: false, - }); + dispatch( + actions.annotationCreateLabelInCategory( + metadataField, + newLabelText, + false + ) + ); e.preventDefault(); }; @@ -48,18 +50,15 @@ class Category extends React.PureComponent { const { newLabelText } = this.state; this.disableAddNewLabelMode(); - dispatch({ - type: "annotation: add new label to category", - metadataField, - newLabelText, - assignSelectedCells: true, - }); + dispatch( + actions.annotationCreateLabelInCategory(metadataField, newLabelText, true) + ); e.preventDefault(); }; labelNameError = (name) => { - const { metadataField, ontology, universe } = this.props; - return isLabelErroneous(name, metadataField, ontology, universe.schema); + const { metadataField, ontology, schema } = this.props; + return isLabelErroneous(name, metadataField, ontology, schema); }; instruction = (label) => { @@ -72,7 +71,7 @@ class Category extends React.PureComponent { render() { const { newLabelText } = this.state; - const { metadataField, annotations, ontology, crossfilter } = this.props; + const { metadataField, annotations, ontology, obsCrossfilter } = this.props; const ontologyEnabled = ontology?.enabled ?? false; return ( @@ -90,7 +89,7 @@ class Category extends React.PureComponent { instruction={this.instruction(newLabelText)} cancelTooltipContent="Close this dialog without adding a label." primaryButtonText="Add label" - secondaryButtonText={`Add label & assign ${crossfilter.countSelected()} selected cells`} + secondaryButtonText={`Add label & assign ${obsCrossfilter.countSelected()} selected cells`} handleSecondaryButtonSubmit={this.addLabelAndAssignCells} text={newLabelText} validationError={this.labelNameError(newLabelText)} diff --git a/client/src/components/categorical/category/annoDialogEditCategoryName.js b/client/src/components/categorical/category/annoDialogEditCategoryName.js index 09cfc26c..832d5ae3 100644 --- a/client/src/components/categorical/category/annoDialogEditCategoryName.js +++ b/client/src/components/categorical/category/annoDialogEditCategoryName.js @@ -5,11 +5,11 @@ import LabelInput from "../labelInput"; import { labelPrompt } from "../labelUtil"; import { AnnotationsHelpers } from "../../../util/stateManager"; +import actions from "../../../actions"; @connect((state) => ({ annotations: state.annotations, - universe: state.universe, - schema: state.world?.schema, + schema: state.annoMatrix?.schema, ontology: state.ontology, })) class AnnoDialogEditCategoryName extends React.PureComponent { @@ -42,7 +42,9 @@ class AnnoDialogEditCategoryName extends React.PureComponent { test for uniqueness against *all* annotation names, not just the subset we render as categorical. */ - const allCategoryNames = this.allCategoryNames(); + const { schema } = this.props; + const allCategoryNames = schema.annotations.obs.columns.map((c) => c.name); + if ( (allCategoryNames.indexOf(newCategoryText) > -1 && newCategoryText !== metadataField) || @@ -52,12 +54,11 @@ class AnnoDialogEditCategoryName extends React.PureComponent { } this.disableEditCategoryMode(); - dispatch({ - type: "annotation: category edited", - metadataField, - newCategoryText, - data: newCategoryText, - }); + + if (metadataField !== newCategoryText) + dispatch( + actions.annotationRenameCategoryAction(metadataField, newCategoryText) + ); e.preventDefault(); }; @@ -76,7 +77,9 @@ class AnnoDialogEditCategoryName extends React.PureComponent { test for uniqueness against *all* annotation names, not just the subset we render as categorical. */ - const allCategoryNames = this.allCategoryNames(); + const { schema } = this.props; + const allCategoryNames = schema.annotations.obs.columns.map((c) => c.name); + const categoryNameAlreadyExists = allCategoryNames.indexOf(name) > -1; const sameName = name === metadataField; if (categoryNameAlreadyExists && !sameName) { diff --git a/client/src/components/categorical/category/annoMenuCategory.js b/client/src/components/categorical/category/annoMenuCategory.js index b1190f67..4606f75a 100644 --- a/client/src/components/categorical/category/annoMenuCategory.js +++ b/client/src/components/categorical/category/annoMenuCategory.js @@ -12,6 +12,7 @@ import { } from "@blueprintjs/core"; import * as globals from "../../../globals"; +import actions from "../../../actions"; @connect((state) => ({ annotations: state.annotations, @@ -41,10 +42,7 @@ class AnnoMenuCategory extends React.PureComponent { handleDeleteCategory = () => { const { dispatch, metadataField } = this.props; - dispatch({ - type: "annotation: delete category", - metadataField, - }); + dispatch(actions.annotationDeleteCategoryAction(metadataField)); }; render() { diff --git a/client/src/components/categorical/category/categoryFlipperLayout.js b/client/src/components/categorical/category/categoryFlipperLayout.js index 5c0a577f..c3efa2bb 100644 --- a/client/src/components/categorical/category/categoryFlipperLayout.js +++ b/client/src/components/categorical/category/categoryFlipperLayout.js @@ -4,14 +4,17 @@ import { Flipper, Flipped } from "react-flip-toolkit"; import * as globals from "../../../globals"; import Value from "../value"; -class Category extends React.Component { - constructor(props) { - super(props); - this.state = {}; - } - +class Category extends React.PureComponent { renderCategoryItems(optTuples) { - const { metadataField, isUserAnno, categorySummary } = this.props; + const { + metadataField, + isUserAnno, + categoryData, + categorySummary, + colorAccessor, + colorData, + colorTable, + } = this.props; return optTuples.map((tuple, i) => { return ( @@ -25,7 +28,11 @@ class Category extends React.Component { categoryIndex={tuple[1]} i={i} flippedProps={flippedProps} + categoryData={categoryData} categorySummary={categorySummary} + colorAccessor={colorAccessor} + colorData={colorData} + colorTable={colorTable} /> )} diff --git a/client/src/components/categorical/category/index.js b/client/src/components/categorical/category/index.js index cd0976ad..da5d5803 100644 --- a/client/src/components/categorical/category/index.js +++ b/client/src/components/categorical/category/index.js @@ -1,88 +1,101 @@ -import React from "react"; -import { connect } from "react-redux"; +import React, { useRef, useEffect } from "react"; +import { connect, shallowEqual } from "react-redux"; import { FaChevronRight, FaChevronDown } from "react-icons/fa"; import { AnchorButton, Button, Tooltip } from "@blueprintjs/core"; +import Async from "react-async"; +import memoize from "memoize-one"; + import CategoryFlipperLayout from "./categoryFlipperLayout"; import AnnoMenu from "./annoMenuCategory"; import AnnoDialogEditCategoryName from "./annoDialogEditCategoryName"; import AnnoDialogAddLabel from "./annoDialogAddLabel"; import Truncate from "../../util/truncate"; +import { CategoryCrossfilterContext } from "../categoryContext"; import * as globals from "../../../globals"; -import { createCategorySummary as _createCategorySummary } from "../../../util/stateManager/controlsHelpers"; +import { createCategorySummaryFromDfCol } from "../../../util/stateManager/controlsHelpers"; +import { + createColorTable, + createColorQuery, +} from "../../../util/stateManager/colorHelpers"; +import actions from "../../../actions"; const LABEL_WIDTH = globals.leftSidebarWidth - 100; const ANNO_BUTTON_WIDTH = 50; const LABEL_WIDTH_ANNO = LABEL_WIDTH - ANNO_BUTTON_WIDTH; @connect((state, ownProps) => { + const schema = state.annoMatrix?.schema; const { metadataField } = ownProps; + const isUserAnno = schema?.annotations?.obsByName[metadataField]?.writable; + const categoricalSelection = state.categoricalSelection?.[metadataField]; return { - isColorAccessor: state.colors.colorAccessor === metadataField, - categoricalSelection: state.categoricalSelection, + colors: state.colors, + categoricalSelection, annotations: state.annotations, - universe: state.universe, - world: state.world, - schema: state.world?.schema, + annoMatrix: state.annoMatrix, + schema, + crossfilter: state.obsCrossfilter, + isUserAnno, }; }) -class Category extends React.Component { - constructor(props) { - super(props); - this.state = { - isChecked: true, - categorySummary: this.createCategorySummary(), - }; +class Category extends React.PureComponent { + static getSelectionState( + categoricalSelection, + metadataField, + categorySummary + ) { + // total number of categories in this dimension + const totalCatCount = categorySummary.numCategoryValues; + // number of selected options in this category + const selectedCatCount = categorySummary.categoryValues.reduce( + (res, label) => (categoricalSelection.get(label) ?? true ? res + 1 : res), + 0 + ); + return selectedCatCount === totalCatCount + ? "all" + : selectedCatCount === 0 + ? "none" + : "some"; } - componentDidUpdate(prevProps) { - const { categoricalSelection, metadataField, world } = this.props; - let { categorySummary } = this.state; - - if ( - world !== prevProps.world || - metadataField !== prevProps.metadataField || - !categorySummary - ) { - const newCategorySummary = this.createCategorySummary(); - if (categorySummary !== newCategorySummary) { - categorySummary = newCategorySummary; - /* eslint-disable-next-line react/no-did-update-set-state -- Contained in if statement to prevent infinite looping */ - this.setState({ categorySummary }); - } - } - - const cat = categoricalSelection?.[metadataField]; - if ( - categoricalSelection !== prevProps.categoricalSelection && - !!cat && - !!this.checkbox - ) { - // total number of categories in this dimension - const totalCatCount = categorySummary.numCategoryValues; - // number of selected options in this category - const selectedCatCount = categorySummary.categoryValues.reduce( - (res, label) => (cat.get(label) ?? true ? res + 1 : res), - 0 - ); - /* eslint-disable react/no-did-update-set-state -- Contained in if statement to prevent infinite looping */ - if (selectedCatCount === totalCatCount) { - /* everything is on, so not indeterminate */ - this.checkbox.indeterminate = false; - this.setState({ isChecked: true }); - } else if (selectedCatCount === 0) { - /* nothing is on, so no */ - this.checkbox.indeterminate = false; - this.setState({ isChecked: false }); - } else if (selectedCatCount < totalCatCount) { - /* to be explicit... */ - this.checkbox.indeterminate = true; - this.setState({ isChecked: false }); - } - /* eslint-enable react/no-did-update-set-state -- re-enabling*/ - } + static watchAsync(props, prevProps) { + return !shallowEqual(props.watchProps, prevProps.watchProps); } + static async fetchData(annoMatrix, metadataField, colors) { + /* + fetch our data and the color-by data if appropriate, and then build a summary + of our category and a color table for the color-by annotation. + */ + const { schema } = annoMatrix; + const { colorAccessor, colorMode } = colors; + let colorDataPromise = Promise.resolve(null); + if (colorAccessor) { + const query = createColorQuery(colorMode, colorAccessor, schema); + if (query) colorDataPromise = annoMatrix.fetch(...query); + } + const [categoryData, colorData] = await Promise.all([ + annoMatrix.fetch("obs", metadataField), + colorDataPromise, + ]); + + // our data + const column = categoryData.icol(0); + const colSchema = schema.annotations.obsByName[metadataField]; + const categorySummary = createCategorySummaryFromDfCol(column, colSchema); + return [categoryData, categorySummary, colorData]; + } + + getSelectionState = memoize((categorySummary) => { + const { categoricalSelection, metadataField } = this.props; + return Category.getSelectionState( + categoricalSelection, + metadataField, + categorySummary + ); + }); + handleColorChange = () => { const { dispatch, metadataField } = this.props; dispatch({ @@ -101,138 +114,252 @@ class Category extends React.Component { } }; - createCategorySummary() { - const { world, metadataField } = this.props; - if (!world || !metadataField || !world.obsAnnotations.hasCol(metadataField)) - return null; - return _createCategorySummary(world, metadataField); - } - - toggleNone() { - const { dispatch, metadataField } = this.props; - const { categorySummary } = this.state; - dispatch({ - type: "categorical metadata filter none of these", - metadataField, - labels: categorySummary.categoryValues, - }); - this.setState({ isChecked: false }); - } - - toggleAll() { - const { dispatch, metadataField } = this.props; - const { categorySummary } = this.state; - dispatch({ - type: "categorical metadata filter all of these", - metadataField, - labels: categorySummary.categoryValues, - }); - this.setState({ isChecked: true }); - } - - handleToggleAllClick() { - const { isChecked } = this.state; - if (isChecked) { - this.toggleNone(); - } else { - this.toggleAll(); + handleCategoryKeyPress = (e) => { + if (e.key === "Enter") { + this.handleCategoryClick(); } + }; + + handleToggleAllClick = (categorySummary) => { + const isChecked = this.getSelectionState(categorySummary); + if (isChecked === "all") { + this.toggleNone(categorySummary); + } else { + this.toggleAll(categorySummary); + } + }; + + fetchAsyncProps = async (props) => { + const { annoMatrix, metadataField, colors } = props.watchProps; + const { crossfilter } = this.props; + + const [categoryData, categorySummary, colorData] = await Category.fetchData( + annoMatrix, + metadataField, + colors + ); + + return { + categoryData, + categorySummary, + colorData, + crossfilter, + ...this.updateColorTable(colorData), + handleCategoryToggleAllClick: () => + this.handleToggleAllClick(categorySummary), + }; + }; + + updateColorTable(colorData) { + // color table, which may be null + const { schema, colors, metadataField } = this.props; + const { colorAccessor, userColors, colorMode } = colors; + return { + isColorAccessor: colorAccessor === metadataField, + colorAccessor, + colorMode, + colorTable: createColorTable( + colorMode, + colorAccessor, + colorData, + schema, + userColors + ), + }; } - renderIsStillLoading() { - /* - We are still loading this category, so render a "busy" signal. - */ - const { metadataField } = this.props; + toggleNone(categorySummary) { + const { dispatch, metadataField } = this.props; + dispatch( + actions.selectCategoricalAllMetadataAction( + "categorical metadata filter none of these", + metadataField, + categorySummary.categoryValues, + false + ) + ); + } + + toggleAll(categorySummary) { + const { dispatch, metadataField } = this.props; + dispatch( + actions.selectCategoricalAllMetadataAction( + "categorical metadata filter all of these", + metadataField, + categorySummary.categoryValues, + true + ) + ); + } + + render() { + const { + metadataField, + isExpanded, + categoricalSelection, + crossfilter, + colors, + annoMatrix, + isUserAnno, + } = this.props; const checkboxID = `category-select-${metadataField}`; return ( + + + + + + + {(error) => ( + + )} + + + {(asyncProps) => { + const { + colorAccessor, + colorTable, + colorData, + categoryData, + categorySummary, + isColorAccessor, + handleCategoryToggleAllClick, + } = asyncProps; + return ( + + ); + }} + + + + ); + } +} + +export default Category; + +const StillLoading = ({ metadataField, checkboxID }) => { + /* + We are still loading this category, so render a "busy" signal. + */ + return ( +
-
- - - - {metadataField} - - -
-
-
-
-
- ); - } - - render() { - const { isChecked, categorySummary } = this.state; - const { metadataField, isColorAccessor, isExpanded, schema } = this.props; - - const isStillLoading = !categorySummary; - if (isStillLoading) { - return this.renderIsStillLoading(); - } - - const checkboxID = `category-select-${metadataField}`; - - const isUserAnno = !!schema?.annotations?.obsByName[metadataField] - ?.writable; - const isTruncated = !!categorySummary?.isTruncated; - - if ( - !isUserAnno && - schema?.annotations?.obsByName[metadataField]?.categories?.length === 1 - ) { - return ( -
+ - + {metadataField} - - - {`: ${schema.annotations.obsByName[metadataField].categories[0]}`} - -
- ); - } +
+
+
+
+ ); +}; + +const ErrorLoading = ({ metadataField, error }) => { + console.error(error); // log error to console as it is unexpected. + return ( +
+ + {`Failure loading ${metadataField}`} + +
+ ); +}; + +const CategoryHeader = React.memo( + ({ + metadataField, + checkboxID, + isUserAnno, + isTruncated, + isColorAccessor, + isExpanded, + selectionState, + onColorChangeClick, + onCategoryMenuClick, + onCategoryMenuKeyPress, + onCategoryToggleAllClick, + }) => { + /* + Render category name and controls (eg, color-by button). + */ + const checkboxRef = useRef(null); + + useEffect(() => { + checkboxRef.current.indeterminate = selectionState === "some"; + }, [checkboxRef.current, selectionState]); return ( - + <>
{ - this.checkbox = el; - return el; - }} - checked={isChecked} + onChange={onCategoryToggleAllClick} + ref={checkboxRef} + checked={selectionState === "all"} type="checkbox" /> @@ -260,15 +384,11 @@ class Category extends React.Component { tabIndex="0" data-testclass="category-expand" data-testid={`${metadataField}:category-expand`} - onKeyPress={(e) => { - if (e.key === "Enter") { - this.handleCategoryClick(); - } - }} + onKeyPress={onCategoryMenuKeyPress} style={{ cursor: "pointer", }} - onClick={this.handleCategoryClick} + onClick={onCategoryMenuClick} >
+ + ); + } +); + +const CategoryRender = React.memo( + ({ + metadataField, + checkboxID, + isUserAnno, + isTruncated, + isColorAccessor, + isExpanded, + selectionState, + categoryData, + categorySummary, + colorAccessor, + colorData, + colorTable, + onColorChangeClick, + onCategoryMenuClick, + onCategoryMenuKeyPress, + onCategoryToggleAllClick, + }) => { + /* + Render the core of the category, including checkboxes, controls, etc. + */ + const { numCategoryValues } = categorySummary; + const isSingularValue = !isUserAnno && numCategoryValues === 1; + + if (isSingularValue) { + /* + Entire category has a single value, special case. + */ + const theOneValue = categorySummary.categoryValues[0]; + return ( +
+ + + {metadataField} + + + + {`: ${theOneValue}`} + +
+ ); + } + + /* + Otherwise, our normal multi-layout layout + */ + return ( + + ); } -} - -export default Category; +); diff --git a/client/src/components/categorical/categoryContext.js b/client/src/components/categorical/categoryContext.js new file mode 100644 index 00000000..5ec1b29f --- /dev/null +++ b/client/src/components/categorical/categoryContext.js @@ -0,0 +1,7 @@ +import React from "react"; + +/* +CategoryCrossfilterContext is used to pass a snapshot of the crossfilter +matching the current category summary. +*/ +export const CategoryCrossfilterContext = React.createContext(null); diff --git a/client/src/components/categorical/index.js b/client/src/components/categorical/index.js index 9254443f..0e48eba8 100644 --- a/client/src/components/categorical/index.js +++ b/client/src/components/categorical/index.js @@ -9,10 +9,11 @@ import AnnoDialog from "./annoDialog"; import AnnoSelect from "./annoSelect"; import LabelInput from "./labelInput"; import { labelPrompt } from "./labelUtil"; +import actions from "../../actions"; @connect((state) => ({ writableCategoriesEnabled: state.config?.parameters?.annotations ?? false, - schema: state.world?.schema, + schema: state.annoMatrix?.schema, ontology: state.ontology, })) class Categories extends React.Component { @@ -29,11 +30,12 @@ class Categories extends React.Component { handleCreateUserAnno = (e) => { const { dispatch } = this.props; const { newCategoryText, categoryToDuplicate } = this.state; - dispatch({ - type: "annotation: create category", - data: newCategoryText, - categoryToDuplicate, - }); + dispatch( + actions.annotationCreateCategoryAction( + newCategoryText, + categoryToDuplicate + ) + ); this.setState({ createAnnoModeActive: false, categoryToDuplicate: null, diff --git a/client/src/components/categorical/value/index.js b/client/src/components/categorical/value/index.js index 1a669d42..cea94469 100644 --- a/client/src/components/categorical/value/index.js +++ b/client/src/components/categorical/value/index.js @@ -19,8 +19,10 @@ import Truncate from "../../util/truncate"; import { AnnotationsHelpers } from "../../../util/stateManager"; import { labelPrompt, isLabelErroneous } from "../labelUtil"; +import actions from "../../../actions"; import MiniHistogram from "../../miniHistogram"; import MiniStackedBar from "../../miniStackedBar"; +import { CategoryCrossfilterContext } from "../categoryContext"; const VALUE_HEIGHT = 11; const CHART_WIDTH = 100; @@ -42,11 +44,7 @@ function _currentLabelAsString(ownProps) { return { categoricalSelection, annotations: state.annotations, - colorScale: state.colors.scale, - colorAccessor: state.colors.colorAccessor, - schema: state.world?.schema, - world: state.world, - crossfilter: state.crossfilter, + schema: state.annoMatrix?.schema, ontology: state.ontology, isDilated, }; @@ -95,50 +93,37 @@ class CategoryValue extends React.Component { handleDeleteValue = () => { const { dispatch, metadataField } = this.props; const label = this.getLabel(); - dispatch({ - type: "annotation: delete label", - metadataField, - label, - }); + dispatch(actions.annotationDeleteLabelFromCategory(metadataField, label)); }; handleAddCurrentSelectionToThisLabel = () => { - const { dispatch, metadataField, categoryIndex } = this.props; + const { dispatch, metadataField } = this.props; const label = this.getLabel(); - dispatch({ - type: "annotation: label current cell selection", - metadataField, - categoryIndex, - label, - }); + dispatch(actions.annotationLabelCurrentSelection(metadataField, label)); }; handleEditValue = (e) => { - const { dispatch, metadataField, categoryIndex } = this.props; + const { dispatch, metadataField } = this.props; const { editedLabelText } = this.state; const label = this.getLabel(); this.cancelEditMode(); - dispatch({ - type: "annotation: label edited", - editedLabel: editedLabelText, - metadataField, - categoryIndex, - label, - }); + dispatch( + actions.annotationRenameLabelInCategory( + metadataField, + label, + editedLabelText + ) + ); e.preventDefault(); }; handleCreateArbitraryLabel = (txt) => { - const { dispatch, metadataField, categoryIndex } = this.props; + const { dispatch, metadataField } = this.props; const label = this.getLabel(); this.cancelEditMode(); - dispatch({ - type: "annotation: label edited", - metadataField, - editedLabel: txt, - categoryIndex, - label, - }); + dispatch( + actions.annotationRenameLabelInCategory(metadataField, label, txt) + ); }; labelNameError = (name) => { @@ -185,13 +170,15 @@ class CategoryValue extends React.Component { } = this.props; const labels = categorySummary.categoryValues; const label = labels[categoryIndex]; - dispatch({ - type: "categorical metadata filter deselect", - metadataField, - categoryIndex, - label, - labels, - }); + dispatch( + actions.selectCategoricalMetadataAction( + "categorical metadata filter deselect", + metadataField, + labels, + label, + false + ) + ); }; shouldComponentUpdate = (nextProps, nextState) => { @@ -224,11 +211,8 @@ class CategoryValue extends React.Component { categoricalSelection[metadataField].get(label) !== newCategoricalSelection[metadataField].get(newLabel); - const worldChange = props.world !== nextProps.world; const colorAccessorChange = props.colorAccessor !== nextProps.colorAccessor; const annotationsChange = props.annotations !== nextProps.annotations; - const crossfilterChange = - props.isUserAnno && props.crossfilter !== nextProps.crossfilter; const editingLabel = state.editedLabelText !== nextState.editedLabelText; const dilationChange = props.isDilated !== nextProps.isDilated; @@ -239,10 +223,8 @@ class CategoryValue extends React.Component { return ( labelChanged || valueSelectionChange || - worldChange || colorAccessorChange || annotationsChange || - crossfilterChange || editingLabel || dilationChange || countChanged @@ -258,13 +240,15 @@ class CategoryValue extends React.Component { } = this.props; const labels = categorySummary.categoryValues; const label = labels[categoryIndex]; - dispatch({ - type: "categorical metadata filter select", - metadataField, - categoryIndex, - label, - labels, - }); + dispatch( + actions.selectCategoricalMetadataAction( + "categorical metadata filter select", + metadataField, + labels, + label, + true + ) + ); }; handleMouseEnter = () => { @@ -299,10 +283,11 @@ class CategoryValue extends React.Component { }; createHistogramBins = ( - world, metadataField, + categoryData, colorAccessor, - value, + colorData, + categoryValue, width, height ) => { @@ -311,12 +296,8 @@ class CategoryValue extends React.Component { createHistogramBins fetches the continuous data in relation to the cells relevant to the category value. It then separates that data into 50 bins for drawing the mini-histogram */ - const groupBy = world.obsAnnotations.col(metadataField); - - const col = - world.obsAnnotations.col(colorAccessor) || - world.varData.col(colorAccessor); - + const groupBy = categoryData.col(metadataField); + const col = colorData.icol(0); const range = col.summarize(); const histogramMap = col.histogram( @@ -325,8 +306,8 @@ class CategoryValue extends React.Component { groupBy ); /* Because the signature changes we really need different names for histogram to differentiate signatures */ - const bins = histogramMap.has(value) - ? histogramMap.get(value) + const bins = histogramMap.has(categoryValue) + ? histogramMap.get(categoryValue) : new Array(50).fill(0); const xScale = d3.scaleLinear().domain([0, bins.length]).range([0, width]); @@ -343,10 +324,13 @@ class CategoryValue extends React.Component { }; createStackedGraphBins = ( - world, metadataField, + categoryData, colorAccessor, + colorData, categoryValue, + colorTable, + schema, width ) => { /* @@ -354,10 +338,8 @@ class CategoryValue extends React.Component { createOccupancyStack obtains a map showing the number if cells per colored value Using the colorScale a stack of colored bars is drawn representing the map */ - const { schema } = world; - - const groupBy = world.obsAnnotations.col(metadataField); - const occupancyMap = world.obsAnnotations + const groupBy = categoryData.col(metadataField); + const occupancyMap = colorData .col(colorAccessor) .histogramCategorical(groupBy); @@ -373,7 +355,7 @@ class CategoryValue extends React.Component { const categories = schema.annotations.obsByName[colorAccessor]?.categories; - const dfColumn = world.obsAnnotations.col(colorAccessor); + const dfColumn = colorData.col(colorAccessor); const categoryValues = dfColumn.summarizeCategorical().categories; return { @@ -390,13 +372,13 @@ class CategoryValue extends React.Component { return _currentLabelAsString(this.props); } - isAddCurrentSelectionDisabled(category, value) { + isAddCurrentSelectionDisabled(crossfilter, category, value) { /* disable "add current selection to label", if one of the following is true: 1. no cells are selected 2. all currently selected cells already have this label, on this category */ - const { crossfilter, world } = this.props; + const { categoryData } = this.props; // 1. no cells selected? if (crossfilter.countSelected() === 0) { @@ -405,12 +387,7 @@ class CategoryValue extends React.Component { // 2. all selected cells already have the label const mask = crossfilter.allSelectedMask(); if ( - AnnotationsHelpers.allHaveLabelByMask( - world.obsAnnotations, - category, - value, - mask - ) + AnnotationsHelpers.allHaveLabelByMask(categoryData, category, value, mask) ) { return true; } @@ -422,11 +399,12 @@ class CategoryValue extends React.Component { const { categoricalSelection, colorAccessor, - colorScale, metadataField, - world, + categoryData, + colorData, + colorTable, + schema, } = this.props; - const isColorBy = metadataField === colorAccessor; if ( @@ -439,10 +417,13 @@ class CategoryValue extends React.Component { const { domainValues, scale, domain, occupancy } = this.createStackedGraphBins( - world, metadataField, + categoryData, colorAccessor, + colorData, categoryValue, + colorTable, + schema, CHART_WIDTH ) ?? {}; @@ -454,7 +435,7 @@ class CategoryValue extends React.Component { @@ -741,32 +721,37 @@ class CategoryValue extends React.Component { position={Position.RIGHT_TOP} content={ - - Re-label currently selected cells as - - {` ${displayString}`} - - - } - disabled={this.isAddCurrentSelectionDisabled( - metadataField, - value + + {(crossfilter) => ( + + Re-label currently selected cells as + + {` ${displayString}`} + + + } + disabled={this.isAddCurrentSelectionDisabled( + crossfilter, + metadataField, + value + )} + /> )} - /> + {displayString !== globals.unassignedCategoryLabel ? ( ({ + schema: state.annoMatrix?.schema, +})) class Occupancy extends React.PureComponent { _WIDTH = 100; @@ -21,16 +23,17 @@ class Occupancy extends React.PureComponent { createHistogram fetches the continous data in relation to the cells releveant to the catagory value. It then seperates that data into 50 bins for drawing the mini-histogram */ - const { world, metadataField, colorAccessor, categoryValue } = this.props; + const { + metadataField, + categoryData, + colorData, + categoryValue, + } = this.props; if (!this.canvas) return; - const groupBy = world.obsAnnotations.col(metadataField); - - const col = - world.obsAnnotations.col(colorAccessor) || - world.varData.col(colorAccessor); - + const groupBy = categoryData.col(metadataField); + const col = colorData.icol(0); const range = col.summarize(); const histogramMap = col.histogram( @@ -39,7 +42,6 @@ class Occupancy extends React.PureComponent { groupBy ); /* Because the signature changes we really need different names for histogram to differentiate signatures */ - // const categoryValue = category.categoryValues[categoryIndex]; const bins = histogramMap.has(categoryValue) ? histogramMap.get(categoryValue) : new Array(50).fill(0); @@ -79,20 +81,22 @@ class Occupancy extends React.PureComponent { Using the colorScale a stack of colored bars is drawn representing the map */ const { - world, metadataField, + categoryData, colorAccessor, categoryValue, - colorScale, + colorTable, + schema, + colorData, } = this.props; - const { schema } = world; + const { scale: colorScale } = colorTable; const ctx = this.canvas?.getContext("2d"); if (!ctx) return; - const groupBy = world.obsAnnotations.col(metadataField); - const occupancyMap = world.obsAnnotations + const groupBy = categoryData.col(metadataField); + const occupancyMap = colorData .col(colorAccessor) .histogramCategorical(groupBy); @@ -109,7 +113,7 @@ class Occupancy extends React.PureComponent { schema.annotations.obsByName[colorAccessor]?.categories; let currentOffset = 0; - const dfColumn = world.obsAnnotations.col(colorAccessor); + const dfColumn = colorData.col(colorAccessor); const categoryValues = dfColumn.summarizeCategorical().categories; let o; diff --git a/client/src/components/continuous/continuous.js b/client/src/components/continuous/continuous.js index c0a170b3..3752aee6 100644 --- a/client/src/components/continuous/continuous.js +++ b/client/src/components/continuous/continuous.js @@ -1,97 +1,28 @@ -// jshint esversion: 6 /* rc slider https://www.npmjs.com/package/rc-slider */ import React from "react"; import { connect } from "react-redux"; -import { Button } from "@blueprintjs/core"; - -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, + schema: state.annoMatrix?.schema, })) class Continuous extends React.PureComponent { - static renderIsStillLoading(zebra, key) { - return ( -
-
-
-
- {key} -
-
-
-
-
- ); - } - render() { - const { obsAnnotations, schema } = this.props; - + /* initial value for iterator to simulate index, ranges is an object */ + const { schema } = this.props; + if (!schema) return null; const obsIndex = schema.annotations.obs.index; const allContinuousNames = schema.annotations.obs.columns .filter((col) => col.type === "int32" || col.type === "float32") .filter((col) => col.name !== obsIndex) .map((col) => col.name); - /* initial value for iterator to simulate index, ranges is an object */ - let zebra = 0; - return (
- {allContinuousNames.map((key) => { - if (!obsAnnotations.hasCol(key)) { - // still loading! - zebra += 1; - return Continuous.renderIsStillLoading(zebra, key); - } - - // data loaded and available - const summary = obsAnnotations.col(key).summarize(); - const nonFiniteExtent = - summary.min === undefined || - summary.max === undefined || - Number.isNaN(summary.min) || - Number.isNaN(summary.max); - if (!summary.categorical && !nonFiniteExtent) { - zebra += 1; - return ( - - ); - } - - return null; - })} + {allContinuousNames.map((key, zebra) => ( + + ))}
); } diff --git a/client/src/components/continuousLegend/index.js b/client/src/components/continuousLegend/index.js index bf7120c5..c1153b22 100644 --- a/client/src/components/continuousLegend/index.js +++ b/client/src/components/continuousLegend/index.js @@ -4,6 +4,11 @@ import { connect } from "react-redux"; import * as d3 from "d3"; import { interpolateCool } from "d3-scale-chromatic"; +import { + createColorTable, + createColorQuery, +} from "../../util/stateManager/colorHelpers"; + // create continuous color legend // http://bl.ocks.org/syntagmatic/e8ccca52559796be775553b467593a9f const continuous = (selectorId, colorscale, colorAccessor) => { @@ -101,38 +106,71 @@ const continuous = (selectorId, colorscale, colorAccessor) => { }; @connect((state) => ({ - colorAccessor: state.colors.colorAccessor, - colorScale: state.colors.scale, + annoMatrix: state.annoMatrix, + colors: state.colors, })) class ContinuousLegend extends React.Component { + constructor(props) { + super(props); + this.ref = null; + this.state = { + colorAccessor: null, + colorScale: null, + }; + } + + componentDidMount() { + this.updateState(null); + } + componentDidUpdate(prevProps) { - const { colorAccessor, colorScale } = this.props; - const range = colorScale?.range; - const [domainMin, domainMax] = colorScale?.domain?.() ?? [0, 0]; + this.updateState(prevProps); + } + + async updateState(prevProps) { + const { annoMatrix, colors } = this.props; + if (!colors || !annoMatrix) return; + + if (colors !== prevProps?.colors || annoMatrix !== prevProps?.annoMatrix) { + const { schema } = annoMatrix; + const { colorMode, colorAccessor, userColors } = colors; + const colorQuery = createColorQuery(colorMode, colorAccessor, schema); + const colorDf = colorQuery ? await annoMatrix.fetch(...colorQuery) : null; + const colorTable = createColorTable( + colorMode, + colorAccessor, + colorDf, + schema, + userColors + ); + + const colorScale = colorTable.scale; + const range = colorScale?.range; + const [domainMin, domainMax] = colorScale?.domain?.() ?? [0, 0]; - if ( - prevProps.colorAccessor !== colorAccessor || - prevProps.colorScale !== colorScale - ) { /* always remove it, if it's not continuous we don't put it back. */ d3.select("#continuous_legend").selectAll("*").remove(); - } - if (colorAccessor && colorScale && range && domainMin < domainMax) { - /* fragile! continuous range is 0 to 1, not [#fa4b2c, ...], make this a flag? */ - if (range()[0][0] !== "#") { - continuous( - "#continuous_legend", - d3.scaleSequential(interpolateCool).domain(colorScale.domain()), - colorAccessor - ); + if (colorAccessor && colorScale && range && domainMin < domainMax) { + /* fragile! continuous range is 0 to 1, not [#fa4b2c, ...], make this a flag? */ + if (range()[0][0] !== "#") { + continuous( + "#continuous_legend", + d3.scaleSequential(interpolateCool).domain(colorScale.domain()), + colorAccessor + ); + } } + + this.setState({ + colorAccessor, + colorScale: colorTable.scale, + }); } } render() { - const { colorAccessor, colorScale } = this.props; - + const { colorAccessor, colorScale } = this.state; if ( colorScale?.domain && colorScale.domain()[1] === colorScale.domain()[0] diff --git a/client/src/components/framework/toasters.js b/client/src/components/framework/toasters.js index 18aae1e9..29e15cdf 100644 --- a/client/src/components/framework/toasters.js +++ b/client/src/components/framework/toasters.js @@ -5,6 +5,7 @@ import { Position, Toaster, Intent } from "@blueprintjs/core"; const ToastTopCenter = Toaster.create({ className: "recipe-toaster", position: Position.TOP, + maxToasts: 4, }); /* @@ -23,12 +24,15 @@ export const keepAroundErrorToast = (message) => /* a hard network error */ -export const postNetworkErrorToast = (message) => - ToastTopCenter.show({ - message, - timeout: 30000, - intent: Intent.DANGER, - }); +export const postNetworkErrorToast = (message, key = undefined) => + ToastTopCenter.show( + { + message, + timeout: 30000, + intent: Intent.DANGER, + }, + key + ); /* Async message to user diff --git a/client/src/components/geneExpression/addGenes.js b/client/src/components/geneExpression/addGenes.js index bd52f803..bc3e2f82 100644 --- a/client/src/components/geneExpression/addGenes.js +++ b/client/src/components/geneExpression/addGenes.js @@ -1,4 +1,3 @@ -// jshint esversion: 6 /* rc slider https://www.npmjs.com/package/rc-slider */ import React from "react"; @@ -34,9 +33,6 @@ const renderGene = (fuzzySortResult, { handleClick, modifiers }) => { active={modifiers.active} disabled={modifiers.disabled} data-testid={`suggest-menu-item-${geneName}`} - // Use of annotations in this way is incorrect and dataset specific. - // See https://github.com/chanzuckerberg/cellxgene/issues/483 - // label={gene.n_counts} key={geneName} onClick={(g) => /* this fires when user clicks a menu item */ @@ -56,11 +52,9 @@ const filterGenes = (query, genes) => @connect((state) => { return { - obsAnnotations: state.world?.obsAnnotations, + annoMatrix: state.annoMatrix, userDefinedGenes: state.controls.userDefinedGenes, userDefinedGenesLoading: state.controls.userDefinedGenesLoading, - world: state.world, - colorAccessor: state.colors.colorAccessor, differential: state.differential, }; }) @@ -71,9 +65,19 @@ class AddGenes extends React.Component { bulkAdd: "", tab: "autosuggest", activeItem: null, + geneNames: [], + status: "pending", }; } + componentDidMount() { + this.updateState(); + } + + componentDidUpdate(prevProps) { + this.updateState(prevProps); + } + _genesToUpper = (listGenes) => { // Has to be a Map to preserve index const upperGenes = new Map(); @@ -88,9 +92,8 @@ class AddGenes extends React.Component { _memoGenesToUpper = memoize(this._genesToUpper, (arr) => arr); handleBulkAddClick = () => { - const { world, dispatch, userDefinedGenes } = this.props; - const varIndexName = world.schema.annotations.var.index; - const { bulkAdd } = this.state; + const { dispatch, userDefinedGenes } = this.props; + const { bulkAdd, geneNames } = this.state; /* test: @@ -98,18 +101,14 @@ class AddGenes extends React.Component { */ if (bulkAdd !== "") { const genes = _.pull(_.uniq(bulkAdd.split(/[ ,]+/)), ""); - console.log("geneExpression genes", genes); if (genes.length === 0) { return keepAroundErrorToast("Must enter a gene name."); } - const worldGenes = - world.varAnnotations?.col(varIndexName)?.asArray() || []; - // These gene lists are unique enough where memoization is useless const upperGenes = this._genesToUpper(genes); const upperUserDefinedGenes = this._genesToUpper(userDefinedGenes); - const upperWorldGenes = this._memoGenesToUpper(worldGenes); + const upperGeneNames = this._memoGenesToUpper(geneNames); dispatch({ type: "bulk user defined gene start" }); @@ -119,7 +118,7 @@ class AddGenes extends React.Component { return keepAroundErrorToast("That gene already exists"); } - const indexOfGene = upperWorldGenes.get(upperGene); + const indexOfGene = upperGeneNames.get(upperGene); if (indexOfGene === undefined) { return keepAroundErrorToast( @@ -129,7 +128,7 @@ class AddGenes extends React.Component { ); } return dispatch( - actions.requestUserDefinedGene(worldGenes[indexOfGene]) + actions.requestUserDefinedGene(geneNames[indexOfGene]) ); }) ).then( @@ -142,6 +141,27 @@ class AddGenes extends React.Component { return undefined; }; + async updateState(prevProps) { + const { annoMatrix } = this.props; + if (!annoMatrix) return; + if (annoMatrix !== prevProps?.annoMatrix) { + const { schema } = annoMatrix; + const varIndex = schema.annotations.var.index; + + this.setState({ status: "pending" }); + try { + const df = await annoMatrix.fetch("var", varIndex); + this.setState({ + status: "success", + geneNames: df.col(varIndex).asArray(), + }); + } catch (error) { + this.setState({ status: "error" }); + throw error; + } + } + } + placeholderGeneNames() { /* return a string containing gene name suggestions for use as a user hint. @@ -151,10 +171,7 @@ class AddGenes extends React.Component { 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(); + const { geneNames } = this.state; if (geneNames.length > 0) { const placeholder = []; let len = geneNames.length; @@ -175,8 +192,8 @@ class AddGenes extends React.Component { } handleClick(g) { - const { world, dispatch, userDefinedGenes } = this.props; - const varIndexName = world.schema.annotations.var.index; + const { dispatch, userDefinedGenes } = this.props; + const { geneNames } = this.state; if (!g) return; const gene = g.target; if (userDefinedGenes.indexOf(gene) !== -1) { @@ -185,27 +202,21 @@ class AddGenes extends React.Component { postUserErrorToast( `That's too many genes, you can have at most ${globals.maxUserDefinedGenes} user defined genes` ); - } else if ( - world.varAnnotations.col(varIndexName).indexOf(gene) === undefined - ) { + } else if (geneNames.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" }) - ); + dispatch(actions.requestUserDefinedGene(gene)); + dispatch({ type: "single user defined gene complete" }); } } render() { - const { world, userDefinedGenesLoading } = this.props; - const varIndexName = world?.schema?.annotations?.var?.index; - const varIndex = world?.varAnnotations?.col(varIndexName)?.asArray(); - const { tab, bulkAdd, activeItem } = this.state; + const { userDefinedGenesLoading } = this.props; + const { tab, bulkAdd, activeItem, status, geneNames } = this.state; // may still be loading! - if (!varIndex) return null; + if (status !== "success") return null; return (
@@ -263,7 +274,7 @@ class AddGenes extends React.Component { itemListPredicate={filterGenes} onActiveItemChange={(item) => this.setState({ activeItem: item })} itemRenderer={renderGene} - items={varIndex || ["No genes"]} + items={geneNames || ["No genes"]} popoverProps={{ minimal: true }} />